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