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