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