device_pager.c revision 58634
1139804Simp/* 2126324Sjhb * Copyright (c) 1990 University of Utah. 3126324Sjhb * Copyright (c) 1991, 1993 4126324Sjhb * The Regents of the University of California. All rights reserved. 5126324Sjhb * 6126324Sjhb * This code is derived from software contributed to Berkeley by 7126324Sjhb * the Systems Programming Group of the University of Utah Computer 8126324Sjhb * Science Department. 9126324Sjhb * 10126324Sjhb * Redistribution and use in source and binary forms, with or without 11126324Sjhb * modification, are permitted provided that the following conditions 12126324Sjhb * are met: 13126324Sjhb * 1. Redistributions of source code must retain the above copyright 14126324Sjhb * notice, this list of conditions and the following disclaimer. 15126324Sjhb * 2. Redistributions in binary form must reproduce the above copyright 16126324Sjhb * notice, this list of conditions and the following disclaimer in the 17126324Sjhb * documentation and/or other materials provided with the distribution. 18126324Sjhb * 3. All advertising materials mentioning features or use of this software 19126324Sjhb * must display the following acknowledgment: 20126324Sjhb * This product includes software developed by the University of 21126324Sjhb * California, Berkeley and its contributors. 22126324Sjhb * 4. Neither the name of the University nor the names of its contributors 23126324Sjhb * may be used to endorse or promote products derived from this software 24126324Sjhb * without specific prior written permission. 25126324Sjhb * 26126324Sjhb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27126324Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28126324Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29126324Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30126324Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31126324Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32126324Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33126324Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34126324Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35126324Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36126324Sjhb * SUCH DAMAGE. 37126324Sjhb * 38126324Sjhb * @(#)device_pager.c 8.1 (Berkeley) 6/11/93 39126324Sjhb * $FreeBSD: head/sys/vm/device_pager.c 58634 2000-03-26 15:20:23Z charnier $ 40126324Sjhb */ 41126324Sjhb 42126324Sjhb#include <sys/param.h> 43126324Sjhb#include <sys/systm.h> 44126324Sjhb#include <sys/conf.h> 45126324Sjhb#include <sys/mman.h> 46126324Sjhb 47126324Sjhb#include <vm/vm.h> 48126324Sjhb#include <vm/vm_object.h> 49126324Sjhb#include <vm/vm_page.h> 50126324Sjhb#include <vm/vm_pager.h> 51126324Sjhb#include <vm/vm_zone.h> 52126324Sjhb 53126324Sjhbstatic void dev_pager_init __P((void)); 54126324Sjhbstatic vm_object_t dev_pager_alloc __P((void *, vm_ooffset_t, vm_prot_t, 55126324Sjhb vm_ooffset_t)); 56126324Sjhbstatic void dev_pager_dealloc __P((vm_object_t)); 57126324Sjhbstatic int dev_pager_getpages __P((vm_object_t, vm_page_t *, int, int)); 58126324Sjhbstatic void dev_pager_putpages __P((vm_object_t, vm_page_t *, int, 59126324Sjhb boolean_t, int *)); 60126324Sjhbstatic boolean_t dev_pager_haspage __P((vm_object_t, vm_pindex_t, int *, 61126324Sjhb int *)); 62154936Sjhb 63154936Sjhb/* list of device pager objects */ 64170640Sjeffstatic struct pagerlst dev_pager_object_list; 65296973Scem 66154936Sjhbstatic vm_zone_t fakepg_zone; 67126324Sjhbstatic struct vm_zone fakepg_zone_store; 68126324Sjhb 69126324Sjhbstatic vm_page_t dev_pager_getfake __P((vm_offset_t)); 70126324Sjhbstatic void dev_pager_putfake __P((vm_page_t)); 71126324Sjhb 72126324Sjhbstatic int dev_pager_alloc_lock, dev_pager_alloc_lock_want; 73126324Sjhb 74177372Sjeffstruct pagerops devicepagerops = { 75126324Sjhb dev_pager_init, 76235459Srstone dev_pager_alloc, 77126324Sjhb dev_pager_dealloc, 78126324Sjhb dev_pager_getpages, 79296927Scem dev_pager_putpages, 80131259Sjhb dev_pager_haspage, 81126324Sjhb NULL 82169666Sjeff}; 83169666Sjeff 84154936Sjhbstatic void 85154936Sjhbdev_pager_init() 86154936Sjhb{ 87154936Sjhb TAILQ_INIT(&dev_pager_object_list); 88296927Scem fakepg_zone = &fakepg_zone_store; 89126324Sjhb zinitna(fakepg_zone, NULL, "DP fakepg", sizeof(struct vm_page), 0, 0, 2); 90248186Smav} 91248186Smav 92126324Sjhbstatic vm_object_t 93248186Smavdev_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff) 94126324Sjhb{ 95126324Sjhb dev_t dev; 96248186Smav d_mmap_t *mapfunc; 97248186Smav vm_object_t object; 98126324Sjhb unsigned int npages; 99165272Skmacy vm_offset_t off; 100126324Sjhb 101126324Sjhb /* 102126324Sjhb * Make sure this device can be mapped. 103126324Sjhb */ 104126324Sjhb dev = handle; 105126324Sjhb mapfunc = devsw(dev)->d_mmap; 106126324Sjhb if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) { 107126324Sjhb printf("obsolete map function %p\n", (void *)mapfunc); 108126324Sjhb return (NULL); 109126324Sjhb } 110126324Sjhb 111126324Sjhb /* 112126324Sjhb * Offset should be page aligned. 113126324Sjhb */ 114126324Sjhb if (foff & PAGE_MASK) 115126324Sjhb return (NULL); 116126324Sjhb 117126324Sjhb size = round_page(size); 118126324Sjhb 119165272Skmacy /* 120200447Sattilio * Check that the specified range of the device allows the desired 121126324Sjhb * protection. 122126324Sjhb * 123126324Sjhb * XXX assumes VM_PROT_* == PROT_* 124201879Sattilio */ 125136445Sjhb npages = OFF_TO_IDX(size); 126164325Spjd for (off = foff; npages--; off += PAGE_SIZE) 127126324Sjhb if ((*mapfunc) (dev, off, (int) prot) == -1) 128126324Sjhb return (NULL); 129126324Sjhb 130126324Sjhb /* 131126324Sjhb * Lock to prevent object creation race condition. 132126324Sjhb */ 133131259Sjhb while (dev_pager_alloc_lock) { 134131259Sjhb dev_pager_alloc_lock_want++; 135131259Sjhb tsleep(&dev_pager_alloc_lock, PVM, "dvpall", 0); 136131259Sjhb dev_pager_alloc_lock_want--; 137126324Sjhb } 138126324Sjhb dev_pager_alloc_lock = 1; 139131259Sjhb 140131259Sjhb /* 141227309Sed * Look up pager, creating as necessary. 142227309Sed */ 143131259Sjhb object = vm_pager_object_lookup(&dev_pager_object_list, handle); 144131259Sjhb if (object == NULL) { 145131259Sjhb /* 146177372Sjeff * Allocate object and associate it with the pager. 147177372Sjeff */ 148177372Sjeff object = vm_object_allocate(OBJT_DEVICE, 149131259Sjhb OFF_TO_IDX(foff + size)); 150126324Sjhb object->handle = handle; 151169666Sjeff TAILQ_INIT(&object->un_pager.devp.devp_pglist); 152126324Sjhb TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list); 153126324Sjhb } else { 154126324Sjhb /* 155126324Sjhb * Gain a reference to the object. 156177085Sjeff */ 157165272Skmacy vm_object_reference(object); 158277528Shselasky if (OFF_TO_IDX(foff + size) > object->size) 159169666Sjeff object->size = OFF_TO_IDX(foff + size); 160169666Sjeff } 161169666Sjeff 162169666Sjeff dev_pager_alloc_lock = 0; 163181334Sjhb if (dev_pager_alloc_lock_want) 164169666Sjeff wakeup(&dev_pager_alloc_lock); 165177085Sjeff 166126324Sjhb return (object); 167126324Sjhb} 168235459Srstone 169235459Srstonestatic void 170235459Srstonedev_pager_dealloc(object) 171126324Sjhb vm_object_t object; 172267820Sattilio{ 173267820Sattilio vm_page_t m; 174267820Sattilio 175126324Sjhb TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list); 176267820Sattilio /* 177267820Sattilio * Free up our fake pages. 178267820Sattilio */ 179126324Sjhb while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) != 0) { 180267820Sattilio TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq); 181131259Sjhb dev_pager_putfake(m); 182267820Sattilio } 183126324Sjhb} 184126324Sjhb 185267820Sattiliostatic int 186131259Sjhbdev_pager_getpages(object, m, count, reqpage) 187131259Sjhb vm_object_t object; 188131259Sjhb vm_page_t *m; 189131259Sjhb int count; 190131259Sjhb int reqpage; 191131259Sjhb{ 192131259Sjhb vm_offset_t offset; 193131259Sjhb vm_offset_t paddr; 194267820Sattilio vm_page_t page; 195267820Sattilio dev_t dev; 196267820Sattilio int i, s; 197267820Sattilio d_mmap_t *mapfunc; 198267820Sattilio int prot; 199131259Sjhb 200267820Sattilio dev = object->handle; 201267820Sattilio offset = m[reqpage]->pindex; 202267820Sattilio prot = PROT_READ; /* XXX should pass in? */ 203267820Sattilio mapfunc = devsw(dev)->d_mmap; 204267820Sattilio 205267820Sattilio if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) 206267820Sattilio panic("dev_pager_getpage: no map function"); 207267820Sattilio 208267820Sattilio paddr = pmap_phys_address((*mapfunc) (dev, (vm_offset_t) offset << PAGE_SHIFT, prot)); 209267820Sattilio KASSERT(paddr != -1,("dev_pager_getpage: map function returns error")); 210267820Sattilio /* 211267820Sattilio * Replace the passed in reqpage page with our own fake page and free up the 212267820Sattilio * all of the original pages. 213267820Sattilio */ 214126324Sjhb page = dev_pager_getfake(paddr); 215169666Sjeff TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq); 216169666Sjeff for (i = 0; i < count; i++) { 217169666Sjeff vm_page_free(m[i]); 218169666Sjeff } 219169666Sjeff s = splhigh(); 220169666Sjeff vm_page_insert(page, object, offset); 221169666Sjeff splx(s); 222126324Sjhb 223126324Sjhb return (VM_PAGER_OK); 224126324Sjhb} 225126324Sjhb 226169666Sjeffstatic void 227126324Sjhbdev_pager_putpages(object, m, count, sync, rtvals) 228126324Sjhb vm_object_t object; 229126324Sjhb vm_page_t *m; 230126324Sjhb int count; 231126324Sjhb boolean_t sync; 232169666Sjeff int *rtvals; 233126324Sjhb{ 234126324Sjhb panic("dev_pager_putpage called"); 235126324Sjhb} 236126324Sjhb 237126324Sjhbstatic boolean_t 238126324Sjhbdev_pager_haspage(object, pindex, before, after) 239126324Sjhb vm_object_t object; 240126324Sjhb vm_pindex_t pindex; 241126324Sjhb int *before; 242169666Sjeff int *after; 243126324Sjhb{ 244126324Sjhb if (before != NULL) 245126324Sjhb *before = 0; 246136445Sjhb if (after != NULL) 247136445Sjhb *after = 0; 248136445Sjhb return (TRUE); 249136445Sjhb} 250136445Sjhb 251136445Sjhbstatic vm_page_t 252136445Sjhbdev_pager_getfake(paddr) 253136445Sjhb vm_offset_t paddr; 254136445Sjhb{ 255136445Sjhb vm_page_t m; 256136445Sjhb 257136445Sjhb m = zalloc(fakepg_zone); 258126324Sjhb 259136445Sjhb m->flags = PG_BUSY | PG_FICTITIOUS; 260136445Sjhb m->valid = VM_PAGE_BITS_ALL; 261126324Sjhb m->dirty = 0; 262126324Sjhb m->busy = 0; 263126324Sjhb m->queue = PQ_NONE; 264126324Sjhb m->object = NULL; 265126324Sjhb 266126324Sjhb m->wire_count = 1; 267126324Sjhb m->hold_count = 0; 268126324Sjhb m->phys_addr = paddr; 269126324Sjhb 270136445Sjhb return (m); 271126324Sjhb} 272126324Sjhb 273126324Sjhbstatic void 274126324Sjhbdev_pager_putfake(m) 275126324Sjhb vm_page_t m; 276126324Sjhb{ 277126324Sjhb if (!(m->flags & PG_FICTITIOUS)) 278126324Sjhb panic("dev_pager_putfake: bad page"); 279126324Sjhb zfree(fakepg_zone, m); 280126324Sjhb} 281126324Sjhb