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*65231Smckusick * @(#)device_pager.c 8.4 (Berkeley) 12/30/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*65231Smckusick struct pagerlst dev_pager_list; /* list of managed devices */ 31*65231Smckusick struct pglist 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 4164757Shibler static vm_pager_t dev_pager_alloc 4264757Shibler __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)); 5064757Shibler static vm_page_t dev_pager_getfake __P((vm_offset_t)); 5164757Shibler 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 69*65231Smckusick TAILQ_INIT(&dev_pager_list); 70*65231Smckusick TAILQ_INIT(&dev_pager_fakelist); 7145749Smckusick } 7245749Smckusick 7353338Sbostic static vm_pager_t 7464757Shibler dev_pager_alloc(handle, size, prot, foff) 7545749Smckusick caddr_t handle; 7645749Smckusick vm_size_t size; 7745749Smckusick vm_prot_t prot; 7864757Shibler vm_offset_t foff; 7945749Smckusick { 8045749Smckusick dev_t dev; 8145749Smckusick vm_pager_t pager; 8264757Shibler int (*mapfunc)(); 8364757Shibler vm_object_t object; 8464757Shibler dev_pager_t devp; 8564757Shibler int npages, off; 8645749Smckusick 8745749Smckusick #ifdef DEBUG 8845749Smckusick if (dpagerdebug & DDB_FOLLOW) 8964757Shibler printf("dev_pager_alloc(%x, %x, %x, %x)\n", 9064757Shibler handle, size, prot, foff); 9145749Smckusick #endif 9264757Shibler #ifdef DIAGNOSTIC 9345749Smckusick /* 9445749Smckusick * Pageout to device, should never happen. 9545749Smckusick */ 9645749Smckusick if (handle == NULL) 9745749Smckusick panic("dev_pager_alloc called"); 9864757Shibler #endif 9945749Smckusick 10045749Smckusick /* 10164757Shibler * Make sure this device can be mapped. 10245749Smckusick */ 10364757Shibler dev = (dev_t)handle; 10464757Shibler mapfunc = cdevsw[major(dev)].d_mmap; 10564757Shibler if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 10664757Shibler return(NULL); 10764757Shibler 10864757Shibler /* 10964757Shibler * Offset should be page aligned. 11064757Shibler */ 11164757Shibler if (foff & PAGE_MASK) 11264757Shibler return(NULL); 11364757Shibler 11464757Shibler /* 11564757Shibler * Check that the specified range of the device allows the 11664757Shibler * desired protection. 11764757Shibler * 11864757Shibler * XXX assumes VM_PROT_* == PROT_* 11964757Shibler */ 12064757Shibler npages = atop(round_page(size)); 12164757Shibler for (off = foff; npages--; off += PAGE_SIZE) 12264757Shibler if ((*mapfunc)(dev, off, (int)prot) == -1) 12364757Shibler return(NULL); 12464757Shibler 12564757Shibler /* 12664757Shibler * Look up pager, creating as necessary. 12764757Shibler */ 12864757Shibler 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; 14564757Shibler pager->pg_data = devp; 146*65231Smckusick TAILQ_INIT(&devp->devp_pglist); 14745749Smckusick /* 14864757Shibler * Allocate object and associate it with the pager. 14945749Smckusick */ 15064757Shibler 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. 15564757Shibler * First we re-lookup in case someone else beat us to this 15664757Shibler * point (due to blocking in the various mallocs). If so, 15764757Shibler * we free everything and start over. 15845749Smckusick */ 15964757Shibler if (vm_pager_lookup(&dev_pager_list, handle)) { 16064757Shibler free((caddr_t)devp, M_VMPGDATA); 16164757Shibler free((caddr_t)pager, M_VMPAGER); 16264757Shibler goto top; 16364757Shibler } 164*65231Smckusick TAILQ_INSERT_TAIL(&dev_pager_list, pager, 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 */ 17764757Shibler object = vm_object_lookup(pager); 17864757Shibler #ifdef DIAGNOSTIC 17945749Smckusick devp = (dev_pager_t)pager->pg_data; 18064757Shibler if (object != devp->devp_object) 18145749Smckusick panic("dev_pager_setup: bad object"); 18264757Shibler #endif 18345749Smckusick } 18445749Smckusick return(pager); 18545749Smckusick } 18645749Smckusick 18753338Sbostic static void 18845749Smckusick dev_pager_dealloc(pager) 18945749Smckusick vm_pager_t pager; 19045749Smckusick { 19164757Shibler dev_pager_t devp; 19264757Shibler vm_object_t object; 19364757Shibler vm_page_t m; 19445749Smckusick 19545749Smckusick #ifdef DEBUG 19645749Smckusick if (dpagerdebug & DDB_FOLLOW) 19745749Smckusick printf("dev_pager_dealloc(%x)\n", pager); 19845749Smckusick #endif 199*65231Smckusick TAILQ_REMOVE(&dev_pager_list, pager, pg_list); 20064757Shibler /* 20164757Shibler * Get the object. 20264757Shibler * Note: cannot use vm_object_lookup since object has already 20364757Shibler * been removed from the hash chain. 20464757Shibler */ 20564757Shibler devp = (dev_pager_t)pager->pg_data; 20645749Smckusick object = devp->devp_object; 20745749Smckusick #ifdef DEBUG 20845749Smckusick if (dpagerdebug & DDB_ALLOC) 20964757Shibler printf("dev_pager_dealloc: devp %x object %x\n", devp, object); 21045749Smckusick #endif 21164757Shibler /* 21264757Shibler * Free up our fake pages. 21364757Shibler */ 214*65231Smckusick while ((m = devp->devp_pglist.tqh_first) != NULL) { 215*65231Smckusick TAILQ_REMOVE(&devp->devp_pglist, m, pageq); 21664757Shibler dev_pager_putfake(m); 21764757Shibler } 21845749Smckusick free((caddr_t)devp, M_VMPGDATA); 21964757Shibler 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 { 22864757Shibler register vm_object_t object; 22964757Shibler vm_offset_t offset, paddr; 23064757Shibler vm_page_t page; 23164757Shibler dev_t dev; 23264757Shibler int (*mapfunc)(), prot; 23364757Shibler 23445749Smckusick #ifdef DEBUG 23545749Smckusick if (dpagerdebug & DDB_FOLLOW) 23645749Smckusick printf("dev_pager_getpage(%x, %x)\n", pager, m); 23745749Smckusick #endif 23864757Shibler 23964757Shibler object = m->object; 24064757Shibler dev = (dev_t)pager->pg_handle; 24164757Shibler offset = m->offset + object->paging_offset; 24264757Shibler prot = PROT_READ; /* XXX should pass in? */ 24364757Shibler mapfunc = cdevsw[major(dev)].d_mmap; 24464757Shibler #ifdef DIAGNOSTIC 24564757Shibler if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 24664757Shibler panic("dev_pager_getpage: no map function"); 24764757Shibler #endif 24864757Shibler paddr = pmap_phys_address((*mapfunc)(dev, (int)offset, prot)); 24964757Shibler #ifdef DIAGNOSTIC 25064757Shibler if (paddr == -1) 25164757Shibler panic("dev_pager_getpage: map function returns error"); 25264757Shibler #endif 25364757Shibler /* 25464757Shibler * Replace the passed in page with our own fake page and free 25564757Shibler * up the original. 25664757Shibler */ 25764757Shibler page = dev_pager_getfake(paddr); 258*65231Smckusick TAILQ_INSERT_TAIL(&((dev_pager_t)pager->pg_data)->devp_pglist, page, 259*65231Smckusick pageq); 26064757Shibler vm_object_lock(object); 26164757Shibler vm_page_lock_queues(); 26264757Shibler vm_page_free(m); 26364933Shibler vm_page_insert(page, object, offset); 26464757Shibler vm_page_unlock_queues(); 26564757Shibler PAGE_WAKEUP(m); 26664757Shibler if (offset + PAGE_SIZE > object->size) 26764757Shibler object->size = offset + PAGE_SIZE; /* XXX anal */ 26864757Shibler vm_object_unlock(object); 26964757Shibler 27064757Shibler 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 } 29964757Shibler 30064757Shibler static vm_page_t 30164757Shibler dev_pager_getfake(paddr) 30264757Shibler vm_offset_t paddr; 30364757Shibler { 30464757Shibler vm_page_t m; 30564757Shibler int i; 30664757Shibler 307*65231Smckusick if (dev_pager_fakelist.tqh_first == NULL) { 30864757Shibler m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK); 30964757Shibler for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) { 310*65231Smckusick TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 31164757Shibler m++; 31264757Shibler } 31364757Shibler } 314*65231Smckusick m = dev_pager_fakelist.tqh_first; 315*65231Smckusick TAILQ_REMOVE(&dev_pager_fakelist, m, pageq); 31664757Shibler m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS; 31764757Shibler m->phys_addr = paddr; 31864757Shibler m->wire_count = 1; 31964757Shibler return(m); 32064757Shibler } 32164757Shibler 32264757Shibler static void 32364757Shibler dev_pager_putfake(m) 32464757Shibler vm_page_t m; 32564757Shibler { 32664757Shibler #ifdef DIAGNOSTIC 32764757Shibler if (!(m->flags & PG_FICTITIOUS)) 32864757Shibler panic("dev_pager_putfake: bad page"); 32964757Shibler #endif 330*65231Smckusick TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 33164757Shibler } 332