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 39efeaf95aSDavid Greenman * $Id: device_pager.c,v 1.15 1995/12/03 18:59:55 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> 48efeaf95aSDavid Greenman #include <sys/queue.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <vm/vm.h> 51efeaf95aSDavid Greenman #include <vm/vm_param.h> 52efeaf95aSDavid Greenman #include <vm/vm_prot.h> 53df8bae1dSRodney W. Grimes #include <vm/vm_kern.h> 54efeaf95aSDavid Greenman #include <vm/vm_object.h> 55df8bae1dSRodney W. Grimes #include <vm/vm_page.h> 5624a1cce3SDavid Greenman #include <vm/vm_pager.h> 57df8bae1dSRodney W. Grimes #include <vm/device_pager.h> 58df8bae1dSRodney W. Grimes 5924a1cce3SDavid Greenman struct pagerlst dev_pager_object_list; /* list of device pager objects */ 6024a1cce3SDavid Greenman TAILQ_HEAD(, vm_page) dev_pager_fakelist; /* list of available vm_page_t's */ 61df8bae1dSRodney W. Grimes 62df8bae1dSRodney W. Grimes static vm_page_t dev_pager_getfake __P((vm_offset_t)); 63df8bae1dSRodney W. Grimes static void dev_pager_putfake __P((vm_page_t)); 64df8bae1dSRodney W. Grimes 6524a1cce3SDavid Greenman static int dev_pager_alloc_lock, dev_pager_alloc_lock_want; 6624a1cce3SDavid Greenman 67df8bae1dSRodney W. Grimes struct pagerops devicepagerops = { 68df8bae1dSRodney W. Grimes dev_pager_init, 69df8bae1dSRodney W. Grimes dev_pager_alloc, 70df8bae1dSRodney W. Grimes dev_pager_dealloc, 7124a1cce3SDavid Greenman dev_pager_getpages, 7224a1cce3SDavid Greenman dev_pager_putpages, 7324a1cce3SDavid Greenman dev_pager_haspage, 7424a1cce3SDavid Greenman NULL 75df8bae1dSRodney W. Grimes }; 76df8bae1dSRodney W. Grimes 7724a1cce3SDavid Greenman void 78df8bae1dSRodney W. Grimes dev_pager_init() 79df8bae1dSRodney W. Grimes { 8024a1cce3SDavid Greenman TAILQ_INIT(&dev_pager_object_list); 81df8bae1dSRodney W. Grimes TAILQ_INIT(&dev_pager_fakelist); 82df8bae1dSRodney W. Grimes } 83df8bae1dSRodney W. Grimes 8424a1cce3SDavid Greenman vm_object_t 85df8bae1dSRodney W. Grimes dev_pager_alloc(handle, size, prot, foff) 86ee3a64c9SDavid Greenman void *handle; 87df8bae1dSRodney W. Grimes vm_size_t size; 88df8bae1dSRodney W. Grimes vm_prot_t prot; 89df8bae1dSRodney W. Grimes vm_offset_t foff; 90df8bae1dSRodney W. Grimes { 91df8bae1dSRodney W. Grimes dev_t dev; 92cac597e4SBruce Evans d_mmap_t *mapfunc; 93df8bae1dSRodney W. Grimes vm_object_t object; 9426f9a767SRodney W. Grimes unsigned int npages, off; 95df8bae1dSRodney W. Grimes 96df8bae1dSRodney W. Grimes /* 97df8bae1dSRodney W. Grimes * Make sure this device can be mapped. 98df8bae1dSRodney W. Grimes */ 9926f9a767SRodney W. Grimes dev = (dev_t) (u_long) handle; 100df8bae1dSRodney W. Grimes mapfunc = cdevsw[major(dev)].d_mmap; 101f31d402cSBruce Evans if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) { 102f31d402cSBruce Evans printf("obsolete map function %p\n", (void *)mapfunc); 103df8bae1dSRodney W. Grimes return (NULL); 104f31d402cSBruce Evans } 105df8bae1dSRodney W. Grimes 106df8bae1dSRodney W. Grimes /* 107df8bae1dSRodney W. Grimes * Offset should be page aligned. 108df8bae1dSRodney W. Grimes */ 10926f9a767SRodney W. Grimes if (foff & (PAGE_SIZE - 1)) 110df8bae1dSRodney W. Grimes return (NULL); 111df8bae1dSRodney W. Grimes 112df8bae1dSRodney W. Grimes /* 1130d94caffSDavid Greenman * Check that the specified range of the device allows the desired 1140d94caffSDavid Greenman * protection. 115df8bae1dSRodney W. Grimes * 116df8bae1dSRodney W. Grimes * XXX assumes VM_PROT_* == PROT_* 117df8bae1dSRodney W. Grimes */ 118df8bae1dSRodney W. Grimes npages = atop(round_page(size)); 119df8bae1dSRodney W. Grimes for (off = foff; npages--; off += PAGE_SIZE) 120df8bae1dSRodney W. Grimes if ((*mapfunc) (dev, off, (int) prot) == -1) 121df8bae1dSRodney W. Grimes return (NULL); 122df8bae1dSRodney W. Grimes 123df8bae1dSRodney W. Grimes /* 12424a1cce3SDavid Greenman * Lock to prevent object creation race contion. 12524a1cce3SDavid Greenman */ 12624a1cce3SDavid Greenman while (dev_pager_alloc_lock) { 12724a1cce3SDavid Greenman dev_pager_alloc_lock_want++; 12824a1cce3SDavid Greenman tsleep(&dev_pager_alloc_lock, PVM, "dvpall", 0); 12924a1cce3SDavid Greenman dev_pager_alloc_lock_want--; 13024a1cce3SDavid Greenman } 13124a1cce3SDavid Greenman dev_pager_alloc_lock = 1; 13224a1cce3SDavid Greenman 13324a1cce3SDavid Greenman /* 134df8bae1dSRodney W. Grimes * Look up pager, creating as necessary. 135df8bae1dSRodney W. Grimes */ 13624a1cce3SDavid Greenman object = vm_pager_object_lookup(&dev_pager_object_list, handle); 13724a1cce3SDavid Greenman if (object == NULL) { 138df8bae1dSRodney W. Grimes /* 139df8bae1dSRodney W. Grimes * Allocate object and associate it with the pager. 140df8bae1dSRodney W. Grimes */ 14124a1cce3SDavid Greenman object = vm_object_allocate(OBJT_DEVICE, foff + size); 14224a1cce3SDavid Greenman object->handle = handle; 14324a1cce3SDavid Greenman TAILQ_INIT(&object->un_pager.devp.devp_pglist); 14424a1cce3SDavid Greenman TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list); 145df8bae1dSRodney W. Grimes } else { 146df8bae1dSRodney W. Grimes /* 1477fb0c17eSDavid Greenman * Gain a reference to the object. 148df8bae1dSRodney W. Grimes */ 14924a1cce3SDavid Greenman vm_object_reference(object); 1505f55e841SDavid Greenman if (foff + size > object->size) 1515f55e841SDavid Greenman object->size = foff + size; 152df8bae1dSRodney W. Grimes } 153df8bae1dSRodney W. Grimes 15424a1cce3SDavid Greenman dev_pager_alloc_lock = 0; 15524a1cce3SDavid Greenman if (dev_pager_alloc_lock_want) 15624a1cce3SDavid Greenman wakeup(&dev_pager_alloc_lock); 15724a1cce3SDavid Greenman 15824a1cce3SDavid Greenman return (object); 15924a1cce3SDavid Greenman } 16024a1cce3SDavid Greenman 16124a1cce3SDavid Greenman void 16224a1cce3SDavid Greenman dev_pager_dealloc(object) 163df8bae1dSRodney W. Grimes vm_object_t object; 16424a1cce3SDavid Greenman { 165df8bae1dSRodney W. Grimes vm_page_t m; 166df8bae1dSRodney W. Grimes 16724a1cce3SDavid Greenman TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list); 168df8bae1dSRodney W. Grimes /* 169df8bae1dSRodney W. Grimes * Free up our fake pages. 170df8bae1dSRodney W. Grimes */ 17124a1cce3SDavid Greenman while ((m = object->un_pager.devp.devp_pglist.tqh_first) != 0) { 17224a1cce3SDavid Greenman TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq); 173df8bae1dSRodney W. Grimes dev_pager_putfake(m); 174df8bae1dSRodney W. Grimes } 175df8bae1dSRodney W. Grimes } 176df8bae1dSRodney W. Grimes 17724a1cce3SDavid Greenman int 17824a1cce3SDavid Greenman dev_pager_getpages(object, m, count, reqpage) 17924a1cce3SDavid Greenman vm_object_t object; 18024a1cce3SDavid Greenman vm_page_t *m; 18124a1cce3SDavid Greenman int count; 18224a1cce3SDavid Greenman int reqpage; 183df8bae1dSRodney W. Grimes { 184df8bae1dSRodney W. Grimes vm_offset_t offset, paddr; 185df8bae1dSRodney W. Grimes vm_page_t page; 186df8bae1dSRodney W. Grimes dev_t dev; 18724a1cce3SDavid Greenman int i, s; 188cac597e4SBruce Evans d_mmap_t *mapfunc; 189cac597e4SBruce Evans int prot; 190df8bae1dSRodney W. Grimes 19124a1cce3SDavid Greenman dev = (dev_t) (u_long) object->handle; 19224a1cce3SDavid Greenman offset = m[reqpage]->offset + object->paging_offset; 193df8bae1dSRodney W. Grimes prot = PROT_READ; /* XXX should pass in? */ 194df8bae1dSRodney W. Grimes mapfunc = cdevsw[major(dev)].d_mmap; 19526f9a767SRodney W. Grimes 196f31d402cSBruce Evans if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) 197df8bae1dSRodney W. Grimes panic("dev_pager_getpage: no map function"); 19826f9a767SRodney W. Grimes 19926f9a767SRodney W. Grimes paddr = pmap_phys_address((*mapfunc) ((dev_t) dev, (int) offset, prot)); 200df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 201df8bae1dSRodney W. Grimes if (paddr == -1) 202df8bae1dSRodney W. Grimes panic("dev_pager_getpage: map function returns error"); 203df8bae1dSRodney W. Grimes #endif 204df8bae1dSRodney W. Grimes /* 20524a1cce3SDavid Greenman * Replace the passed in reqpage page with our own fake page and free up the 20624a1cce3SDavid Greenman * all of the original pages. 207df8bae1dSRodney W. Grimes */ 208df8bae1dSRodney W. Grimes page = dev_pager_getfake(paddr); 20924a1cce3SDavid Greenman TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq); 21024a1cce3SDavid Greenman for (i = 0; i < count; i++) { 21124a1cce3SDavid Greenman PAGE_WAKEUP(m[i]); 21224a1cce3SDavid Greenman vm_page_free(m[i]); 21324a1cce3SDavid Greenman } 21426f9a767SRodney W. Grimes s = splhigh(); 21526f9a767SRodney W. Grimes vm_page_insert(page, object, offset); 21626f9a767SRodney W. Grimes splx(s); 217df8bae1dSRodney W. Grimes 218df8bae1dSRodney W. Grimes return (VM_PAGER_OK); 219df8bae1dSRodney W. Grimes } 220df8bae1dSRodney W. Grimes 22124a1cce3SDavid Greenman int 22224a1cce3SDavid Greenman dev_pager_putpages(object, m, count, sync, rtvals) 22324a1cce3SDavid Greenman vm_object_t object; 22424a1cce3SDavid Greenman vm_page_t *m; 22524a1cce3SDavid Greenman int count; 226df8bae1dSRodney W. Grimes boolean_t sync; 22724a1cce3SDavid Greenman int *rtvals; 228df8bae1dSRodney W. Grimes { 229df8bae1dSRodney W. Grimes panic("dev_pager_putpage called"); 230df8bae1dSRodney W. Grimes } 231df8bae1dSRodney W. Grimes 23224a1cce3SDavid Greenman boolean_t 23324a1cce3SDavid Greenman dev_pager_haspage(object, offset, before, after) 23424a1cce3SDavid Greenman vm_object_t object; 235df8bae1dSRodney W. Grimes vm_offset_t offset; 23624a1cce3SDavid Greenman int *before; 23724a1cce3SDavid Greenman int *after; 238df8bae1dSRodney W. Grimes { 23924a1cce3SDavid Greenman if (before != NULL) 24024a1cce3SDavid Greenman *before = 0; 24124a1cce3SDavid Greenman if (after != NULL) 24224a1cce3SDavid Greenman *after = 0; 243df8bae1dSRodney W. Grimes return (TRUE); 244df8bae1dSRodney W. Grimes } 245df8bae1dSRodney W. Grimes 246df8bae1dSRodney W. Grimes static vm_page_t 247df8bae1dSRodney W. Grimes dev_pager_getfake(paddr) 248df8bae1dSRodney W. Grimes vm_offset_t paddr; 249df8bae1dSRodney W. Grimes { 250df8bae1dSRodney W. Grimes vm_page_t m; 251df8bae1dSRodney W. Grimes int i; 252df8bae1dSRodney W. Grimes 253df8bae1dSRodney W. Grimes if (dev_pager_fakelist.tqh_first == NULL) { 2540d94caffSDavid Greenman m = (vm_page_t) malloc(PAGE_SIZE * 2, M_VMPGDATA, M_WAITOK); 2550d94caffSDavid Greenman for (i = (PAGE_SIZE * 2) / sizeof(*m); i > 0; i--) { 256df8bae1dSRodney W. Grimes TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 257df8bae1dSRodney W. Grimes m++; 258df8bae1dSRodney W. Grimes } 259df8bae1dSRodney W. Grimes } 260df8bae1dSRodney W. Grimes m = dev_pager_fakelist.tqh_first; 261df8bae1dSRodney W. Grimes TAILQ_REMOVE(&dev_pager_fakelist, m, pageq); 26226f9a767SRodney W. Grimes 2630d94caffSDavid Greenman m->flags = PG_BUSY | PG_FICTITIOUS; 2640d94caffSDavid Greenman m->valid = VM_PAGE_BITS_ALL; 26524a1cce3SDavid Greenman m->dirty = 0; 2660d94caffSDavid Greenman m->busy = 0; 2670d94caffSDavid Greenman m->bmapped = 0; 26826f9a767SRodney W. Grimes 269df8bae1dSRodney W. Grimes m->wire_count = 1; 27026f9a767SRodney W. Grimes m->phys_addr = paddr; 27126f9a767SRodney W. Grimes 272df8bae1dSRodney W. Grimes return (m); 273df8bae1dSRodney W. Grimes } 274df8bae1dSRodney W. Grimes 275df8bae1dSRodney W. Grimes static void 276df8bae1dSRodney W. Grimes dev_pager_putfake(m) 277df8bae1dSRodney W. Grimes vm_page_t m; 278df8bae1dSRodney W. Grimes { 279df8bae1dSRodney W. Grimes if (!(m->flags & PG_FICTITIOUS)) 280df8bae1dSRodney W. Grimes panic("dev_pager_putfake: bad page"); 281df8bae1dSRodney W. Grimes TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 282df8bae1dSRodney W. Grimes } 283