1 /* 2 * Copyright (c) 1990 University of Utah. 3 * Copyright (c) 1991 The Regents of the University of California. 4 * 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 * %sccs.include.redist.c% 11 * 12 * @(#)vnode_pager.c 7.10 (Berkeley) 05/14/92 13 */ 14 15 /* 16 * Page to/from files (vnodes). 17 * 18 * TODO: 19 * pageouts 20 * fix credential use (uses current process credentials now) 21 */ 22 #include "vnodepager.h" 23 #if NVNODEPAGER > 0 24 25 #include <sys/param.h> 26 #include <sys/systm.h> 27 #include <sys/proc.h> 28 #include <sys/malloc.h> 29 #include <sys/vnode.h> 30 #include <sys/uio.h> 31 #include <sys/mount.h> 32 33 #include <vm/vm.h> 34 #include <vm/vm_page.h> 35 #include <vm/vnode_pager.h> 36 37 queue_head_t vnode_pager_list; /* list of managed vnodes */ 38 39 #ifdef DEBUG 40 int vpagerdebug = 0x00; 41 #define VDB_FOLLOW 0x01 42 #define VDB_INIT 0x02 43 #define VDB_IO 0x04 44 #define VDB_FAIL 0x08 45 #define VDB_ALLOC 0x10 46 #define VDB_SIZE 0x20 47 #endif 48 49 static vm_pager_t vnode_pager_alloc __P((caddr_t, vm_size_t, vm_prot_t)); 50 static void vnode_pager_dealloc __P((vm_pager_t)); 51 static int vnode_pager_getpage 52 __P((vm_pager_t, vm_page_t, boolean_t)); 53 static boolean_t vnode_pager_haspage __P((vm_pager_t, vm_offset_t)); 54 static void vnode_pager_init __P((void)); 55 static int vnode_pager_io 56 __P((vn_pager_t, vm_page_t, enum uio_rw)); 57 static boolean_t vnode_pager_putpage 58 __P((vm_pager_t, vm_page_t, boolean_t)); 59 60 struct pagerops vnodepagerops = { 61 vnode_pager_init, 62 vnode_pager_alloc, 63 vnode_pager_dealloc, 64 vnode_pager_getpage, 65 vnode_pager_putpage, 66 vnode_pager_haspage 67 }; 68 69 static void 70 vnode_pager_init() 71 { 72 #ifdef DEBUG 73 if (vpagerdebug & VDB_FOLLOW) 74 printf("vnode_pager_init()\n"); 75 #endif 76 queue_init(&vnode_pager_list); 77 } 78 79 /* 80 * Allocate (or lookup) pager for a vnode. 81 * Handle is a vnode pointer. 82 */ 83 static vm_pager_t 84 vnode_pager_alloc(handle, size, prot) 85 caddr_t handle; 86 vm_size_t size; 87 vm_prot_t prot; 88 { 89 USES_VOP_GETATTR; 90 register vm_pager_t pager; 91 register vn_pager_t vnp; 92 vm_object_t object; 93 struct vattr vattr; 94 struct vnode *vp; 95 struct proc *p = curproc; /* XXX */ 96 97 #ifdef DEBUG 98 if (vpagerdebug & (VDB_FOLLOW|VDB_ALLOC)) 99 printf("vnode_pager_alloc(%x, %x, %x)\n", handle, size, prot); 100 #endif 101 /* 102 * Pageout to vnode, no can do yet. 103 */ 104 if (handle == NULL) 105 return(NULL); 106 107 /* 108 * Vnodes keep a pointer to any associated pager so no need to 109 * lookup with vm_pager_lookup. 110 */ 111 vp = (struct vnode *)handle; 112 pager = (vm_pager_t)vp->v_vmdata; 113 if (pager == NULL) { 114 /* 115 * Allocate pager structures 116 */ 117 pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 118 if (pager == NULL) 119 return(NULL); 120 vnp = (vn_pager_t)malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK); 121 if (vnp == NULL) { 122 free((caddr_t)pager, M_VMPAGER); 123 return(NULL); 124 } 125 /* 126 * And an object of the appropriate size 127 */ 128 if (VOP_GETATTR(vp, &vattr, p->p_ucred, p) == 0) { 129 object = vm_object_allocate(round_page(vattr.va_size)); 130 vm_object_enter(object, pager); 131 vm_object_setpager(object, pager, 0, TRUE); 132 } else { 133 free((caddr_t)vnp, M_VMPGDATA); 134 free((caddr_t)pager, M_VMPAGER); 135 return(NULL); 136 } 137 /* 138 * Hold a reference to the vnode and initialize pager data. 139 */ 140 VREF(vp); 141 vnp->vnp_flags = 0; 142 vnp->vnp_vp = vp; 143 vnp->vnp_size = vattr.va_size; 144 queue_enter(&vnode_pager_list, pager, vm_pager_t, pg_list); 145 pager->pg_handle = handle; 146 pager->pg_type = PG_VNODE; 147 pager->pg_ops = &vnodepagerops; 148 pager->pg_data = (caddr_t)vnp; 149 vp->v_vmdata = (caddr_t)pager; 150 } else { 151 /* 152 * vm_object_lookup() will remove the object from the 153 * cache if found and also gain a reference to the object. 154 */ 155 object = vm_object_lookup(pager); 156 #ifdef DEBUG 157 vnp = (vn_pager_t)pager->pg_data; 158 #endif 159 } 160 #ifdef DEBUG 161 if (vpagerdebug & VDB_ALLOC) 162 printf("vnode_pager_setup: vp %x sz %x pager %x object %x\n", 163 vp, vnp->vnp_size, pager, object); 164 #endif 165 return(pager); 166 } 167 168 static void 169 vnode_pager_dealloc(pager) 170 vm_pager_t pager; 171 { 172 USES_VOP_FSYNC; 173 register vn_pager_t vnp = (vn_pager_t)pager->pg_data; 174 register struct vnode *vp; 175 struct proc *p = curproc; /* XXX */ 176 177 #ifdef DEBUG 178 if (vpagerdebug & VDB_FOLLOW) 179 printf("vnode_pager_dealloc(%x)\n", pager); 180 #endif 181 if (vp = vnp->vnp_vp) { 182 vp->v_vmdata = NULL; 183 vp->v_flag &= ~VTEXT; 184 #if 0 185 /* can hang if done at reboot on NFS FS */ 186 (void) VOP_FSYNC(vp, p->p_ucred, p); 187 #endif 188 vrele(vp); 189 } 190 queue_remove(&vnode_pager_list, pager, vm_pager_t, pg_list); 191 free((caddr_t)vnp, M_VMPGDATA); 192 free((caddr_t)pager, M_VMPAGER); 193 } 194 195 static int 196 vnode_pager_getpage(pager, m, sync) 197 vm_pager_t pager; 198 vm_page_t m; 199 boolean_t sync; 200 { 201 202 #ifdef DEBUG 203 if (vpagerdebug & VDB_FOLLOW) 204 printf("vnode_pager_getpage(%x, %x)\n", pager, m); 205 #endif 206 return(vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_READ)); 207 } 208 209 static boolean_t 210 vnode_pager_putpage(pager, m, sync) 211 vm_pager_t pager; 212 vm_page_t m; 213 boolean_t sync; 214 { 215 int err; 216 217 #ifdef DEBUG 218 if (vpagerdebug & VDB_FOLLOW) 219 printf("vnode_pager_putpage(%x, %x)\n", pager, m); 220 #endif 221 if (pager == NULL) 222 return; 223 err = vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_WRITE); 224 if (err == VM_PAGER_OK) { 225 m->clean = TRUE; /* XXX - wrong place */ 226 pmap_clear_modify(VM_PAGE_TO_PHYS(m)); /* XXX - wrong place */ 227 } 228 return(err); 229 } 230 231 static boolean_t 232 vnode_pager_haspage(pager, offset) 233 vm_pager_t pager; 234 vm_offset_t offset; 235 { 236 USES_VOP_BMAP; 237 register vn_pager_t vnp = (vn_pager_t)pager->pg_data; 238 daddr_t bn; 239 int err; 240 241 #ifdef DEBUG 242 if (vpagerdebug & VDB_FOLLOW) 243 printf("vnode_pager_haspage(%x, %x)\n", pager, offset); 244 #endif 245 246 /* 247 * Offset beyond end of file, do not have the page 248 */ 249 if (offset >= vnp->vnp_size) { 250 #ifdef DEBUG 251 if (vpagerdebug & (VDB_FAIL|VDB_SIZE)) 252 printf("vnode_pager_haspage: pg %x, off %x, size %x\n", 253 pager, offset, vnp->vnp_size); 254 #endif 255 return(FALSE); 256 } 257 258 /* 259 * Read the index to find the disk block to read 260 * from. If there is no block, report that we don't 261 * have this data. 262 * 263 * Assumes that the vnode has whole page or nothing. 264 */ 265 err = VOP_BMAP(vnp->vnp_vp, 266 offset / vnp->vnp_vp->v_mount->mnt_stat.f_iosize, 267 (struct vnode **)0, &bn); 268 if (err) { 269 #ifdef DEBUG 270 if (vpagerdebug & VDB_FAIL) 271 printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n", 272 err, pager, offset); 273 #endif 274 return(TRUE); 275 } 276 return((long)bn < 0 ? FALSE : TRUE); 277 } 278 279 /* 280 * (XXX) 281 * Lets the VM system know about a change in size for a file. 282 * If this vnode is mapped into some address space (i.e. we have a pager 283 * for it) we adjust our own internal size and flush any cached pages in 284 * the associated object that are affected by the size change. 285 * 286 * Note: this routine may be invoked as a result of a pager put 287 * operation (possibly at object termination time), so we must be careful. 288 */ 289 void 290 vnode_pager_setsize(vp, nsize) 291 struct vnode *vp; 292 u_long nsize; 293 { 294 register vn_pager_t vnp; 295 register vm_object_t object; 296 vm_pager_t pager; 297 298 /* 299 * Not a mapped vnode 300 */ 301 if (vp == NULL || vp->v_type != VREG || vp->v_vmdata == NULL) 302 return; 303 /* 304 * Hasn't changed size 305 */ 306 pager = (vm_pager_t)vp->v_vmdata; 307 vnp = (vn_pager_t)pager->pg_data; 308 if (nsize == vnp->vnp_size) 309 return; 310 /* 311 * No object. 312 * This can happen during object termination since 313 * vm_object_page_clean is called after the object 314 * has been removed from the hash table, and clean 315 * may cause vnode write operations which can wind 316 * up back here. 317 */ 318 object = vm_object_lookup(pager); 319 if (object == NULL) 320 return; 321 322 #ifdef DEBUG 323 if (vpagerdebug & (VDB_FOLLOW|VDB_SIZE)) 324 printf("vnode_pager_setsize: vp %x obj %x osz %d nsz %d\n", 325 vp, object, vnp->vnp_size, nsize); 326 #endif 327 /* 328 * File has shrunk. 329 * Toss any cached pages beyond the new EOF. 330 */ 331 if (nsize < vnp->vnp_size) { 332 vm_object_lock(object); 333 vm_object_page_remove(object, 334 (vm_offset_t)nsize, vnp->vnp_size); 335 vm_object_unlock(object); 336 } 337 vnp->vnp_size = (vm_offset_t)nsize; 338 vm_object_deallocate(object); 339 } 340 341 void 342 vnode_pager_umount(mp) 343 register struct mount *mp; 344 { 345 register vm_pager_t pager, npager; 346 struct vnode *vp; 347 348 pager = (vm_pager_t) queue_first(&vnode_pager_list); 349 while (!queue_end(&vnode_pager_list, (queue_entry_t)pager)) { 350 /* 351 * Save the next pointer now since uncaching may 352 * terminate the object and render pager invalid 353 */ 354 vp = ((vn_pager_t)pager->pg_data)->vnp_vp; 355 npager = (vm_pager_t) queue_next(&pager->pg_list); 356 if (mp == (struct mount *)0 || vp->v_mount == mp) 357 (void) vnode_pager_uncache(vp); 358 pager = npager; 359 } 360 } 361 362 /* 363 * Remove vnode associated object from the object cache. 364 * 365 * Note: this routine may be invoked as a result of a pager put 366 * operation (possibly at object termination time), so we must be careful. 367 */ 368 boolean_t 369 vnode_pager_uncache(vp) 370 register struct vnode *vp; 371 { 372 USES_VOP_ISLOCKED; 373 USES_VOP_LOCK; 374 USES_VOP_UNLOCK; 375 register vm_object_t object; 376 boolean_t uncached, locked; 377 vm_pager_t pager; 378 379 /* 380 * Not a mapped vnode 381 */ 382 pager = (vm_pager_t)vp->v_vmdata; 383 if (pager == NULL) 384 return (TRUE); 385 /* 386 * Unlock the vnode if it is currently locked. 387 * We do this since uncaching the object may result 388 * in its destruction which may initiate paging 389 * activity which may necessitate locking the vnode. 390 */ 391 locked = VOP_ISLOCKED(vp); 392 if (locked) 393 VOP_UNLOCK(vp); 394 /* 395 * Must use vm_object_lookup() as it actually removes 396 * the object from the cache list. 397 */ 398 object = vm_object_lookup(pager); 399 if (object) { 400 uncached = (object->ref_count <= 1); 401 pager_cache(object, FALSE); 402 } else 403 uncached = TRUE; 404 if (locked) 405 VOP_LOCK(vp); 406 return(uncached); 407 } 408 409 static int 410 vnode_pager_io(vnp, m, rw) 411 register vn_pager_t vnp; 412 vm_page_t m; 413 enum uio_rw rw; 414 { 415 USES_VOP_READ; 416 USES_VOP_WRITE; 417 struct uio auio; 418 struct iovec aiov; 419 vm_offset_t kva, foff; 420 int error, size; 421 struct proc *p = curproc; /* XXX */ 422 423 #ifdef DEBUG 424 if (vpagerdebug & VDB_FOLLOW) 425 printf("vnode_pager_io(%x, %x, %c): vnode %x\n", 426 vnp, m, rw == UIO_READ ? 'R' : 'W', vnp->vnp_vp); 427 #endif 428 foff = m->offset + m->object->paging_offset; 429 /* 430 * Return failure if beyond current EOF 431 */ 432 if (foff >= vnp->vnp_size) { 433 #ifdef DEBUG 434 if (vpagerdebug & VDB_SIZE) 435 printf("vnode_pager_io: vp %x, off %d size %d\n", 436 vnp->vnp_vp, foff, vnp->vnp_size); 437 #endif 438 return(VM_PAGER_BAD); 439 } 440 if (foff + PAGE_SIZE > vnp->vnp_size) 441 size = vnp->vnp_size - foff; 442 else 443 size = PAGE_SIZE; 444 /* 445 * Allocate a kernel virtual address and initialize so that 446 * we can use VOP_READ/WRITE routines. 447 */ 448 kva = vm_pager_map_page(m); 449 aiov.iov_base = (caddr_t)kva; 450 aiov.iov_len = size; 451 auio.uio_iov = &aiov; 452 auio.uio_iovcnt = 1; 453 auio.uio_offset = foff; 454 auio.uio_segflg = UIO_SYSSPACE; 455 auio.uio_rw = rw; 456 auio.uio_resid = size; 457 auio.uio_procp = (struct proc *)0; 458 #ifdef DEBUG 459 if (vpagerdebug & VDB_IO) 460 printf("vnode_pager_io: vp %x kva %x foff %x size %x", 461 vnp->vnp_vp, kva, foff, size); 462 #endif 463 if (rw == UIO_READ) 464 error = VOP_READ(vnp->vnp_vp, &auio, 0, p->p_ucred); 465 else 466 error = VOP_WRITE(vnp->vnp_vp, &auio, 0, p->p_ucred); 467 #ifdef DEBUG 468 if (vpagerdebug & VDB_IO) { 469 if (error || auio.uio_resid) 470 printf(" returns error %x, resid %x", 471 error, auio.uio_resid); 472 printf("\n"); 473 } 474 #endif 475 if (!error) { 476 register int count = size - auio.uio_resid; 477 478 if (count == 0) 479 error = EINVAL; 480 else if (count != PAGE_SIZE && rw == UIO_READ) 481 bzero((void *)(kva + count), PAGE_SIZE - count); 482 } 483 vm_pager_unmap_page(kva); 484 return (error ? VM_PAGER_FAIL : VM_PAGER_OK); 485 } 486 #endif 487