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