diff options
Diffstat (limited to 'sys/vm/device_pager.c')
-rw-r--r-- | sys/vm/device_pager.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c new file mode 100644 index 0000000..235c917a --- /dev/null +++ b/sys/vm/device_pager.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 1990 University of Utah. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)device_pager.c 8.5 (Berkeley) 1/12/94 + */ + +/* + * Page to/from special files. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/mman.h> +#include <sys/malloc.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> +#include <vm/device_pager.h> + +struct pagerlst dev_pager_list; /* list of managed devices */ +struct pglist dev_pager_fakelist; /* list of available vm_page_t's */ + +#ifdef DEBUG +int dpagerdebug = 0; +#define DDB_FOLLOW 0x01 +#define DDB_INIT 0x02 +#define DDB_ALLOC 0x04 +#define DDB_FAIL 0x08 +#endif + +static vm_pager_t dev_pager_alloc + __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t)); +static void dev_pager_dealloc __P((vm_pager_t)); +static int dev_pager_getpage + __P((vm_pager_t, vm_page_t *, int, boolean_t)); +static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t)); +static void dev_pager_init __P((void)); +static int dev_pager_putpage + __P((vm_pager_t, vm_page_t *, int, boolean_t)); +static vm_page_t dev_pager_getfake __P((vm_offset_t)); +static void dev_pager_putfake __P((vm_page_t)); + +struct pagerops devicepagerops = { + dev_pager_init, + dev_pager_alloc, + dev_pager_dealloc, + dev_pager_getpage, + dev_pager_putpage, + dev_pager_haspage, + vm_pager_clusternull +}; + +static void +dev_pager_init() +{ +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_init()\n"); +#endif + TAILQ_INIT(&dev_pager_list); + TAILQ_INIT(&dev_pager_fakelist); +} + +static vm_pager_t +dev_pager_alloc(handle, size, prot, foff) + caddr_t handle; + vm_size_t size; + vm_prot_t prot; + vm_offset_t foff; +{ + dev_t dev; + vm_pager_t pager; + int (*mapfunc)(); + vm_object_t object; + dev_pager_t devp; + int npages, off; + +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_alloc(%x, %x, %x, %x)\n", + handle, size, prot, foff); +#endif +#ifdef DIAGNOSTIC + /* + * Pageout to device, should never happen. + */ + if (handle == NULL) + panic("dev_pager_alloc called"); +#endif + + /* + * Make sure this device can be mapped. + */ + dev = (dev_t)handle; + mapfunc = cdevsw[major(dev)].d_mmap; + if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) + return(NULL); + + /* + * Offset should be page aligned. + */ + if (foff & PAGE_MASK) + return(NULL); + + /* + * Check that the specified range of the device allows the + * desired protection. + * + * XXX assumes VM_PROT_* == PROT_* + */ + npages = atop(round_page(size)); + for (off = foff; npages--; off += PAGE_SIZE) + if ((*mapfunc)(dev, off, (int)prot) == -1) + return(NULL); + + /* + * Look up pager, creating as necessary. + */ +top: + pager = vm_pager_lookup(&dev_pager_list, handle); + if (pager == NULL) { + /* + * Allocate and initialize pager structs + */ + pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); + if (pager == NULL) + return(NULL); + devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); + if (devp == NULL) { + free((caddr_t)pager, M_VMPAGER); + return(NULL); + } + pager->pg_handle = handle; + pager->pg_ops = &devicepagerops; + pager->pg_type = PG_DEVICE; + pager->pg_flags = 0; + pager->pg_data = devp; + TAILQ_INIT(&devp->devp_pglist); + /* + * Allocate object and associate it with the pager. + */ + object = devp->devp_object = vm_object_allocate(0); + vm_object_enter(object, pager); + vm_object_setpager(object, pager, (vm_offset_t)0, FALSE); + /* + * Finally, put it on the managed list so other can find it. + * First we re-lookup in case someone else beat us to this + * point (due to blocking in the various mallocs). If so, + * we free everything and start over. + */ + if (vm_pager_lookup(&dev_pager_list, handle)) { + free((caddr_t)devp, M_VMPGDATA); + free((caddr_t)pager, M_VMPAGER); + goto top; + } + TAILQ_INSERT_TAIL(&dev_pager_list, pager, pg_list); +#ifdef DEBUG + if (dpagerdebug & DDB_ALLOC) { + printf("dev_pager_alloc: pager %x devp %x object %x\n", + pager, devp, object); + vm_object_print(object, FALSE); + } +#endif + } else { + /* + * vm_object_lookup() gains a reference and also + * removes the object from the cache. + */ + object = vm_object_lookup(pager); +#ifdef DIAGNOSTIC + devp = (dev_pager_t)pager->pg_data; + if (object != devp->devp_object) + panic("dev_pager_setup: bad object"); +#endif + } + return(pager); +} + +static void +dev_pager_dealloc(pager) + vm_pager_t pager; +{ + dev_pager_t devp; + vm_object_t object; + vm_page_t m; + +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_dealloc(%x)\n", pager); +#endif + TAILQ_REMOVE(&dev_pager_list, pager, pg_list); + /* + * Get the object. + * Note: cannot use vm_object_lookup since object has already + * been removed from the hash chain. + */ + devp = (dev_pager_t)pager->pg_data; + object = devp->devp_object; +#ifdef DEBUG + if (dpagerdebug & DDB_ALLOC) + printf("dev_pager_dealloc: devp %x object %x\n", devp, object); +#endif + /* + * Free up our fake pages. + */ + while ((m = devp->devp_pglist.tqh_first) != NULL) { + TAILQ_REMOVE(&devp->devp_pglist, m, pageq); + dev_pager_putfake(m); + } + free((caddr_t)devp, M_VMPGDATA); + free((caddr_t)pager, M_VMPAGER); +} + +static int +dev_pager_getpage(pager, mlist, npages, sync) + vm_pager_t pager; + vm_page_t *mlist; + int npages; + boolean_t sync; +{ + register vm_object_t object; + vm_offset_t offset, paddr; + vm_page_t page; + dev_t dev; + int (*mapfunc)(), prot; + vm_page_t m; + +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_getpage(%x, %x, %x, %x)\n", + pager, mlist, npages, sync); +#endif + + if (npages != 1) + panic("dev_pager_getpage: cannot handle multiple pages"); + m = *mlist; + + object = m->object; + dev = (dev_t)pager->pg_handle; + offset = m->offset + object->paging_offset; + prot = PROT_READ; /* XXX should pass in? */ + mapfunc = cdevsw[major(dev)].d_mmap; +#ifdef DIAGNOSTIC + if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) + panic("dev_pager_getpage: no map function"); +#endif + paddr = pmap_phys_address((*mapfunc)(dev, (int)offset, prot)); +#ifdef DIAGNOSTIC + if (paddr == -1) + panic("dev_pager_getpage: map function returns error"); +#endif + /* + * Replace the passed in page with our own fake page and free + * up the original. + */ + page = dev_pager_getfake(paddr); + TAILQ_INSERT_TAIL(&((dev_pager_t)pager->pg_data)->devp_pglist, page, + pageq); + vm_object_lock(object); + vm_page_lock_queues(); + vm_page_free(m); + vm_page_insert(page, object, offset); + vm_page_unlock_queues(); + PAGE_WAKEUP(m); + if (offset + PAGE_SIZE > object->size) + object->size = offset + PAGE_SIZE; /* XXX anal */ + vm_object_unlock(object); + + return(VM_PAGER_OK); +} + +static int +dev_pager_putpage(pager, mlist, npages, sync) + vm_pager_t pager; + vm_page_t *mlist; + int npages; + boolean_t sync; +{ +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_putpage(%x, %x, %x, %x)\n", + pager, mlist, npages, sync); +#endif + if (pager == NULL) + return; + panic("dev_pager_putpage called"); +} + +static boolean_t +dev_pager_haspage(pager, offset) + vm_pager_t pager; + vm_offset_t offset; +{ +#ifdef DEBUG + if (dpagerdebug & DDB_FOLLOW) + printf("dev_pager_haspage(%x, %x)\n", pager, offset); +#endif + return(TRUE); +} + +static vm_page_t +dev_pager_getfake(paddr) + vm_offset_t paddr; +{ + vm_page_t m; + int i; + + if (dev_pager_fakelist.tqh_first == NULL) { + m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK); + for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) { + TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); + m++; + } + } + m = dev_pager_fakelist.tqh_first; + TAILQ_REMOVE(&dev_pager_fakelist, m, pageq); + m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS; + m->phys_addr = paddr; + m->wire_count = 1; + return(m); +} + +static void +dev_pager_putfake(m) + vm_page_t m; +{ +#ifdef DIAGNOSTIC + if (!(m->flags & PG_FICTITIOUS)) + panic("dev_pager_putfake: bad page"); +#endif + TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); +} |