xref: /original-bsd/sys/vm/device_pager.c (revision 333da485)
1 /*
2  * Copyright (c) 1990 University of Utah.
3  * Copyright (c) 1991, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)device_pager.c	8.5 (Berkeley) 01/12/94
13  */
14 
15 /*
16  * Page to/from special files.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/conf.h>
22 #include <sys/mman.h>
23 #include <sys/malloc.h>
24 
25 #include <vm/vm.h>
26 #include <vm/vm_kern.h>
27 #include <vm/vm_page.h>
28 #include <vm/device_pager.h>
29 
30 struct pagerlst	dev_pager_list;		/* list of managed devices */
31 struct pglist	dev_pager_fakelist;	/* list of available vm_page_t's */
32 
33 #ifdef DEBUG
34 int	dpagerdebug = 0;
35 #define	DDB_FOLLOW	0x01
36 #define DDB_INIT	0x02
37 #define DDB_ALLOC	0x04
38 #define DDB_FAIL	0x08
39 #endif
40 
41 static vm_pager_t	 dev_pager_alloc
42 			    __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
43 static void		 dev_pager_dealloc __P((vm_pager_t));
44 static int		 dev_pager_getpage
45 			    __P((vm_pager_t, vm_page_t *, int, boolean_t));
46 static boolean_t	 dev_pager_haspage __P((vm_pager_t, vm_offset_t));
47 static void		 dev_pager_init __P((void));
48 static int		 dev_pager_putpage
49 			    __P((vm_pager_t, vm_page_t *, int, boolean_t));
50 static vm_page_t	 dev_pager_getfake __P((vm_offset_t));
51 static void		 dev_pager_putfake __P((vm_page_t));
52 
53 struct pagerops devicepagerops = {
54 	dev_pager_init,
55 	dev_pager_alloc,
56 	dev_pager_dealloc,
57 	dev_pager_getpage,
58 	dev_pager_putpage,
59 	dev_pager_haspage,
60 	vm_pager_clusternull
61 };
62 
63 static void
64 dev_pager_init()
65 {
66 #ifdef DEBUG
67 	if (dpagerdebug & DDB_FOLLOW)
68 		printf("dev_pager_init()\n");
69 #endif
70 	TAILQ_INIT(&dev_pager_list);
71 	TAILQ_INIT(&dev_pager_fakelist);
72 }
73 
74 static vm_pager_t
75 dev_pager_alloc(handle, size, prot, foff)
76 	caddr_t handle;
77 	vm_size_t size;
78 	vm_prot_t prot;
79 	vm_offset_t foff;
80 {
81 	dev_t dev;
82 	vm_pager_t pager;
83 	int (*mapfunc)();
84 	vm_object_t object;
85 	dev_pager_t devp;
86 	int npages, off;
87 
88 #ifdef DEBUG
89 	if (dpagerdebug & DDB_FOLLOW)
90 		printf("dev_pager_alloc(%x, %x, %x, %x)\n",
91 		       handle, size, prot, foff);
92 #endif
93 #ifdef DIAGNOSTIC
94 	/*
95 	 * Pageout to device, should never happen.
96 	 */
97 	if (handle == NULL)
98 		panic("dev_pager_alloc called");
99 #endif
100 
101 	/*
102 	 * Make sure this device can be mapped.
103 	 */
104 	dev = (dev_t)handle;
105 	mapfunc = cdevsw[major(dev)].d_mmap;
106 	if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
107 		return(NULL);
108 
109 	/*
110 	 * Offset should be page aligned.
111 	 */
112 	if (foff & PAGE_MASK)
113 		return(NULL);
114 
115 	/*
116 	 * Check that the specified range of the device allows the
117 	 * desired protection.
118 	 *
119 	 * XXX assumes VM_PROT_* == PROT_*
120 	 */
121 	npages = atop(round_page(size));
122 	for (off = foff; npages--; off += PAGE_SIZE)
123 		if ((*mapfunc)(dev, off, (int)prot) == -1)
124 			return(NULL);
125 
126 	/*
127 	 * Look up pager, creating as necessary.
128 	 */
129 top:
130 	pager = vm_pager_lookup(&dev_pager_list, handle);
131 	if (pager == NULL) {
132 		/*
133 		 * Allocate and initialize pager structs
134 		 */
135 		pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
136 		if (pager == NULL)
137 			return(NULL);
138 		devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK);
139 		if (devp == NULL) {
140 			free((caddr_t)pager, M_VMPAGER);
141 			return(NULL);
142 		}
143 		pager->pg_handle = handle;
144 		pager->pg_ops = &devicepagerops;
145 		pager->pg_type = PG_DEVICE;
146 		pager->pg_flags = 0;
147 		pager->pg_data = devp;
148 		TAILQ_INIT(&devp->devp_pglist);
149 		/*
150 		 * Allocate object and associate it with the pager.
151 		 */
152 		object = devp->devp_object = vm_object_allocate(0);
153 		vm_object_enter(object, pager);
154 		vm_object_setpager(object, pager, (vm_offset_t)0, FALSE);
155 		/*
156 		 * Finally, put it on the managed list so other can find it.
157 		 * First we re-lookup in case someone else beat us to this
158 		 * point (due to blocking in the various mallocs).  If so,
159 		 * we free everything and start over.
160 		 */
161 		if (vm_pager_lookup(&dev_pager_list, handle)) {
162 			free((caddr_t)devp, M_VMPGDATA);
163 			free((caddr_t)pager, M_VMPAGER);
164 			goto top;
165 		}
166 		TAILQ_INSERT_TAIL(&dev_pager_list, pager, pg_list);
167 #ifdef DEBUG
168 		if (dpagerdebug & DDB_ALLOC) {
169 			printf("dev_pager_alloc: pager %x devp %x object %x\n",
170 			       pager, devp, object);
171 			vm_object_print(object, FALSE);
172 		}
173 #endif
174 	} else {
175 		/*
176 		 * vm_object_lookup() gains a reference and also
177 		 * removes the object from the cache.
178 		 */
179 		object = vm_object_lookup(pager);
180 #ifdef DIAGNOSTIC
181 		devp = (dev_pager_t)pager->pg_data;
182 		if (object != devp->devp_object)
183 			panic("dev_pager_setup: bad object");
184 #endif
185 	}
186 	return(pager);
187 }
188 
189 static void
190 dev_pager_dealloc(pager)
191 	vm_pager_t pager;
192 {
193 	dev_pager_t devp;
194 	vm_object_t object;
195 	vm_page_t m;
196 
197 #ifdef DEBUG
198 	if (dpagerdebug & DDB_FOLLOW)
199 		printf("dev_pager_dealloc(%x)\n", pager);
200 #endif
201 	TAILQ_REMOVE(&dev_pager_list, pager, pg_list);
202 	/*
203 	 * Get the object.
204 	 * Note: cannot use vm_object_lookup since object has already
205 	 * been removed from the hash chain.
206 	 */
207 	devp = (dev_pager_t)pager->pg_data;
208 	object = devp->devp_object;
209 #ifdef DEBUG
210 	if (dpagerdebug & DDB_ALLOC)
211 		printf("dev_pager_dealloc: devp %x object %x\n", devp, object);
212 #endif
213 	/*
214 	 * Free up our fake pages.
215 	 */
216 	while ((m = devp->devp_pglist.tqh_first) != NULL) {
217 		TAILQ_REMOVE(&devp->devp_pglist, m, pageq);
218 		dev_pager_putfake(m);
219 	}
220 	free((caddr_t)devp, M_VMPGDATA);
221 	free((caddr_t)pager, M_VMPAGER);
222 }
223 
224 static int
225 dev_pager_getpage(pager, mlist, npages, sync)
226 	vm_pager_t pager;
227 	vm_page_t *mlist;
228 	int npages;
229 	boolean_t sync;
230 {
231 	register vm_object_t object;
232 	vm_offset_t offset, paddr;
233 	vm_page_t page;
234 	dev_t dev;
235 	int (*mapfunc)(), prot;
236 	vm_page_t m;
237 
238 #ifdef DEBUG
239 	if (dpagerdebug & DDB_FOLLOW)
240 		printf("dev_pager_getpage(%x, %x, %x, %x)\n",
241 		       pager, mlist, npages, sync);
242 #endif
243 
244 	if (npages != 1)
245 		panic("dev_pager_getpage: cannot handle multiple pages");
246 	m = *mlist;
247 
248 	object = m->object;
249 	dev = (dev_t)pager->pg_handle;
250 	offset = m->offset + object->paging_offset;
251 	prot = PROT_READ;	/* XXX should pass in? */
252 	mapfunc = cdevsw[major(dev)].d_mmap;
253 #ifdef DIAGNOSTIC
254 	if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
255 		panic("dev_pager_getpage: no map function");
256 #endif
257 	paddr = pmap_phys_address((*mapfunc)(dev, (int)offset, prot));
258 #ifdef DIAGNOSTIC
259 	if (paddr == -1)
260 		panic("dev_pager_getpage: map function returns error");
261 #endif
262 	/*
263 	 * Replace the passed in page with our own fake page and free
264 	 * up the original.
265 	 */
266 	page = dev_pager_getfake(paddr);
267 	TAILQ_INSERT_TAIL(&((dev_pager_t)pager->pg_data)->devp_pglist, page,
268 	    pageq);
269 	vm_object_lock(object);
270 	vm_page_lock_queues();
271 	vm_page_free(m);
272 	vm_page_insert(page, object, offset);
273 	vm_page_unlock_queues();
274 	PAGE_WAKEUP(m);
275 	if (offset + PAGE_SIZE > object->size)
276 		object->size = offset + PAGE_SIZE;	/* XXX anal */
277 	vm_object_unlock(object);
278 
279 	return(VM_PAGER_OK);
280 }
281 
282 static int
283 dev_pager_putpage(pager, mlist, npages, sync)
284 	vm_pager_t pager;
285 	vm_page_t *mlist;
286 	int npages;
287 	boolean_t sync;
288 {
289 #ifdef DEBUG
290 	if (dpagerdebug & DDB_FOLLOW)
291 		printf("dev_pager_putpage(%x, %x, %x, %x)\n",
292 		       pager, mlist, npages, sync);
293 #endif
294 	if (pager == NULL)
295 		return;
296 	panic("dev_pager_putpage called");
297 }
298 
299 static boolean_t
300 dev_pager_haspage(pager, offset)
301 	vm_pager_t pager;
302 	vm_offset_t offset;
303 {
304 #ifdef DEBUG
305 	if (dpagerdebug & DDB_FOLLOW)
306 		printf("dev_pager_haspage(%x, %x)\n", pager, offset);
307 #endif
308 	return(TRUE);
309 }
310 
311 static vm_page_t
312 dev_pager_getfake(paddr)
313 	vm_offset_t paddr;
314 {
315 	vm_page_t m;
316 	int i;
317 
318 	if (dev_pager_fakelist.tqh_first == NULL) {
319 		m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK);
320 		for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) {
321 			TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
322 			m++;
323 		}
324 	}
325 	m = dev_pager_fakelist.tqh_first;
326 	TAILQ_REMOVE(&dev_pager_fakelist, m, pageq);
327 	m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS;
328 	m->phys_addr = paddr;
329 	m->wire_count = 1;
330 	return(m);
331 }
332 
333 static void
334 dev_pager_putfake(m)
335 	vm_page_t m;
336 {
337 #ifdef DIAGNOSTIC
338 	if (!(m->flags & PG_FICTITIOUS))
339 		panic("dev_pager_putfake: bad page");
340 #endif
341 	TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
342 }
343