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.3 (Berkeley) 11/21/93 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 queue_head_t dev_pager_list; /* list of managed devices */ 31 queue_head_t 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, 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, 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 }; 61 62 static void 63 dev_pager_init() 64 { 65 #ifdef DEBUG 66 if (dpagerdebug & DDB_FOLLOW) 67 printf("dev_pager_init()\n"); 68 #endif 69 queue_init(&dev_pager_list); 70 queue_init(&dev_pager_fakelist); 71 } 72 73 static vm_pager_t 74 dev_pager_alloc(handle, size, prot, foff) 75 caddr_t handle; 76 vm_size_t size; 77 vm_prot_t prot; 78 vm_offset_t foff; 79 { 80 dev_t dev; 81 vm_pager_t pager; 82 int (*mapfunc)(); 83 vm_object_t object; 84 dev_pager_t devp; 85 int npages, off; 86 87 #ifdef DEBUG 88 if (dpagerdebug & DDB_FOLLOW) 89 printf("dev_pager_alloc(%x, %x, %x, %x)\n", 90 handle, size, prot, foff); 91 #endif 92 #ifdef DIAGNOSTIC 93 /* 94 * Pageout to device, should never happen. 95 */ 96 if (handle == NULL) 97 panic("dev_pager_alloc called"); 98 #endif 99 100 /* 101 * Make sure this device can be mapped. 102 */ 103 dev = (dev_t)handle; 104 mapfunc = cdevsw[major(dev)].d_mmap; 105 if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 106 return(NULL); 107 108 /* 109 * Offset should be page aligned. 110 */ 111 if (foff & PAGE_MASK) 112 return(NULL); 113 114 /* 115 * Check that the specified range of the device allows the 116 * desired protection. 117 * 118 * XXX assumes VM_PROT_* == PROT_* 119 */ 120 npages = atop(round_page(size)); 121 for (off = foff; npages--; off += PAGE_SIZE) 122 if ((*mapfunc)(dev, off, (int)prot) == -1) 123 return(NULL); 124 125 /* 126 * Look up pager, creating as necessary. 127 */ 128 top: 129 pager = vm_pager_lookup(&dev_pager_list, handle); 130 if (pager == NULL) { 131 /* 132 * Allocate and initialize pager structs 133 */ 134 pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 135 if (pager == NULL) 136 return(NULL); 137 devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); 138 if (devp == NULL) { 139 free((caddr_t)pager, M_VMPAGER); 140 return(NULL); 141 } 142 pager->pg_handle = handle; 143 pager->pg_ops = &devicepagerops; 144 pager->pg_type = PG_DEVICE; 145 pager->pg_data = devp; 146 queue_init(&devp->devp_pglist); 147 /* 148 * Allocate object and associate it with the pager. 149 */ 150 object = devp->devp_object = vm_object_allocate(0); 151 vm_object_enter(object, pager); 152 vm_object_setpager(object, pager, (vm_offset_t)0, FALSE); 153 /* 154 * Finally, put it on the managed list so other can find it. 155 * First we re-lookup in case someone else beat us to this 156 * point (due to blocking in the various mallocs). If so, 157 * we free everything and start over. 158 */ 159 if (vm_pager_lookup(&dev_pager_list, handle)) { 160 free((caddr_t)devp, M_VMPGDATA); 161 free((caddr_t)pager, M_VMPAGER); 162 goto top; 163 } 164 queue_enter(&dev_pager_list, pager, vm_pager_t, pg_list); 165 #ifdef DEBUG 166 if (dpagerdebug & DDB_ALLOC) { 167 printf("dev_pager_alloc: pager %x devp %x object %x\n", 168 pager, devp, object); 169 vm_object_print(object, FALSE); 170 } 171 #endif 172 } else { 173 /* 174 * vm_object_lookup() gains a reference and also 175 * removes the object from the cache. 176 */ 177 object = vm_object_lookup(pager); 178 #ifdef DIAGNOSTIC 179 devp = (dev_pager_t)pager->pg_data; 180 if (object != devp->devp_object) 181 panic("dev_pager_setup: bad object"); 182 #endif 183 } 184 return(pager); 185 } 186 187 static void 188 dev_pager_dealloc(pager) 189 vm_pager_t pager; 190 { 191 dev_pager_t devp; 192 vm_object_t object; 193 vm_page_t m; 194 195 #ifdef DEBUG 196 if (dpagerdebug & DDB_FOLLOW) 197 printf("dev_pager_dealloc(%x)\n", pager); 198 #endif 199 queue_remove(&dev_pager_list, pager, vm_pager_t, pg_list); 200 /* 201 * Get the object. 202 * Note: cannot use vm_object_lookup since object has already 203 * been removed from the hash chain. 204 */ 205 devp = (dev_pager_t)pager->pg_data; 206 object = devp->devp_object; 207 #ifdef DEBUG 208 if (dpagerdebug & DDB_ALLOC) 209 printf("dev_pager_dealloc: devp %x object %x\n", devp, object); 210 #endif 211 /* 212 * Free up our fake pages. 213 */ 214 while (!queue_empty(&devp->devp_pglist)) { 215 queue_remove_first(&devp->devp_pglist, m, vm_page_t, pageq); 216 dev_pager_putfake(m); 217 } 218 free((caddr_t)devp, M_VMPGDATA); 219 free((caddr_t)pager, M_VMPAGER); 220 } 221 222 static int 223 dev_pager_getpage(pager, m, sync) 224 vm_pager_t pager; 225 vm_page_t m; 226 boolean_t sync; 227 { 228 register vm_object_t object; 229 vm_offset_t offset, paddr; 230 vm_page_t page; 231 dev_t dev; 232 int (*mapfunc)(), prot; 233 234 #ifdef DEBUG 235 if (dpagerdebug & DDB_FOLLOW) 236 printf("dev_pager_getpage(%x, %x)\n", pager, m); 237 #endif 238 239 object = m->object; 240 dev = (dev_t)pager->pg_handle; 241 offset = m->offset + object->paging_offset; 242 prot = PROT_READ; /* XXX should pass in? */ 243 mapfunc = cdevsw[major(dev)].d_mmap; 244 #ifdef DIAGNOSTIC 245 if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 246 panic("dev_pager_getpage: no map function"); 247 #endif 248 paddr = pmap_phys_address((*mapfunc)(dev, (int)offset, prot)); 249 #ifdef DIAGNOSTIC 250 if (paddr == -1) 251 panic("dev_pager_getpage: map function returns error"); 252 #endif 253 /* 254 * Replace the passed in page with our own fake page and free 255 * up the original. 256 */ 257 page = dev_pager_getfake(paddr); 258 queue_enter(&((dev_pager_t)pager->pg_data)->devp_pglist, 259 page, vm_page_t, pageq); 260 vm_object_lock(object); 261 vm_page_lock_queues(); 262 vm_page_free(m); 263 vm_page_insert(page, object, offset); 264 vm_page_unlock_queues(); 265 PAGE_WAKEUP(m); 266 if (offset + PAGE_SIZE > object->size) 267 object->size = offset + PAGE_SIZE; /* XXX anal */ 268 vm_object_unlock(object); 269 270 return(VM_PAGER_OK); 271 } 272 273 static int 274 dev_pager_putpage(pager, m, sync) 275 vm_pager_t pager; 276 vm_page_t m; 277 boolean_t sync; 278 { 279 #ifdef DEBUG 280 if (dpagerdebug & DDB_FOLLOW) 281 printf("dev_pager_putpage(%x, %x)\n", pager, m); 282 #endif 283 if (pager == NULL) 284 return; 285 panic("dev_pager_putpage called"); 286 } 287 288 static boolean_t 289 dev_pager_haspage(pager, offset) 290 vm_pager_t pager; 291 vm_offset_t offset; 292 { 293 #ifdef DEBUG 294 if (dpagerdebug & DDB_FOLLOW) 295 printf("dev_pager_haspage(%x, %x)\n", pager, offset); 296 #endif 297 return(TRUE); 298 } 299 300 static vm_page_t 301 dev_pager_getfake(paddr) 302 vm_offset_t paddr; 303 { 304 vm_page_t m; 305 int i; 306 307 if (queue_empty(&dev_pager_fakelist)) { 308 m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK); 309 for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) { 310 queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq); 311 m++; 312 } 313 } 314 queue_remove_first(&dev_pager_fakelist, m, vm_page_t, pageq); 315 m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS; 316 m->phys_addr = paddr; 317 m->wire_count = 1; 318 return(m); 319 } 320 321 static void 322 dev_pager_putfake(m) 323 vm_page_t m; 324 { 325 #ifdef DIAGNOSTIC 326 if (!(m->flags & PG_FICTITIOUS)) 327 panic("dev_pager_putfake: bad page"); 328 #endif 329 queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq); 330 } 331