1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1990 University of Utah. 3df8bae1dSRodney W. Grimes * Copyright (c) 1991, 1993 4df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 5df8bae1dSRodney W. Grimes * 6df8bae1dSRodney W. Grimes * This code is derived from software contributed to Berkeley by 7df8bae1dSRodney W. Grimes * the Systems Programming Group of the University of Utah Computer 8df8bae1dSRodney W. Grimes * Science Department. 9df8bae1dSRodney W. Grimes * 10df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 11df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 12df8bae1dSRodney W. Grimes * are met: 13df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 15df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 16df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 17df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 18df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 19df8bae1dSRodney W. Grimes * must display the following acknowledgement: 20df8bae1dSRodney W. Grimes * This product includes software developed by the University of 21df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 22df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 23df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 24df8bae1dSRodney W. Grimes * without specific prior written permission. 25df8bae1dSRodney W. Grimes * 26df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36df8bae1dSRodney W. Grimes * SUCH DAMAGE. 37df8bae1dSRodney W. Grimes * 3826f9a767SRodney W. Grimes * @(#)device_pager.c 8.1 (Berkeley) 6/11/93 39f31d402cSBruce Evans * $Id: device_pager.c,v 1.14 1995/12/03 12:18:30 bde Exp $ 40df8bae1dSRodney W. Grimes */ 41df8bae1dSRodney W. Grimes 42df8bae1dSRodney W. Grimes #include <sys/param.h> 43df8bae1dSRodney W. Grimes #include <sys/systm.h> 44df8bae1dSRodney W. Grimes #include <sys/conf.h> 45df8bae1dSRodney W. Grimes #include <sys/mman.h> 46df8bae1dSRodney W. Grimes #include <sys/malloc.h> 47623ae52eSPoul-Henning Kamp #include <sys/proc.h> 48df8bae1dSRodney W. Grimes 49df8bae1dSRodney W. Grimes #include <vm/vm.h> 50df8bae1dSRodney W. Grimes #include <vm/vm_kern.h> 51df8bae1dSRodney W. Grimes #include <vm/vm_page.h> 5224a1cce3SDavid Greenman #include <vm/vm_pager.h> 53df8bae1dSRodney W. Grimes #include <vm/device_pager.h> 54df8bae1dSRodney W. Grimes 5524a1cce3SDavid Greenman struct pagerlst dev_pager_object_list; /* list of device pager objects */ 5624a1cce3SDavid Greenman TAILQ_HEAD(, vm_page) dev_pager_fakelist; /* list of available vm_page_t's */ 57df8bae1dSRodney W. Grimes 58df8bae1dSRodney W. Grimes static vm_page_t dev_pager_getfake __P((vm_offset_t)); 59df8bae1dSRodney W. Grimes static void dev_pager_putfake __P((vm_page_t)); 60df8bae1dSRodney W. Grimes 6124a1cce3SDavid Greenman static int dev_pager_alloc_lock, dev_pager_alloc_lock_want; 6224a1cce3SDavid Greenman 63df8bae1dSRodney W. Grimes struct pagerops devicepagerops = { 64df8bae1dSRodney W. Grimes dev_pager_init, 65df8bae1dSRodney W. Grimes dev_pager_alloc, 66df8bae1dSRodney W. Grimes dev_pager_dealloc, 6724a1cce3SDavid Greenman dev_pager_getpages, 6824a1cce3SDavid Greenman dev_pager_putpages, 6924a1cce3SDavid Greenman dev_pager_haspage, 7024a1cce3SDavid Greenman NULL 71df8bae1dSRodney W. Grimes }; 72df8bae1dSRodney W. Grimes 7324a1cce3SDavid Greenman void 74df8bae1dSRodney W. Grimes dev_pager_init() 75df8bae1dSRodney W. Grimes { 7624a1cce3SDavid Greenman TAILQ_INIT(&dev_pager_object_list); 77df8bae1dSRodney W. Grimes TAILQ_INIT(&dev_pager_fakelist); 78df8bae1dSRodney W. Grimes } 79df8bae1dSRodney W. Grimes 8024a1cce3SDavid Greenman vm_object_t 81df8bae1dSRodney W. Grimes dev_pager_alloc(handle, size, prot, foff) 82ee3a64c9SDavid Greenman void *handle; 83df8bae1dSRodney W. Grimes vm_size_t size; 84df8bae1dSRodney W. Grimes vm_prot_t prot; 85df8bae1dSRodney W. Grimes vm_offset_t foff; 86df8bae1dSRodney W. Grimes { 87df8bae1dSRodney W. Grimes dev_t dev; 88cac597e4SBruce Evans d_mmap_t *mapfunc; 89df8bae1dSRodney W. Grimes vm_object_t object; 9026f9a767SRodney W. Grimes unsigned int npages, off; 91df8bae1dSRodney W. Grimes 92df8bae1dSRodney W. Grimes /* 93df8bae1dSRodney W. Grimes * Make sure this device can be mapped. 94df8bae1dSRodney W. Grimes */ 9526f9a767SRodney W. Grimes dev = (dev_t) (u_long) handle; 96df8bae1dSRodney W. Grimes mapfunc = cdevsw[major(dev)].d_mmap; 97f31d402cSBruce Evans if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) { 98f31d402cSBruce Evans printf("obsolete map function %p\n", (void *)mapfunc); 99df8bae1dSRodney W. Grimes return (NULL); 100f31d402cSBruce Evans } 101df8bae1dSRodney W. Grimes 102df8bae1dSRodney W. Grimes /* 103df8bae1dSRodney W. Grimes * Offset should be page aligned. 104df8bae1dSRodney W. Grimes */ 10526f9a767SRodney W. Grimes if (foff & (PAGE_SIZE - 1)) 106df8bae1dSRodney W. Grimes return (NULL); 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes /* 1090d94caffSDavid Greenman * Check that the specified range of the device allows the desired 1100d94caffSDavid Greenman * protection. 111df8bae1dSRodney W. Grimes * 112df8bae1dSRodney W. Grimes * XXX assumes VM_PROT_* == PROT_* 113df8bae1dSRodney W. Grimes */ 114df8bae1dSRodney W. Grimes npages = atop(round_page(size)); 115df8bae1dSRodney W. Grimes for (off = foff; npages--; off += PAGE_SIZE) 116df8bae1dSRodney W. Grimes if ((*mapfunc) (dev, off, (int) prot) == -1) 117df8bae1dSRodney W. Grimes return (NULL); 118df8bae1dSRodney W. Grimes 119df8bae1dSRodney W. Grimes /* 12024a1cce3SDavid Greenman * Lock to prevent object creation race contion. 12124a1cce3SDavid Greenman */ 12224a1cce3SDavid Greenman while (dev_pager_alloc_lock) { 12324a1cce3SDavid Greenman dev_pager_alloc_lock_want++; 12424a1cce3SDavid Greenman tsleep(&dev_pager_alloc_lock, PVM, "dvpall", 0); 12524a1cce3SDavid Greenman dev_pager_alloc_lock_want--; 12624a1cce3SDavid Greenman } 12724a1cce3SDavid Greenman dev_pager_alloc_lock = 1; 12824a1cce3SDavid Greenman 12924a1cce3SDavid Greenman /* 130df8bae1dSRodney W. Grimes * Look up pager, creating as necessary. 131df8bae1dSRodney W. Grimes */ 13224a1cce3SDavid Greenman object = vm_pager_object_lookup(&dev_pager_object_list, handle); 13324a1cce3SDavid Greenman if (object == NULL) { 134df8bae1dSRodney W. Grimes /* 135df8bae1dSRodney W. Grimes * Allocate object and associate it with the pager. 136df8bae1dSRodney W. Grimes */ 13724a1cce3SDavid Greenman object = vm_object_allocate(OBJT_DEVICE, foff + size); 13824a1cce3SDavid Greenman object->handle = handle; 13924a1cce3SDavid Greenman TAILQ_INIT(&object->un_pager.devp.devp_pglist); 14024a1cce3SDavid Greenman TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list); 141df8bae1dSRodney W. Grimes } else { 142df8bae1dSRodney W. Grimes /* 1437fb0c17eSDavid Greenman * Gain a reference to the object. 144df8bae1dSRodney W. Grimes */ 14524a1cce3SDavid Greenman vm_object_reference(object); 1465f55e841SDavid Greenman if (foff + size > object->size) 1475f55e841SDavid Greenman object->size = foff + size; 148df8bae1dSRodney W. Grimes } 149df8bae1dSRodney W. Grimes 15024a1cce3SDavid Greenman dev_pager_alloc_lock = 0; 15124a1cce3SDavid Greenman if (dev_pager_alloc_lock_want) 15224a1cce3SDavid Greenman wakeup(&dev_pager_alloc_lock); 15324a1cce3SDavid Greenman 15424a1cce3SDavid Greenman return (object); 15524a1cce3SDavid Greenman } 15624a1cce3SDavid Greenman 15724a1cce3SDavid Greenman void 15824a1cce3SDavid Greenman dev_pager_dealloc(object) 159df8bae1dSRodney W. Grimes vm_object_t object; 16024a1cce3SDavid Greenman { 161df8bae1dSRodney W. Grimes vm_page_t m; 162df8bae1dSRodney W. Grimes 16324a1cce3SDavid Greenman TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list); 164df8bae1dSRodney W. Grimes /* 165df8bae1dSRodney W. Grimes * Free up our fake pages. 166df8bae1dSRodney W. Grimes */ 16724a1cce3SDavid Greenman while ((m = object->un_pager.devp.devp_pglist.tqh_first) != 0) { 16824a1cce3SDavid Greenman TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq); 169df8bae1dSRodney W. Grimes dev_pager_putfake(m); 170df8bae1dSRodney W. Grimes } 171df8bae1dSRodney W. Grimes } 172df8bae1dSRodney W. Grimes 17324a1cce3SDavid Greenman int 17424a1cce3SDavid Greenman dev_pager_getpages(object, m, count, reqpage) 17524a1cce3SDavid Greenman vm_object_t object; 17624a1cce3SDavid Greenman vm_page_t *m; 17724a1cce3SDavid Greenman int count; 17824a1cce3SDavid Greenman int reqpage; 179df8bae1dSRodney W. Grimes { 180df8bae1dSRodney W. Grimes vm_offset_t offset, paddr; 181df8bae1dSRodney W. Grimes vm_page_t page; 182df8bae1dSRodney W. Grimes dev_t dev; 18324a1cce3SDavid Greenman int i, s; 184cac597e4SBruce Evans d_mmap_t *mapfunc; 185cac597e4SBruce Evans int prot; 186df8bae1dSRodney W. Grimes 18724a1cce3SDavid Greenman dev = (dev_t) (u_long) object->handle; 18824a1cce3SDavid Greenman offset = m[reqpage]->offset + object->paging_offset; 189df8bae1dSRodney W. Grimes prot = PROT_READ; /* XXX should pass in? */ 190df8bae1dSRodney W. Grimes mapfunc = cdevsw[major(dev)].d_mmap; 19126f9a767SRodney W. Grimes 192f31d402cSBruce Evans if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) 193df8bae1dSRodney W. Grimes panic("dev_pager_getpage: no map function"); 19426f9a767SRodney W. Grimes 19526f9a767SRodney W. Grimes paddr = pmap_phys_address((*mapfunc) ((dev_t) dev, (int) offset, prot)); 196df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 197df8bae1dSRodney W. Grimes if (paddr == -1) 198df8bae1dSRodney W. Grimes panic("dev_pager_getpage: map function returns error"); 199df8bae1dSRodney W. Grimes #endif 200df8bae1dSRodney W. Grimes /* 20124a1cce3SDavid Greenman * Replace the passed in reqpage page with our own fake page and free up the 20224a1cce3SDavid Greenman * all of the original pages. 203df8bae1dSRodney W. Grimes */ 204df8bae1dSRodney W. Grimes page = dev_pager_getfake(paddr); 20524a1cce3SDavid Greenman TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq); 20624a1cce3SDavid Greenman for (i = 0; i < count; i++) { 20724a1cce3SDavid Greenman PAGE_WAKEUP(m[i]); 20824a1cce3SDavid Greenman vm_page_free(m[i]); 20924a1cce3SDavid Greenman } 21026f9a767SRodney W. Grimes s = splhigh(); 21126f9a767SRodney W. Grimes vm_page_insert(page, object, offset); 21226f9a767SRodney W. Grimes splx(s); 213df8bae1dSRodney W. Grimes 214df8bae1dSRodney W. Grimes return (VM_PAGER_OK); 215df8bae1dSRodney W. Grimes } 216df8bae1dSRodney W. Grimes 21724a1cce3SDavid Greenman int 21824a1cce3SDavid Greenman dev_pager_putpages(object, m, count, sync, rtvals) 21924a1cce3SDavid Greenman vm_object_t object; 22024a1cce3SDavid Greenman vm_page_t *m; 22124a1cce3SDavid Greenman int count; 222df8bae1dSRodney W. Grimes boolean_t sync; 22324a1cce3SDavid Greenman int *rtvals; 224df8bae1dSRodney W. Grimes { 225df8bae1dSRodney W. Grimes panic("dev_pager_putpage called"); 226df8bae1dSRodney W. Grimes } 227df8bae1dSRodney W. Grimes 22824a1cce3SDavid Greenman boolean_t 22924a1cce3SDavid Greenman dev_pager_haspage(object, offset, before, after) 23024a1cce3SDavid Greenman vm_object_t object; 231df8bae1dSRodney W. Grimes vm_offset_t offset; 23224a1cce3SDavid Greenman int *before; 23324a1cce3SDavid Greenman int *after; 234df8bae1dSRodney W. Grimes { 23524a1cce3SDavid Greenman if (before != NULL) 23624a1cce3SDavid Greenman *before = 0; 23724a1cce3SDavid Greenman if (after != NULL) 23824a1cce3SDavid Greenman *after = 0; 239df8bae1dSRodney W. Grimes return (TRUE); 240df8bae1dSRodney W. Grimes } 241df8bae1dSRodney W. Grimes 242df8bae1dSRodney W. Grimes static vm_page_t 243df8bae1dSRodney W. Grimes dev_pager_getfake(paddr) 244df8bae1dSRodney W. Grimes vm_offset_t paddr; 245df8bae1dSRodney W. Grimes { 246df8bae1dSRodney W. Grimes vm_page_t m; 247df8bae1dSRodney W. Grimes int i; 248df8bae1dSRodney W. Grimes 249df8bae1dSRodney W. Grimes if (dev_pager_fakelist.tqh_first == NULL) { 2500d94caffSDavid Greenman m = (vm_page_t) malloc(PAGE_SIZE * 2, M_VMPGDATA, M_WAITOK); 2510d94caffSDavid Greenman for (i = (PAGE_SIZE * 2) / sizeof(*m); i > 0; i--) { 252df8bae1dSRodney W. Grimes TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 253df8bae1dSRodney W. Grimes m++; 254df8bae1dSRodney W. Grimes } 255df8bae1dSRodney W. Grimes } 256df8bae1dSRodney W. Grimes m = dev_pager_fakelist.tqh_first; 257df8bae1dSRodney W. Grimes TAILQ_REMOVE(&dev_pager_fakelist, m, pageq); 25826f9a767SRodney W. Grimes 2590d94caffSDavid Greenman m->flags = PG_BUSY | PG_FICTITIOUS; 2600d94caffSDavid Greenman m->valid = VM_PAGE_BITS_ALL; 26124a1cce3SDavid Greenman m->dirty = 0; 2620d94caffSDavid Greenman m->busy = 0; 2630d94caffSDavid Greenman m->bmapped = 0; 26426f9a767SRodney W. Grimes 265df8bae1dSRodney W. Grimes m->wire_count = 1; 26626f9a767SRodney W. Grimes m->phys_addr = paddr; 26726f9a767SRodney W. Grimes 268df8bae1dSRodney W. Grimes return (m); 269df8bae1dSRodney W. Grimes } 270df8bae1dSRodney W. Grimes 271df8bae1dSRodney W. Grimes static void 272df8bae1dSRodney W. Grimes dev_pager_putfake(m) 273df8bae1dSRodney W. Grimes vm_page_t m; 274df8bae1dSRodney W. Grimes { 275df8bae1dSRodney W. Grimes if (!(m->flags & PG_FICTITIOUS)) 276df8bae1dSRodney W. Grimes panic("dev_pager_putfake: bad page"); 277df8bae1dSRodney W. Grimes TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 278df8bae1dSRodney W. Grimes } 279