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
dev_pager_init()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
dev_pager_alloc(handle,size,prot,foff)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
dev_pager_dealloc(pager)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
dev_pager_getpage(pager,mlist,npages,sync)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
dev_pager_putpage(pager,mlist,npages,sync)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
dev_pager_haspage(pager,offset)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
dev_pager_getfake(paddr)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
dev_pager_putfake(m)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