145749Smckusick /* 245749Smckusick * Copyright (c) 1990 University of Utah. 3*63378Sbostic * Copyright (c) 1991, 1993 4*63378Sbostic * 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*63378Sbostic * @(#)device_pager.c 8.1 (Berkeley) 06/11/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 3045749Smckusick queue_head_t dev_pager_list; /* list of managed devices */ 3145749Smckusick 3245749Smckusick #ifdef DEBUG 3345749Smckusick int dpagerdebug = 0; 3445749Smckusick #define DDB_FOLLOW 0x01 3545749Smckusick #define DDB_INIT 0x02 3645749Smckusick #define DDB_ALLOC 0x04 3745749Smckusick #define DDB_FAIL 0x08 3845749Smckusick #endif 3945749Smckusick 4053338Sbostic static vm_pager_t dev_pager_alloc __P((caddr_t, vm_size_t, vm_prot_t)); 4153338Sbostic static void dev_pager_dealloc __P((vm_pager_t)); 4253338Sbostic static int dev_pager_getpage 4353338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 4453338Sbostic static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t)); 4553338Sbostic static void dev_pager_init __P((void)); 4653338Sbostic static int dev_pager_putpage 4753338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 4853338Sbostic 4953338Sbostic struct pagerops devicepagerops = { 5053338Sbostic dev_pager_init, 5153338Sbostic dev_pager_alloc, 5253338Sbostic dev_pager_dealloc, 5353338Sbostic dev_pager_getpage, 5453338Sbostic dev_pager_putpage, 5553338Sbostic dev_pager_haspage 5653338Sbostic }; 5753338Sbostic 5853338Sbostic static void 5945749Smckusick dev_pager_init() 6045749Smckusick { 6145749Smckusick #ifdef DEBUG 6245749Smckusick if (dpagerdebug & DDB_FOLLOW) 6345749Smckusick printf("dev_pager_init()\n"); 6445749Smckusick #endif 6545749Smckusick queue_init(&dev_pager_list); 6645749Smckusick } 6745749Smckusick 6853338Sbostic static vm_pager_t 6945749Smckusick dev_pager_alloc(handle, size, prot) 7045749Smckusick caddr_t handle; 7145749Smckusick vm_size_t size; 7245749Smckusick vm_prot_t prot; 7345749Smckusick { 7445749Smckusick dev_t dev; 7545749Smckusick vm_pager_t pager; 7645749Smckusick int (*mapfunc)(), nprot; 7745749Smckusick register vm_object_t object; 7845749Smckusick register vm_page_t page; 7945749Smckusick register dev_pager_t devp; 8045749Smckusick register int npages, off; 8148386Skarels extern int nullop(), enodev(); 8245749Smckusick 8345749Smckusick 8445749Smckusick #ifdef DEBUG 8545749Smckusick if (dpagerdebug & DDB_FOLLOW) 8645749Smckusick printf("dev_pager_alloc(%x, %x, %x)\n", handle, size, prot); 8745749Smckusick #endif 8845749Smckusick /* 8945749Smckusick * Pageout to device, should never happen. 9045749Smckusick */ 9145749Smckusick if (handle == NULL) 9245749Smckusick panic("dev_pager_alloc called"); 9345749Smckusick 9445749Smckusick /* 9545749Smckusick * Look it up, creating as necessary 9645749Smckusick */ 9745749Smckusick pager = vm_pager_lookup(&dev_pager_list, handle); 9848386Skarels if (pager == NULL) { 9945749Smckusick /* 10045749Smckusick * Validation. Make sure this device can be mapped 10145749Smckusick * and that range to map is acceptible to device. 10245749Smckusick */ 10345749Smckusick dev = (dev_t)handle; 10445749Smckusick mapfunc = cdevsw[major(dev)].d_mmap; 10548386Skarels if (!mapfunc || mapfunc == enodev || mapfunc == nullop) 10648386Skarels return(NULL); 10745749Smckusick nprot = 0; 10845749Smckusick if (prot & VM_PROT_READ) 10945749Smckusick nprot |= PROT_READ; 11045749Smckusick if (prot & VM_PROT_WRITE) 11145749Smckusick nprot |= PROT_WRITE; 11245749Smckusick if (prot & VM_PROT_EXECUTE) 11345749Smckusick nprot |= PROT_EXEC; 11445749Smckusick npages = atop(round_page(size)); 11545749Smckusick for (off = 0; npages--; off += PAGE_SIZE) 11645749Smckusick if ((*mapfunc)(dev, off, nprot) == -1) 11748386Skarels return(NULL); 11845749Smckusick /* 11945749Smckusick * Allocate and initialize pager structs 12045749Smckusick */ 12145749Smckusick pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 12248386Skarels if (pager == NULL) 12348386Skarels return(NULL); 12445749Smckusick devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); 12548386Skarels if (devp == NULL) { 12645749Smckusick free((caddr_t)pager, M_VMPAGER); 12748386Skarels return(NULL); 12845749Smckusick } 12945749Smckusick devp->devp_dev = dev; 13045749Smckusick devp->devp_npages = atop(round_page(size)); 13145749Smckusick pager->pg_handle = handle; 13245749Smckusick pager->pg_ops = &devicepagerops; 13345749Smckusick pager->pg_type = PG_DEVICE; 13445749Smckusick pager->pg_data = (caddr_t)devp; 13545749Smckusick /* 13645749Smckusick * Allocate object and vm_page structures to describe memory 13745749Smckusick */ 13845749Smckusick npages = devp->devp_npages; 13945749Smckusick object = devp->devp_object = vm_object_allocate(ptoa(npages)); 14045749Smckusick vm_object_enter(object, pager); 14145749Smckusick vm_object_setpager(object, pager, (vm_offset_t)0, FALSE); 14245749Smckusick devp->devp_pages = (vm_page_t) 14345749Smckusick kmem_alloc(kernel_map, npages*sizeof(struct vm_page)); 14445749Smckusick off = 0; 14545749Smckusick for (page = devp->devp_pages; 14645749Smckusick page < &devp->devp_pages[npages]; page++) { 14745749Smckusick vm_object_lock(object); 14852595Storek VM_PAGE_INIT(page, object, off); 14945749Smckusick page->phys_addr = 15045749Smckusick pmap_phys_address((*mapfunc)(dev, off, nprot)); 15145749Smckusick page->wire_count = 1; 15256382Smckusick page->flags |= PG_FICTITIOUS; 15345749Smckusick PAGE_WAKEUP(page); 15445749Smckusick vm_object_unlock(object); 15545749Smckusick off += PAGE_SIZE; 15645749Smckusick } 15745749Smckusick /* 15845749Smckusick * Finally, put it on the managed list so other can find it. 15945749Smckusick */ 16045749Smckusick queue_enter(&dev_pager_list, devp, dev_pager_t, devp_list); 16145749Smckusick #ifdef DEBUG 16250857Smckusick if (dpagerdebug & DDB_ALLOC) { 16345749Smckusick printf("dev_pager_alloc: pages %d@%x\n", 16445749Smckusick devp->devp_npages, devp->devp_pages); 16550857Smckusick printf("dev_pager_alloc: pager %x devp %x object %x\n", 16650857Smckusick pager, devp, object); 16750857Smckusick vm_object_print(object, FALSE); 16850857Smckusick } 16945749Smckusick #endif 17045749Smckusick } else { 17145749Smckusick /* 17245749Smckusick * vm_object_lookup() gains a reference and also 17345749Smckusick * removes the object from the cache. 17445749Smckusick */ 17545749Smckusick devp = (dev_pager_t)pager->pg_data; 17645749Smckusick if (vm_object_lookup(pager) != devp->devp_object) 17745749Smckusick panic("dev_pager_setup: bad object"); 17845749Smckusick } 17945749Smckusick return(pager); 18045749Smckusick 18145749Smckusick } 18245749Smckusick 18353338Sbostic static void 18445749Smckusick dev_pager_dealloc(pager) 18545749Smckusick vm_pager_t pager; 18645749Smckusick { 18745749Smckusick dev_pager_t devp = (dev_pager_t)pager->pg_data; 18845749Smckusick register vm_object_t object; 18945749Smckusick 19045749Smckusick #ifdef DEBUG 19145749Smckusick if (dpagerdebug & DDB_FOLLOW) 19245749Smckusick printf("dev_pager_dealloc(%x)\n", pager); 19345749Smckusick #endif 19445749Smckusick queue_remove(&dev_pager_list, devp, dev_pager_t, devp_list); 19545749Smckusick object = devp->devp_object; 19645749Smckusick #ifdef DEBUG 19745749Smckusick if (dpagerdebug & DDB_ALLOC) 19845749Smckusick printf("dev_pager_dealloc: devp %x object %x pages %d@%x\n", 19945749Smckusick devp, object, devp->devp_npages, devp->devp_pages); 20045749Smckusick #endif 20145749Smckusick while (!queue_empty(&object->memq)) 20245749Smckusick vm_page_remove((vm_page_t)queue_first(&object->memq)); 20352613Smckusick kmem_free(kernel_map, (vm_offset_t)devp->devp_pages, 20445749Smckusick devp->devp_npages * sizeof(struct vm_page)); 20545749Smckusick free((caddr_t)devp, M_VMPGDATA); 20645749Smckusick pager->pg_data = 0; 20745749Smckusick } 20845749Smckusick 20953338Sbostic static int 21045749Smckusick dev_pager_getpage(pager, m, sync) 21145749Smckusick vm_pager_t pager; 21245749Smckusick vm_page_t m; 21345749Smckusick boolean_t sync; 21445749Smckusick { 21545749Smckusick #ifdef DEBUG 21645749Smckusick if (dpagerdebug & DDB_FOLLOW) 21745749Smckusick printf("dev_pager_getpage(%x, %x)\n", pager, m); 21845749Smckusick #endif 21945749Smckusick return(VM_PAGER_BAD); 22045749Smckusick } 22145749Smckusick 22253338Sbostic static int 22345749Smckusick dev_pager_putpage(pager, m, sync) 22445749Smckusick vm_pager_t pager; 22545749Smckusick vm_page_t m; 22645749Smckusick boolean_t sync; 22745749Smckusick { 22845749Smckusick #ifdef DEBUG 22945749Smckusick if (dpagerdebug & DDB_FOLLOW) 23045749Smckusick printf("dev_pager_putpage(%x, %x)\n", pager, m); 23145749Smckusick #endif 23248386Skarels if (pager == NULL) 23345749Smckusick return; 23445749Smckusick panic("dev_pager_putpage called"); 23545749Smckusick } 23645749Smckusick 23753338Sbostic static boolean_t 23845749Smckusick dev_pager_haspage(pager, offset) 23945749Smckusick vm_pager_t pager; 24045749Smckusick vm_offset_t offset; 24145749Smckusick { 24245749Smckusick #ifdef DEBUG 24345749Smckusick if (dpagerdebug & DDB_FOLLOW) 24445749Smckusick printf("dev_pager_haspage(%x, %x)\n", pager, offset); 24545749Smckusick #endif 24645749Smckusick return(TRUE); 24745749Smckusick } 248