145749Smckusick /* 245749Smckusick * Copyright (c) 1990 University of Utah. 363378Sbostic * Copyright (c) 1991, 1993 463378Sbostic * The Regents of the University of California. All rights reserved. 545749Smckusick * 645749Smckusick * This code is derived from software contributed to Berkeley by 745749Smckusick * the Systems Programming Group of the University of Utah Computer 845749Smckusick * Science Department. 945749Smckusick * 1045749Smckusick * %sccs.include.redist.c% 1145749Smckusick * 12*64757Shibler * @(#)device_pager.c 8.2 (Berkeley) 10/24/93 1345749Smckusick */ 1445749Smckusick 1545749Smckusick /* 1645749Smckusick * Page to/from special files. 1745749Smckusick */ 1845749Smckusick 1953338Sbostic #include <sys/param.h> 2053338Sbostic #include <sys/systm.h> 2153338Sbostic #include <sys/conf.h> 2253338Sbostic #include <sys/mman.h> 2353338Sbostic #include <sys/malloc.h> 2445749Smckusick 2553338Sbostic #include <vm/vm.h> 2653338Sbostic #include <vm/vm_kern.h> 2753338Sbostic #include <vm/vm_page.h> 2853338Sbostic #include <vm/device_pager.h> 2945749Smckusick 30*64757Shibler queue_head_t dev_pager_list; /* list of managed devices */ 31*64757Shibler queue_head_t dev_pager_fakelist; /* list of available vm_page_t's */ 3245749Smckusick 3345749Smckusick #ifdef DEBUG 3445749Smckusick int dpagerdebug = 0; 3545749Smckusick #define DDB_FOLLOW 0x01 3645749Smckusick #define DDB_INIT 0x02 3745749Smckusick #define DDB_ALLOC 0x04 3845749Smckusick #define DDB_FAIL 0x08 3945749Smckusick #endif 4045749Smckusick 41*64757Shibler static vm_pager_t dev_pager_alloc 42*64757Shibler __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t)); 4353338Sbostic static void dev_pager_dealloc __P((vm_pager_t)); 4453338Sbostic static int dev_pager_getpage 4553338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 4653338Sbostic static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t)); 4753338Sbostic static void dev_pager_init __P((void)); 4853338Sbostic static int dev_pager_putpage 4953338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 50*64757Shibler static vm_page_t dev_pager_getfake __P((vm_offset_t)); 51*64757Shibler static void dev_pager_putfake __P((vm_page_t)); 5253338Sbostic 5353338Sbostic struct pagerops devicepagerops = { 5453338Sbostic dev_pager_init, 5553338Sbostic dev_pager_alloc, 5653338Sbostic dev_pager_dealloc, 5753338Sbostic dev_pager_getpage, 5853338Sbostic dev_pager_putpage, 5953338Sbostic dev_pager_haspage 6053338Sbostic }; 6153338Sbostic 6253338Sbostic static void 6345749Smckusick dev_pager_init() 6445749Smckusick { 6545749Smckusick #ifdef DEBUG 6645749Smckusick if (dpagerdebug & DDB_FOLLOW) 6745749Smckusick printf("dev_pager_init()\n"); 6845749Smckusick #endif 6945749Smckusick queue_init(&dev_pager_list); 70*64757Shibler queue_init(&dev_pager_fakelist); 7145749Smckusick } 7245749Smckusick 7353338Sbostic static vm_pager_t 74*64757Shibler dev_pager_alloc(handle, size, prot, foff) 7545749Smckusick caddr_t handle; 7645749Smckusick vm_size_t size; 7745749Smckusick vm_prot_t prot; 78*64757Shibler vm_offset_t foff; 7945749Smckusick { 8045749Smckusick dev_t dev; 8145749Smckusick vm_pager_t pager; 82*64757Shibler int (*mapfunc)(); 83*64757Shibler vm_object_t object; 84*64757Shibler dev_pager_t devp; 85*64757Shibler int npages, off; 8645749Smckusick 8745749Smckusick #ifdef DEBUG 8845749Smckusick if (dpagerdebug & DDB_FOLLOW) 89*64757Shibler printf("dev_pager_alloc(%x, %x, %x, %x)\n", 90*64757Shibler handle, size, prot, foff); 9145749Smckusick #endif 92*64757Shibler #ifdef DIAGNOSTIC 9345749Smckusick /* 9445749Smckusick * Pageout to device, should never happen. 9545749Smckusick */ 9645749Smckusick if (handle == NULL) 9745749Smckusick panic("dev_pager_alloc called"); 98*64757Shibler #endif 9945749Smckusick 10045749Smckusick /* 101*64757Shibler * Make sure this device can be mapped. 10245749Smckusick */ 103*64757Shibler dev = (dev_t)handle; 104*64757Shibler mapfunc = cdevsw[major(dev)].d_mmap; 105*64757Shibler if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 106*64757Shibler return(NULL); 107*64757Shibler 108*64757Shibler /* 109*64757Shibler * Offset should be page aligned. 110*64757Shibler */ 111*64757Shibler if (foff & PAGE_MASK) 112*64757Shibler return(NULL); 113*64757Shibler 114*64757Shibler /* 115*64757Shibler * Check that the specified range of the device allows the 116*64757Shibler * desired protection. 117*64757Shibler * 118*64757Shibler * XXX assumes VM_PROT_* == PROT_* 119*64757Shibler */ 120*64757Shibler npages = atop(round_page(size)); 121*64757Shibler for (off = foff; npages--; off += PAGE_SIZE) 122*64757Shibler if ((*mapfunc)(dev, off, (int)prot) == -1) 123*64757Shibler return(NULL); 124*64757Shibler 125*64757Shibler /* 126*64757Shibler * Look up pager, creating as necessary. 127*64757Shibler */ 128*64757Shibler top: 12945749Smckusick pager = vm_pager_lookup(&dev_pager_list, handle); 13048386Skarels if (pager == NULL) { 13145749Smckusick /* 13245749Smckusick * Allocate and initialize pager structs 13345749Smckusick */ 13445749Smckusick pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 13548386Skarels if (pager == NULL) 13648386Skarels return(NULL); 13745749Smckusick devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); 13848386Skarels if (devp == NULL) { 13945749Smckusick free((caddr_t)pager, M_VMPAGER); 14048386Skarels return(NULL); 14145749Smckusick } 14245749Smckusick pager->pg_handle = handle; 14345749Smckusick pager->pg_ops = &devicepagerops; 14445749Smckusick pager->pg_type = PG_DEVICE; 145*64757Shibler pager->pg_data = devp; 146*64757Shibler queue_init(&devp->devp_pglist); 14745749Smckusick /* 148*64757Shibler * Allocate object and associate it with the pager. 14945749Smckusick */ 150*64757Shibler object = devp->devp_object = vm_object_allocate(0); 15145749Smckusick vm_object_enter(object, pager); 15245749Smckusick vm_object_setpager(object, pager, (vm_offset_t)0, FALSE); 15345749Smckusick /* 15445749Smckusick * Finally, put it on the managed list so other can find it. 155*64757Shibler * First we re-lookup in case someone else beat us to this 156*64757Shibler * point (due to blocking in the various mallocs). If so, 157*64757Shibler * we free everything and start over. 15845749Smckusick */ 159*64757Shibler if (vm_pager_lookup(&dev_pager_list, handle)) { 160*64757Shibler free((caddr_t)devp, M_VMPGDATA); 161*64757Shibler free((caddr_t)pager, M_VMPAGER); 162*64757Shibler goto top; 163*64757Shibler } 164*64757Shibler queue_enter(&dev_pager_list, pager, vm_pager_t, pg_list); 16545749Smckusick #ifdef DEBUG 16650857Smckusick if (dpagerdebug & DDB_ALLOC) { 16750857Smckusick printf("dev_pager_alloc: pager %x devp %x object %x\n", 16850857Smckusick pager, devp, object); 16950857Smckusick vm_object_print(object, FALSE); 17050857Smckusick } 17145749Smckusick #endif 17245749Smckusick } else { 17345749Smckusick /* 17445749Smckusick * vm_object_lookup() gains a reference and also 17545749Smckusick * removes the object from the cache. 17645749Smckusick */ 177*64757Shibler object = vm_object_lookup(pager); 178*64757Shibler #ifdef DIAGNOSTIC 17945749Smckusick devp = (dev_pager_t)pager->pg_data; 180*64757Shibler if (object != devp->devp_object) 18145749Smckusick panic("dev_pager_setup: bad object"); 182*64757Shibler #endif 18345749Smckusick } 18445749Smckusick return(pager); 18545749Smckusick } 18645749Smckusick 18753338Sbostic static void 18845749Smckusick dev_pager_dealloc(pager) 18945749Smckusick vm_pager_t pager; 19045749Smckusick { 191*64757Shibler dev_pager_t devp; 192*64757Shibler vm_object_t object; 193*64757Shibler vm_page_t m; 19445749Smckusick 19545749Smckusick #ifdef DEBUG 19645749Smckusick if (dpagerdebug & DDB_FOLLOW) 19745749Smckusick printf("dev_pager_dealloc(%x)\n", pager); 19845749Smckusick #endif 199*64757Shibler queue_remove(&dev_pager_list, pager, vm_pager_t, pg_list); 200*64757Shibler /* 201*64757Shibler * Get the object. 202*64757Shibler * Note: cannot use vm_object_lookup since object has already 203*64757Shibler * been removed from the hash chain. 204*64757Shibler */ 205*64757Shibler devp = (dev_pager_t)pager->pg_data; 20645749Smckusick object = devp->devp_object; 20745749Smckusick #ifdef DEBUG 20845749Smckusick if (dpagerdebug & DDB_ALLOC) 209*64757Shibler printf("dev_pager_dealloc: devp %x object %x\n", devp, object); 21045749Smckusick #endif 211*64757Shibler /* 212*64757Shibler * Free up our fake pages. 213*64757Shibler */ 214*64757Shibler while (!queue_empty(&devp->devp_pglist)) { 215*64757Shibler queue_remove_first(&devp->devp_pglist, m, vm_page_t, pageq); 216*64757Shibler dev_pager_putfake(m); 217*64757Shibler } 21845749Smckusick free((caddr_t)devp, M_VMPGDATA); 219*64757Shibler free((caddr_t)pager, M_VMPAGER); 22045749Smckusick } 22145749Smckusick 22253338Sbostic static int 22345749Smckusick dev_pager_getpage(pager, m, sync) 22445749Smckusick vm_pager_t pager; 22545749Smckusick vm_page_t m; 22645749Smckusick boolean_t sync; 22745749Smckusick { 228*64757Shibler register vm_object_t object; 229*64757Shibler vm_offset_t offset, paddr; 230*64757Shibler vm_page_t page; 231*64757Shibler dev_t dev; 232*64757Shibler int (*mapfunc)(), prot; 233*64757Shibler 23445749Smckusick #ifdef DEBUG 23545749Smckusick if (dpagerdebug & DDB_FOLLOW) 23645749Smckusick printf("dev_pager_getpage(%x, %x)\n", pager, m); 23745749Smckusick #endif 238*64757Shibler 239*64757Shibler object = m->object; 240*64757Shibler dev = (dev_t)pager->pg_handle; 241*64757Shibler offset = m->offset + object->paging_offset; 242*64757Shibler prot = PROT_READ; /* XXX should pass in? */ 243*64757Shibler mapfunc = cdevsw[major(dev)].d_mmap; 244*64757Shibler #ifdef DIAGNOSTIC 245*64757Shibler if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 246*64757Shibler panic("dev_pager_getpage: no map function"); 247*64757Shibler #endif 248*64757Shibler paddr = pmap_phys_address((*mapfunc)(dev, (int)offset, prot)); 249*64757Shibler #ifdef DIAGNOSTIC 250*64757Shibler if (paddr == -1) 251*64757Shibler panic("dev_pager_getpage: map function returns error"); 252*64757Shibler #endif 253*64757Shibler /* 254*64757Shibler * Replace the passed in page with our own fake page and free 255*64757Shibler * up the original. 256*64757Shibler */ 257*64757Shibler page = dev_pager_getfake(paddr); 258*64757Shibler queue_enter(&((dev_pager_t)pager->pg_data)->devp_pglist, 259*64757Shibler page, vm_page_t, pageq); 260*64757Shibler vm_object_lock(object); 261*64757Shibler vm_page_lock_queues(); 262*64757Shibler vm_page_free(m); 263*64757Shibler vm_page_unlock_queues(); 264*64757Shibler vm_page_insert(page, object, offset); 265*64757Shibler PAGE_WAKEUP(m); 266*64757Shibler if (offset + PAGE_SIZE > object->size) 267*64757Shibler object->size = offset + PAGE_SIZE; /* XXX anal */ 268*64757Shibler vm_object_unlock(object); 269*64757Shibler 270*64757Shibler return(VM_PAGER_OK); 27145749Smckusick } 27245749Smckusick 27353338Sbostic static int 27445749Smckusick dev_pager_putpage(pager, m, sync) 27545749Smckusick vm_pager_t pager; 27645749Smckusick vm_page_t m; 27745749Smckusick boolean_t sync; 27845749Smckusick { 27945749Smckusick #ifdef DEBUG 28045749Smckusick if (dpagerdebug & DDB_FOLLOW) 28145749Smckusick printf("dev_pager_putpage(%x, %x)\n", pager, m); 28245749Smckusick #endif 28348386Skarels if (pager == NULL) 28445749Smckusick return; 28545749Smckusick panic("dev_pager_putpage called"); 28645749Smckusick } 28745749Smckusick 28853338Sbostic static boolean_t 28945749Smckusick dev_pager_haspage(pager, offset) 29045749Smckusick vm_pager_t pager; 29145749Smckusick vm_offset_t offset; 29245749Smckusick { 29345749Smckusick #ifdef DEBUG 29445749Smckusick if (dpagerdebug & DDB_FOLLOW) 29545749Smckusick printf("dev_pager_haspage(%x, %x)\n", pager, offset); 29645749Smckusick #endif 29745749Smckusick return(TRUE); 29845749Smckusick } 299*64757Shibler 300*64757Shibler static vm_page_t 301*64757Shibler dev_pager_getfake(paddr) 302*64757Shibler vm_offset_t paddr; 303*64757Shibler { 304*64757Shibler vm_page_t m; 305*64757Shibler int i; 306*64757Shibler 307*64757Shibler if (queue_empty(&dev_pager_fakelist)) { 308*64757Shibler m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK); 309*64757Shibler for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) { 310*64757Shibler queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq); 311*64757Shibler m++; 312*64757Shibler } 313*64757Shibler } 314*64757Shibler queue_remove_first(&dev_pager_fakelist, m, vm_page_t, pageq); 315*64757Shibler m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS; 316*64757Shibler m->phys_addr = paddr; 317*64757Shibler m->wire_count = 1; 318*64757Shibler return(m); 319*64757Shibler } 320*64757Shibler 321*64757Shibler static void 322*64757Shibler dev_pager_putfake(m) 323*64757Shibler vm_page_t m; 324*64757Shibler { 325*64757Shibler #ifdef DIAGNOSTIC 326*64757Shibler if (!(m->flags & PG_FICTITIOUS)) 327*64757Shibler panic("dev_pager_putfake: bad page"); 328*64757Shibler #endif 329*64757Shibler queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq); 330*64757Shibler } 331