1 /* 2 * Copyright (c) 1990 University of Utah. 3 * Copyright (c) 1991, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * @(#)device_pager.c 8.1 (Berkeley) 6/11/93 39 * $Id$ 40 */ 41 42 /* 43 * Page to/from special files. 44 */ 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/conf.h> 49 #include <sys/mman.h> 50 #include <sys/malloc.h> 51 52 #include <vm/vm.h> 53 #include <vm/vm_kern.h> 54 #include <vm/vm_page.h> 55 #include <vm/device_pager.h> 56 57 struct pagerlst dev_pager_list; /* list of managed devices */ 58 struct pglist dev_pager_fakelist; /* list of available vm_page_t's */ 59 60 #ifdef DEBUG 61 int dpagerdebug = 0; 62 #define DDB_FOLLOW 0x01 63 #define DDB_INIT 0x02 64 #define DDB_ALLOC 0x04 65 #define DDB_FAIL 0x08 66 #endif 67 68 static vm_pager_t dev_pager_alloc 69 __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t)); 70 static void dev_pager_dealloc __P((vm_pager_t)); 71 static int dev_pager_getpage 72 __P((vm_pager_t, vm_page_t, boolean_t)); 73 static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t)); 74 static void dev_pager_init __P((void)); 75 static int dev_pager_putpage 76 __P((vm_pager_t, vm_page_t, boolean_t)); 77 static vm_page_t dev_pager_getfake __P((vm_offset_t)); 78 static void dev_pager_putfake __P((vm_page_t)); 79 80 struct pagerops devicepagerops = { 81 dev_pager_init, 82 dev_pager_alloc, 83 dev_pager_dealloc, 84 dev_pager_getpage, 85 0, 86 dev_pager_putpage, 87 0, 88 dev_pager_haspage 89 }; 90 91 static void 92 dev_pager_init() 93 { 94 #ifdef DEBUG 95 if (dpagerdebug & DDB_FOLLOW) 96 printf("dev_pager_init()\n"); 97 #endif 98 TAILQ_INIT(&dev_pager_list); 99 TAILQ_INIT(&dev_pager_fakelist); 100 } 101 102 static vm_pager_t 103 dev_pager_alloc(handle, size, prot, foff) 104 caddr_t handle; 105 vm_size_t size; 106 vm_prot_t prot; 107 vm_offset_t foff; 108 { 109 dev_t dev; 110 vm_pager_t pager; 111 int (*mapfunc)(); 112 vm_object_t object; 113 dev_pager_t devp; 114 unsigned int npages, off; 115 116 #ifdef DEBUG 117 if (dpagerdebug & DDB_FOLLOW) 118 printf("dev_pager_alloc(%x, %x, %x, %x)\n", 119 handle, size, prot, foff); 120 #endif 121 #ifdef DIAGNOSTIC 122 /* 123 * Pageout to device, should never happen. 124 */ 125 if (handle == NULL) 126 panic("dev_pager_alloc called"); 127 #endif 128 129 /* 130 * Make sure this device can be mapped. 131 */ 132 dev = (dev_t)(u_long)handle; 133 mapfunc = cdevsw[major(dev)].d_mmap; 134 if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 135 return(NULL); 136 137 /* 138 * Offset should be page aligned. 139 */ 140 if (foff & (PAGE_SIZE-1)) 141 return(NULL); 142 143 /* 144 * Check that the specified range of the device allows the 145 * desired protection. 146 * 147 * XXX assumes VM_PROT_* == PROT_* 148 */ 149 npages = atop(round_page(size)); 150 for (off = foff; npages--; off += PAGE_SIZE) 151 if ((*mapfunc)(dev, off, (int)prot) == -1) 152 return(NULL); 153 154 /* 155 * Look up pager, creating as necessary. 156 */ 157 top: 158 pager = vm_pager_lookup(&dev_pager_list, handle); 159 if (pager == NULL) { 160 /* 161 * Allocate and initialize pager structs 162 */ 163 pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 164 if (pager == NULL) 165 return(NULL); 166 devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); 167 if (devp == NULL) { 168 free((caddr_t)pager, M_VMPAGER); 169 return(NULL); 170 } 171 pager->pg_handle = handle; 172 pager->pg_ops = &devicepagerops; 173 pager->pg_type = PG_DEVICE; 174 pager->pg_data = (caddr_t)devp; 175 pager->pg_flags = 0; 176 TAILQ_INIT(&devp->devp_pglist); 177 /* 178 * Allocate object and associate it with the pager. 179 */ 180 object = devp->devp_object = vm_object_allocate(0); 181 vm_object_enter(object, pager); 182 vm_object_setpager(object, pager, (vm_offset_t)foff, FALSE); 183 /* 184 * Finally, put it on the managed list so other can find it. 185 * First we re-lookup in case someone else beat us to this 186 * point (due to blocking in the various mallocs). If so, 187 * we free everything and start over. 188 */ 189 if (vm_pager_lookup(&dev_pager_list, handle)) { 190 free((caddr_t)devp, M_VMPGDATA); 191 free((caddr_t)pager, M_VMPAGER); 192 goto top; 193 } 194 TAILQ_INSERT_TAIL(&dev_pager_list, pager, pg_list); 195 #ifdef DEBUG 196 if (dpagerdebug & DDB_ALLOC) { 197 printf("dev_pager_alloc: pager %x devp %x object %x\n", 198 pager, devp, object); 199 vm_object_print(object, FALSE); 200 } 201 #endif 202 } else { 203 /* 204 * vm_object_lookup() gains a reference and also 205 * removes the object from the cache. 206 */ 207 object = vm_object_lookup(pager); 208 #ifdef DIAGNOSTIC 209 devp = (dev_pager_t)pager->pg_data; 210 if (object != devp->devp_object) 211 panic("dev_pager_setup: bad object"); 212 #endif 213 } 214 return(pager); 215 } 216 217 static void 218 dev_pager_dealloc(pager) 219 vm_pager_t pager; 220 { 221 dev_pager_t devp; 222 vm_object_t object; 223 vm_page_t m; 224 225 #ifdef DEBUG 226 if (dpagerdebug & DDB_FOLLOW) 227 printf("dev_pager_dealloc(%x)\n", pager); 228 #endif 229 TAILQ_REMOVE(&dev_pager_list, pager, pg_list); 230 /* 231 * Get the object. 232 * Note: cannot use vm_object_lookup since object has already 233 * been removed from the hash chain. 234 */ 235 devp = (dev_pager_t)pager->pg_data; 236 object = devp->devp_object; 237 #ifdef DEBUG 238 if (dpagerdebug & DDB_ALLOC) 239 printf("dev_pager_dealloc: devp %x object %x\n", devp, object); 240 #endif 241 /* 242 * Free up our fake pages. 243 */ 244 while (m=devp->devp_pglist.tqh_first) { 245 TAILQ_REMOVE(&devp->devp_pglist, m, pageq); 246 dev_pager_putfake(m); 247 } 248 free((caddr_t)devp, M_VMPGDATA); 249 free((caddr_t)pager, M_VMPAGER); 250 } 251 252 static int 253 dev_pager_getpage(pager, m, sync) 254 vm_pager_t pager; 255 vm_page_t m; 256 boolean_t sync; 257 { 258 register vm_object_t object; 259 vm_offset_t offset, paddr; 260 vm_page_t page; 261 dev_t dev; 262 int s; 263 int (*mapfunc)(), prot; 264 265 #ifdef DEBUG 266 if (dpagerdebug & DDB_FOLLOW) 267 printf("dev_pager_getpage(%x, %x)\n", pager, m); 268 #endif 269 270 object = m->object; 271 dev = (dev_t)(u_long)pager->pg_handle; 272 offset = m->offset + object->paging_offset; 273 prot = PROT_READ; /* XXX should pass in? */ 274 mapfunc = cdevsw[major(dev)].d_mmap; 275 276 if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop) 277 panic("dev_pager_getpage: no map function"); 278 279 paddr = pmap_phys_address((*mapfunc)((dev_t)dev, (int)offset, prot)); 280 #ifdef DIAGNOSTIC 281 if (paddr == -1) 282 panic("dev_pager_getpage: map function returns error"); 283 #endif 284 /* 285 * Replace the passed in page with our own fake page and free 286 * up the original. 287 */ 288 page = dev_pager_getfake(paddr); 289 TAILQ_INSERT_TAIL(&((dev_pager_t)pager->pg_data)->devp_pglist, 290 page, pageq); 291 vm_object_lock(object); 292 vm_page_lock_queues(); 293 vm_page_free(m); 294 vm_page_unlock_queues(); 295 s = splhigh(); 296 vm_page_insert(page, object, offset); 297 splx(s); 298 PAGE_WAKEUP(m); 299 if (offset + PAGE_SIZE > object->size) 300 object->size = offset + PAGE_SIZE; /* XXX anal */ 301 vm_object_unlock(object); 302 303 return(VM_PAGER_OK); 304 } 305 306 static int 307 dev_pager_putpage(pager, m, sync) 308 vm_pager_t pager; 309 vm_page_t m; 310 boolean_t sync; 311 { 312 #ifdef DEBUG 313 if (dpagerdebug & DDB_FOLLOW) 314 printf("dev_pager_putpage(%x, %x)\n", pager, m); 315 #endif 316 if (pager == NULL) 317 return 0; 318 panic("dev_pager_putpage called"); 319 } 320 321 static boolean_t 322 dev_pager_haspage(pager, offset) 323 vm_pager_t pager; 324 vm_offset_t offset; 325 { 326 #ifdef DEBUG 327 if (dpagerdebug & DDB_FOLLOW) 328 printf("dev_pager_haspage(%x, %x)\n", pager, offset); 329 #endif 330 return(TRUE); 331 } 332 333 static vm_page_t 334 dev_pager_getfake(paddr) 335 vm_offset_t paddr; 336 { 337 vm_page_t m; 338 int i; 339 340 if (dev_pager_fakelist.tqh_first == NULL) { 341 m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK); 342 for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) { 343 TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 344 m++; 345 } 346 } 347 m = dev_pager_fakelist.tqh_first; 348 TAILQ_REMOVE(&dev_pager_fakelist, m, pageq); 349 350 m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS; 351 352 m->wire_count = 1; 353 m->phys_addr = paddr; 354 355 return(m); 356 } 357 358 static void 359 dev_pager_putfake(m) 360 vm_page_t m; 361 { 362 #ifdef DIAGNOSTIC 363 if (!(m->flags & PG_FICTITIOUS)) 364 panic("dev_pager_putfake: bad page"); 365 #endif 366 TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq); 367 } 368