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