vnode_pager.c revision 181020
1251881Speter/*- 2251881Speter * Copyright (c) 1990 University of Utah. 3251881Speter * Copyright (c) 1991 The Regents of the University of California. 4251881Speter * All rights reserved. 5251881Speter * Copyright (c) 1993, 1994 John S. Dyson 6251881Speter * Copyright (c) 1995, David Greenman 7251881Speter * 8251881Speter * This code is derived from software contributed to Berkeley by 9251881Speter * the Systems Programming Group of the University of Utah Computer 10251881Speter * Science Department. 11251881Speter * 12251881Speter * Redistribution and use in source and binary forms, with or without 13251881Speter * modification, are permitted provided that the following conditions 14251881Speter * are met: 15251881Speter * 1. Redistributions of source code must retain the above copyright 16251881Speter * notice, this list of conditions and the following disclaimer. 17251881Speter * 2. Redistributions in binary form must reproduce the above copyright 18251881Speter * notice, this list of conditions and the following disclaimer in the 19251881Speter * documentation and/or other materials provided with the distribution. 20251881Speter * 3. All advertising materials mentioning features or use of this software 21251881Speter * must display the following acknowledgement: 22251881Speter * This product includes software developed by the University of 23251881Speter * California, Berkeley and its contributors. 24251881Speter * 4. Neither the name of the University nor the names of its contributors 25251881Speter * may be used to endorse or promote products derived from this software 26251881Speter * without specific prior written permission. 27251881Speter * 28251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31299742Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32299742Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38251881Speter * SUCH DAMAGE. 39251881Speter * 40251881Speter * from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91 41251881Speter */ 42251881Speter 43251881Speter/* 44251881Speter * Page to/from files (vnodes). 45251881Speter */ 46251881Speter 47251881Speter/* 48299742Sdim * TODO: 49251881Speter * Implement VOP_GETPAGES/PUTPAGES interface for filesystems. Will 50251881Speter * greatly re-simplify the vnode_pager. 51299742Sdim */ 52251881Speter 53251881Speter#include <sys/cdefs.h> 54251881Speter__FBSDID("$FreeBSD: head/sys/vm/vnode_pager.c 181020 2008-07-30 21:18:08Z jhb $"); 55251881Speter 56251881Speter#include <sys/param.h> 57251881Speter#include <sys/systm.h> 58251881Speter#include <sys/proc.h> 59251881Speter#include <sys/vnode.h> 60251881Speter#include <sys/mount.h> 61251881Speter#include <sys/bio.h> 62251881Speter#include <sys/buf.h> 63251881Speter#include <sys/vmmeter.h> 64251881Speter#include <sys/limits.h> 65251881Speter#include <sys/conf.h> 66251881Speter#include <sys/sf_buf.h> 67251881Speter 68251881Speter#include <machine/atomic.h> 69251881Speter 70251881Speter#include <vm/vm.h> 71251881Speter#include <vm/vm_object.h> 72251881Speter#include <vm/vm_page.h> 73251881Speter#include <vm/vm_pager.h> 74251881Speter#include <vm/vm_map.h> 75251881Speter#include <vm/vnode_pager.h> 76251881Speter#include <vm/vm_extern.h> 77251881Speter 78251881Speterstatic int vnode_pager_addr(struct vnode *vp, vm_ooffset_t address, 79251881Speter daddr_t *rtaddress, int *run); 80251881Speterstatic int vnode_pager_input_smlfs(vm_object_t object, vm_page_t m); 81251881Speterstatic int vnode_pager_input_old(vm_object_t object, vm_page_t m); 82251881Speterstatic void vnode_pager_dealloc(vm_object_t); 83251881Speterstatic int vnode_pager_getpages(vm_object_t, vm_page_t *, int, int); 84251881Speterstatic void vnode_pager_putpages(vm_object_t, vm_page_t *, int, boolean_t, int *); 85251881Speterstatic boolean_t vnode_pager_haspage(vm_object_t, vm_pindex_t, int *, int *); 86251881Speterstatic vm_object_t vnode_pager_alloc(void *, vm_ooffset_t, vm_prot_t, vm_ooffset_t); 87251881Speter 88251881Speterstruct pagerops vnodepagerops = { 89251881Speter .pgo_alloc = vnode_pager_alloc, 90251881Speter .pgo_dealloc = vnode_pager_dealloc, 91251881Speter .pgo_getpages = vnode_pager_getpages, 92251881Speter .pgo_putpages = vnode_pager_putpages, 93251881Speter .pgo_haspage = vnode_pager_haspage, 94251881Speter}; 95251881Speter 96251881Speterint vnode_pbuf_freecnt; 97251881Speter 98251881Speter/* Create the VM system backing object for this vnode */ 99251881Speterint 100251881Spetervnode_create_vobject(struct vnode *vp, off_t isize, struct thread *td) 101251881Speter{ 102251881Speter vm_object_t object; 103251881Speter vm_ooffset_t size = isize; 104251881Speter struct vattr va; 105251881Speter 106251881Speter if (!vn_isdisk(vp, NULL) && vn_canvmio(vp) == FALSE) 107251881Speter return (0); 108251881Speter 109251881Speter while ((object = vp->v_object) != NULL) { 110251881Speter VM_OBJECT_LOCK(object); 111251881Speter if (!(object->flags & OBJ_DEAD)) { 112251881Speter VM_OBJECT_UNLOCK(object); 113251881Speter return (0); 114251881Speter } 115251881Speter VOP_UNLOCK(vp, 0); 116251881Speter vm_object_set_flag(object, OBJ_DISCONNECTWNT); 117251881Speter msleep(object, VM_OBJECT_MTX(object), PDROP | PVM, "vodead", 0); 118251881Speter vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 119251881Speter } 120251881Speter 121251881Speter if (size == 0) { 122251881Speter if (vn_isdisk(vp, NULL)) { 123251881Speter size = IDX_TO_OFF(INT_MAX); 124251881Speter } else { 125251881Speter if (VOP_GETATTR(vp, &va, td->td_ucred, td) != 0) 126251881Speter return (0); 127251881Speter size = va.va_size; 128251881Speter } 129251881Speter } 130251881Speter 131251881Speter object = vnode_pager_alloc(vp, size, 0, 0); 132251881Speter /* 133251881Speter * Dereference the reference we just created. This assumes 134251881Speter * that the object is associated with the vp. 135251881Speter */ 136251881Speter VM_OBJECT_LOCK(object); 137251881Speter object->ref_count--; 138251881Speter VM_OBJECT_UNLOCK(object); 139251881Speter vrele(vp); 140251881Speter 141251881Speter KASSERT(vp->v_object != NULL, ("vnode_create_vobject: NULL object")); 142251881Speter 143251881Speter return (0); 144251881Speter} 145251881Speter 146251881Spetervoid 147251881Spetervnode_destroy_vobject(struct vnode *vp) 148251881Speter{ 149251881Speter struct vm_object *obj; 150251881Speter 151251881Speter obj = vp->v_object; 152251881Speter if (obj == NULL) 153251881Speter return; 154251881Speter ASSERT_VOP_ELOCKED(vp, "vnode_destroy_vobject"); 155251881Speter VM_OBJECT_LOCK(obj); 156251881Speter if (obj->ref_count == 0) { 157251881Speter /* 158251881Speter * vclean() may be called twice. The first time 159251881Speter * removes the primary reference to the object, 160251881Speter * the second time goes one further and is a 161251881Speter * special-case to terminate the object. 162251881Speter * 163251881Speter * don't double-terminate the object 164251881Speter */ 165251881Speter if ((obj->flags & OBJ_DEAD) == 0) 166251881Speter vm_object_terminate(obj); 167251881Speter else 168251881Speter VM_OBJECT_UNLOCK(obj); 169251881Speter } else { 170251881Speter /* 171251881Speter * Woe to the process that tries to page now :-). 172251881Speter */ 173251881Speter vm_pager_deallocate(obj); 174251881Speter VM_OBJECT_UNLOCK(obj); 175251881Speter } 176251881Speter vp->v_object = NULL; 177251881Speter} 178251881Speter 179251881Speter 180251881Speter/* 181251881Speter * Allocate (or lookup) pager for a vnode. 182251881Speter * Handle is a vnode pointer. 183251881Speter * 184251881Speter * MPSAFE 185251881Speter */ 186251881Spetervm_object_t 187251881Spetervnode_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 188251881Speter vm_ooffset_t offset) 189251881Speter{ 190251881Speter vm_object_t object; 191251881Speter struct vnode *vp; 192251881Speter 193251881Speter /* 194251881Speter * Pageout to vnode, no can do yet. 195251881Speter */ 196251881Speter if (handle == NULL) 197251881Speter return (NULL); 198251881Speter 199251881Speter vp = (struct vnode *) handle; 200251881Speter 201251881Speter /* 202251881Speter * If the object is being terminated, wait for it to 203251881Speter * go away. 204251881Speter */ 205251881Speterretry: 206251881Speter while ((object = vp->v_object) != NULL) { 207251881Speter VM_OBJECT_LOCK(object); 208251881Speter if ((object->flags & OBJ_DEAD) == 0) 209251881Speter break; 210251881Speter vm_object_set_flag(object, OBJ_DISCONNECTWNT); 211251881Speter msleep(object, VM_OBJECT_MTX(object), PDROP | PVM, "vadead", 0); 212251881Speter } 213251881Speter 214251881Speter if (vp->v_usecount == 0) 215251881Speter panic("vnode_pager_alloc: no vnode reference"); 216251881Speter 217251881Speter if (object == NULL) { 218251881Speter /* 219251881Speter * Add an object of the appropriate size 220251881Speter */ 221251881Speter object = vm_object_allocate(OBJT_VNODE, OFF_TO_IDX(round_page(size))); 222251881Speter 223251881Speter object->un_pager.vnp.vnp_size = size; 224251881Speter 225251881Speter object->handle = handle; 226251881Speter if (VFS_NEEDSGIANT(vp->v_mount)) 227251881Speter vm_object_set_flag(object, OBJ_NEEDGIANT); 228251881Speter VI_LOCK(vp); 229251881Speter if (vp->v_object != NULL) { 230251881Speter /* 231251881Speter * Object has been created while we were sleeping 232251881Speter */ 233251881Speter VI_UNLOCK(vp); 234251881Speter vm_object_destroy(object); 235251881Speter goto retry; 236251881Speter } 237251881Speter vp->v_object = object; 238251881Speter VI_UNLOCK(vp); 239251881Speter } else { 240251881Speter object->ref_count++; 241251881Speter VM_OBJECT_UNLOCK(object); 242251881Speter } 243251881Speter vref(vp); 244251881Speter return (object); 245251881Speter} 246251881Speter 247251881Speter/* 248251881Speter * The object must be locked. 249251881Speter */ 250251881Speterstatic void 251251881Spetervnode_pager_dealloc(object) 252251881Speter vm_object_t object; 253251881Speter{ 254251881Speter struct vnode *vp = object->handle; 255251881Speter 256251881Speter if (vp == NULL) 257251881Speter panic("vnode_pager_dealloc: pager already dealloced"); 258251881Speter 259251881Speter VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 260251881Speter vm_object_pip_wait(object, "vnpdea"); 261251881Speter 262251881Speter object->handle = NULL; 263251881Speter object->type = OBJT_DEAD; 264251881Speter if (object->flags & OBJ_DISCONNECTWNT) { 265251881Speter vm_object_clear_flag(object, OBJ_DISCONNECTWNT); 266251881Speter wakeup(object); 267251881Speter } 268251881Speter ASSERT_VOP_ELOCKED(vp, "vnode_pager_dealloc"); 269251881Speter vp->v_object = NULL; 270251881Speter vp->v_vflag &= ~VV_TEXT; 271251881Speter} 272251881Speter 273251881Speterstatic boolean_t 274251881Spetervnode_pager_haspage(object, pindex, before, after) 275251881Speter vm_object_t object; 276251881Speter vm_pindex_t pindex; 277251881Speter int *before; 278251881Speter int *after; 279251881Speter{ 280251881Speter struct vnode *vp = object->handle; 281251881Speter daddr_t bn; 282251881Speter int err; 283251881Speter daddr_t reqblock; 284251881Speter int poff; 285251881Speter int bsize; 286251881Speter int pagesperblock, blocksperpage; 287251881Speter int vfslocked; 288251881Speter 289251881Speter VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 290251881Speter /* 291251881Speter * If no vp or vp is doomed or marked transparent to VM, we do not 292251881Speter * have the page. 293251881Speter */ 294251881Speter if (vp == NULL || vp->v_iflag & VI_DOOMED) 295251881Speter return FALSE; 296251881Speter /* 297251881Speter * If the offset is beyond end of file we do 298251881Speter * not have the page. 299251881Speter */ 300251881Speter if (IDX_TO_OFF(pindex) >= object->un_pager.vnp.vnp_size) 301251881Speter return FALSE; 302251881Speter 303251881Speter bsize = vp->v_mount->mnt_stat.f_iosize; 304251881Speter pagesperblock = bsize / PAGE_SIZE; 305251881Speter blocksperpage = 0; 306251881Speter if (pagesperblock > 0) { 307251881Speter reqblock = pindex / pagesperblock; 308251881Speter } else { 309251881Speter blocksperpage = (PAGE_SIZE / bsize); 310251881Speter reqblock = pindex * blocksperpage; 311251881Speter } 312251881Speter VM_OBJECT_UNLOCK(object); 313251881Speter vfslocked = VFS_LOCK_GIANT(vp->v_mount); 314251881Speter err = VOP_BMAP(vp, reqblock, NULL, &bn, after, before); 315251881Speter VFS_UNLOCK_GIANT(vfslocked); 316251881Speter VM_OBJECT_LOCK(object); 317251881Speter if (err) 318251881Speter return TRUE; 319251881Speter if (bn == -1) 320251881Speter return FALSE; 321251881Speter if (pagesperblock > 0) { 322251881Speter poff = pindex - (reqblock * pagesperblock); 323251881Speter if (before) { 324251881Speter *before *= pagesperblock; 325251881Speter *before += poff; 326251881Speter } 327251881Speter if (after) { 328251881Speter int numafter; 329251881Speter *after *= pagesperblock; 330251881Speter numafter = pagesperblock - (poff + 1); 331251881Speter if (IDX_TO_OFF(pindex + numafter) > 332251881Speter object->un_pager.vnp.vnp_size) { 333251881Speter numafter = 334251881Speter OFF_TO_IDX(object->un_pager.vnp.vnp_size) - 335251881Speter pindex; 336251881Speter } 337251881Speter *after += numafter; 338251881Speter } 339251881Speter } else { 340251881Speter if (before) { 341251881Speter *before /= blocksperpage; 342251881Speter } 343251881Speter 344251881Speter if (after) { 345251881Speter *after /= blocksperpage; 346251881Speter } 347251881Speter } 348251881Speter return TRUE; 349251881Speter} 350251881Speter 351251881Speter/* 352251881Speter * Lets the VM system know about a change in size for a file. 353251881Speter * We adjust our own internal size and flush any cached pages in 354251881Speter * the associated object that are affected by the size change. 355251881Speter * 356251881Speter * Note: this routine may be invoked as a result of a pager put 357251881Speter * operation (possibly at object termination time), so we must be careful. 358251881Speter */ 359251881Spetervoid 360251881Spetervnode_pager_setsize(vp, nsize) 361251881Speter struct vnode *vp; 362251881Speter vm_ooffset_t nsize; 363251881Speter{ 364251881Speter vm_object_t object; 365251881Speter vm_page_t m; 366251881Speter vm_pindex_t nobjsize; 367251881Speter 368251881Speter if ((object = vp->v_object) == NULL) 369251881Speter return; 370251881Speter VM_OBJECT_LOCK(object); 371251881Speter if (nsize == object->un_pager.vnp.vnp_size) { 372251881Speter /* 373251881Speter * Hasn't changed size 374251881Speter */ 375251881Speter VM_OBJECT_UNLOCK(object); 376251881Speter return; 377251881Speter } 378251881Speter nobjsize = OFF_TO_IDX(nsize + PAGE_MASK); 379251881Speter if (nsize < object->un_pager.vnp.vnp_size) { 380251881Speter /* 381251881Speter * File has shrunk. Toss any cached pages beyond the new EOF. 382251881Speter */ 383251881Speter if (nobjsize < object->size) 384251881Speter vm_object_page_remove(object, nobjsize, object->size, 385251881Speter FALSE); 386251881Speter /* 387251881Speter * this gets rid of garbage at the end of a page that is now 388251881Speter * only partially backed by the vnode. 389251881Speter * 390251881Speter * XXX for some reason (I don't know yet), if we take a 391251881Speter * completely invalid page and mark it partially valid 392251881Speter * it can screw up NFS reads, so we don't allow the case. 393251881Speter */ 394251881Speter if ((nsize & PAGE_MASK) && 395251881Speter (m = vm_page_lookup(object, OFF_TO_IDX(nsize))) != NULL && 396251881Speter m->valid != 0) { 397251881Speter int base = (int)nsize & PAGE_MASK; 398251881Speter int size = PAGE_SIZE - base; 399251881Speter 400251881Speter /* 401251881Speter * Clear out partial-page garbage in case 402251881Speter * the page has been mapped. 403251881Speter */ 404251881Speter pmap_zero_page_area(m, base, size); 405251881Speter 406251881Speter /* 407251881Speter * Clear out partial-page dirty bits. This 408251881Speter * has the side effect of setting the valid 409251881Speter * bits, but that is ok. There are a bunch 410251881Speter * of places in the VM system where we expected 411251881Speter * m->dirty == VM_PAGE_BITS_ALL. The file EOF 412251881Speter * case is one of them. If the page is still 413251881Speter * partially dirty, make it fully dirty. 414251881Speter * 415251881Speter * note that we do not clear out the valid 416251881Speter * bits. This would prevent bogus_page 417251881Speter * replacement from working properly. 418251881Speter */ 419251881Speter vm_page_lock_queues(); 420251881Speter vm_page_set_validclean(m, base, size); 421251881Speter if (m->dirty != 0) 422251881Speter m->dirty = VM_PAGE_BITS_ALL; 423251881Speter vm_page_unlock_queues(); 424251881Speter } else if ((nsize & PAGE_MASK) && 425251881Speter __predict_false(object->cache != NULL)) { 426251881Speter vm_page_cache_free(object, OFF_TO_IDX(nsize), 427251881Speter nobjsize); 428251881Speter } 429251881Speter } 430251881Speter object->un_pager.vnp.vnp_size = nsize; 431251881Speter object->size = nobjsize; 432251881Speter VM_OBJECT_UNLOCK(object); 433251881Speter} 434251881Speter 435251881Speter/* 436251881Speter * calculate the linear (byte) disk address of specified virtual 437251881Speter * file address 438251881Speter */ 439251881Speterstatic int 440251881Spetervnode_pager_addr(struct vnode *vp, vm_ooffset_t address, daddr_t *rtaddress, 441251881Speter int *run) 442251881Speter{ 443251881Speter int bsize; 444251881Speter int err; 445251881Speter daddr_t vblock; 446251881Speter daddr_t voffset; 447251881Speter 448251881Speter if (address < 0) 449251881Speter return -1; 450251881Speter 451251881Speter if (vp->v_iflag & VI_DOOMED) 452251881Speter return -1; 453251881Speter 454251881Speter bsize = vp->v_mount->mnt_stat.f_iosize; 455251881Speter vblock = address / bsize; 456251881Speter voffset = address % bsize; 457251881Speter 458251881Speter err = VOP_BMAP(vp, vblock, NULL, rtaddress, run, NULL); 459251881Speter if (err == 0) { 460251881Speter if (*rtaddress != -1) 461251881Speter *rtaddress += voffset / DEV_BSIZE; 462251881Speter if (run) { 463251881Speter *run += 1; 464251881Speter *run *= bsize/PAGE_SIZE; 465251881Speter *run -= voffset/PAGE_SIZE; 466251881Speter } 467251881Speter } 468251881Speter 469251881Speter return (err); 470251881Speter} 471251881Speter 472251881Speter/* 473251881Speter * small block filesystem vnode pager input 474251881Speter */ 475251881Speterstatic int 476251881Spetervnode_pager_input_smlfs(object, m) 477251881Speter vm_object_t object; 478251881Speter vm_page_t m; 479251881Speter{ 480251881Speter int i; 481251881Speter struct vnode *vp; 482251881Speter struct bufobj *bo; 483251881Speter struct buf *bp; 484251881Speter struct sf_buf *sf; 485251881Speter daddr_t fileaddr; 486251881Speter vm_offset_t bsize; 487251881Speter int error = 0; 488251881Speter 489251881Speter vp = object->handle; 490251881Speter if (vp->v_iflag & VI_DOOMED) 491251881Speter return VM_PAGER_BAD; 492251881Speter 493251881Speter bsize = vp->v_mount->mnt_stat.f_iosize; 494251881Speter 495251881Speter VOP_BMAP(vp, 0, &bo, 0, NULL, NULL); 496251881Speter 497251881Speter sf = sf_buf_alloc(m, 0); 498251881Speter 499251881Speter for (i = 0; i < PAGE_SIZE / bsize; i++) { 500251881Speter vm_ooffset_t address; 501251881Speter 502251881Speter if (vm_page_bits(i * bsize, bsize) & m->valid) 503251881Speter continue; 504251881Speter 505251881Speter address = IDX_TO_OFF(m->pindex) + i * bsize; 506251881Speter if (address >= object->un_pager.vnp.vnp_size) { 507251881Speter fileaddr = -1; 508251881Speter } else { 509251881Speter error = vnode_pager_addr(vp, address, &fileaddr, NULL); 510251881Speter if (error) 511251881Speter break; 512251881Speter } 513251881Speter if (fileaddr != -1) { 514251881Speter bp = getpbuf(&vnode_pbuf_freecnt); 515251881Speter 516251881Speter /* build a minimal buffer header */ 517251881Speter bp->b_iocmd = BIO_READ; 518251881Speter bp->b_iodone = bdone; 519251881Speter KASSERT(bp->b_rcred == NOCRED, ("leaking read ucred")); 520251881Speter KASSERT(bp->b_wcred == NOCRED, ("leaking write ucred")); 521251881Speter bp->b_rcred = crhold(curthread->td_ucred); 522251881Speter bp->b_wcred = crhold(curthread->td_ucred); 523251881Speter bp->b_data = (caddr_t)sf_buf_kva(sf) + i * bsize; 524251881Speter bp->b_blkno = fileaddr; 525251881Speter pbgetbo(bo, bp); 526251881Speter bp->b_bcount = bsize; 527251881Speter bp->b_bufsize = bsize; 528251881Speter bp->b_runningbufspace = bp->b_bufsize; 529251881Speter atomic_add_int(&runningbufspace, bp->b_runningbufspace); 530251881Speter 531251881Speter /* do the input */ 532251881Speter bp->b_iooffset = dbtob(bp->b_blkno); 533251881Speter bstrategy(bp); 534251881Speter 535251881Speter bwait(bp, PVM, "vnsrd"); 536251881Speter 537251881Speter if ((bp->b_ioflags & BIO_ERROR) != 0) 538251881Speter error = EIO; 539251881Speter 540251881Speter /* 541251881Speter * free the buffer header back to the swap buffer pool 542251881Speter */ 543251881Speter pbrelbo(bp); 544251881Speter relpbuf(bp, &vnode_pbuf_freecnt); 545251881Speter if (error) 546251881Speter break; 547251881Speter 548251881Speter VM_OBJECT_LOCK(object); 549251881Speter vm_page_lock_queues(); 550251881Speter vm_page_set_validclean(m, (i * bsize) & PAGE_MASK, bsize); 551251881Speter vm_page_unlock_queues(); 552251881Speter VM_OBJECT_UNLOCK(object); 553251881Speter } else { 554251881Speter VM_OBJECT_LOCK(object); 555251881Speter vm_page_lock_queues(); 556251881Speter vm_page_set_validclean(m, (i * bsize) & PAGE_MASK, bsize); 557251881Speter vm_page_unlock_queues(); 558251881Speter VM_OBJECT_UNLOCK(object); 559251881Speter bzero((caddr_t)sf_buf_kva(sf) + i * bsize, bsize); 560251881Speter } 561251881Speter } 562251881Speter sf_buf_free(sf); 563251881Speter vm_page_lock_queues(); 564251881Speter pmap_clear_modify(m); 565251881Speter vm_page_unlock_queues(); 566251881Speter if (error) { 567251881Speter return VM_PAGER_ERROR; 568251881Speter } 569251881Speter return VM_PAGER_OK; 570251881Speter 571251881Speter} 572251881Speter 573251881Speter 574251881Speter/* 575251881Speter * old style vnode pager input routine 576251881Speter */ 577251881Speterstatic int 578251881Spetervnode_pager_input_old(object, m) 579251881Speter vm_object_t object; 580251881Speter vm_page_t m; 581251881Speter{ 582251881Speter struct uio auio; 583251881Speter struct iovec aiov; 584251881Speter int error; 585251881Speter int size; 586251881Speter struct sf_buf *sf; 587251881Speter struct vnode *vp; 588251881Speter 589251881Speter VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 590251881Speter error = 0; 591251881Speter 592251881Speter /* 593251881Speter * Return failure if beyond current EOF 594251881Speter */ 595251881Speter if (IDX_TO_OFF(m->pindex) >= object->un_pager.vnp.vnp_size) { 596251881Speter return VM_PAGER_BAD; 597251881Speter } else { 598251881Speter size = PAGE_SIZE; 599251881Speter if (IDX_TO_OFF(m->pindex) + size > object->un_pager.vnp.vnp_size) 600251881Speter size = object->un_pager.vnp.vnp_size - IDX_TO_OFF(m->pindex); 601251881Speter vp = object->handle; 602251881Speter VM_OBJECT_UNLOCK(object); 603251881Speter 604251881Speter /* 605251881Speter * Allocate a kernel virtual address and initialize so that 606251881Speter * we can use VOP_READ/WRITE routines. 607251881Speter */ 608251881Speter sf = sf_buf_alloc(m, 0); 609251881Speter 610251881Speter aiov.iov_base = (caddr_t)sf_buf_kva(sf); 611251881Speter aiov.iov_len = size; 612251881Speter auio.uio_iov = &aiov; 613251881Speter auio.uio_iovcnt = 1; 614251881Speter auio.uio_offset = IDX_TO_OFF(m->pindex); 615251881Speter auio.uio_segflg = UIO_SYSSPACE; 616251881Speter auio.uio_rw = UIO_READ; 617251881Speter auio.uio_resid = size; 618251881Speter auio.uio_td = curthread; 619251881Speter 620251881Speter error = VOP_READ(vp, &auio, 0, curthread->td_ucred); 621251881Speter if (!error) { 622251881Speter int count = size - auio.uio_resid; 623251881Speter 624251881Speter if (count == 0) 625251881Speter error = EINVAL; 626251881Speter else if (count != PAGE_SIZE) 627251881Speter bzero((caddr_t)sf_buf_kva(sf) + count, 628251881Speter PAGE_SIZE - count); 629251881Speter } 630251881Speter sf_buf_free(sf); 631251881Speter 632251881Speter VM_OBJECT_LOCK(object); 633251881Speter } 634251881Speter vm_page_lock_queues(); 635251881Speter pmap_clear_modify(m); 636251881Speter vm_page_undirty(m); 637251881Speter vm_page_unlock_queues(); 638251881Speter if (!error) 639251881Speter m->valid = VM_PAGE_BITS_ALL; 640251881Speter return error ? VM_PAGER_ERROR : VM_PAGER_OK; 641251881Speter} 642251881Speter 643251881Speter/* 644251881Speter * generic vnode pager input routine 645251881Speter */ 646251881Speter 647251881Speter/* 648251881Speter * Local media VFS's that do not implement their own VOP_GETPAGES 649251881Speter * should have their VOP_GETPAGES call to vnode_pager_generic_getpages() 650251881Speter * to implement the previous behaviour. 651251881Speter * 652251881Speter * All other FS's should use the bypass to get to the local media 653251881Speter * backing vp's VOP_GETPAGES. 654251881Speter */ 655251881Speterstatic int 656251881Spetervnode_pager_getpages(object, m, count, reqpage) 657251881Speter vm_object_t object; 658251881Speter vm_page_t *m; 659251881Speter int count; 660251881Speter int reqpage; 661251881Speter{ 662251881Speter int rtval; 663251881Speter struct vnode *vp; 664251881Speter int bytes = count * PAGE_SIZE; 665251881Speter int vfslocked; 666251881Speter 667251881Speter vp = object->handle; 668251881Speter VM_OBJECT_UNLOCK(object); 669251881Speter vfslocked = VFS_LOCK_GIANT(vp->v_mount); 670251881Speter rtval = VOP_GETPAGES(vp, m, bytes, reqpage, 0); 671251881Speter KASSERT(rtval != EOPNOTSUPP, 672251881Speter ("vnode_pager: FS getpages not implemented\n")); 673251881Speter VFS_UNLOCK_GIANT(vfslocked); 674251881Speter VM_OBJECT_LOCK(object); 675251881Speter return rtval; 676251881Speter} 677251881Speter 678251881Speter/* 679251881Speter * This is now called from local media FS's to operate against their 680251881Speter * own vnodes if they fail to implement VOP_GETPAGES. 681251881Speter */ 682251881Speterint 683251881Spetervnode_pager_generic_getpages(vp, m, bytecount, reqpage) 684251881Speter struct vnode *vp; 685251881Speter vm_page_t *m; 686251881Speter int bytecount; 687251881Speter int reqpage; 688251881Speter{ 689251881Speter vm_object_t object; 690251881Speter vm_offset_t kva; 691251881Speter off_t foff, tfoff, nextoff; 692251881Speter int i, j, size, bsize, first; 693251881Speter daddr_t firstaddr, reqblock; 694251881Speter struct bufobj *bo; 695251881Speter int runpg; 696251881Speter int runend; 697251881Speter struct buf *bp; 698251881Speter int count; 699251881Speter int error; 700251881Speter 701251881Speter object = vp->v_object; 702251881Speter count = bytecount / PAGE_SIZE; 703251881Speter 704251881Speter KASSERT(vp->v_type != VCHR && vp->v_type != VBLK, 705251881Speter ("vnode_pager_generic_getpages does not support devices")); 706251881Speter if (vp->v_iflag & VI_DOOMED) 707251881Speter return VM_PAGER_BAD; 708251881Speter 709251881Speter bsize = vp->v_mount->mnt_stat.f_iosize; 710251881Speter 711251881Speter /* get the UNDERLYING device for the file with VOP_BMAP() */ 712251881Speter 713251881Speter /* 714251881Speter * originally, we did not check for an error return value -- assuming 715251881Speter * an fs always has a bmap entry point -- that assumption is wrong!!! 716251881Speter */ 717251881Speter foff = IDX_TO_OFF(m[reqpage]->pindex); 718251881Speter 719251881Speter /* 720251881Speter * if we can't bmap, use old VOP code 721251881Speter */ 722251881Speter error = VOP_BMAP(vp, foff / bsize, &bo, &reqblock, NULL, NULL); 723251881Speter if (error == EOPNOTSUPP) { 724251881Speter VM_OBJECT_LOCK(object); 725251881Speter vm_page_lock_queues(); 726251881Speter for (i = 0; i < count; i++) 727251881Speter if (i != reqpage) 728251881Speter vm_page_free(m[i]); 729251881Speter vm_page_unlock_queues(); 730251881Speter PCPU_INC(cnt.v_vnodein); 731251881Speter PCPU_INC(cnt.v_vnodepgsin); 732251881Speter error = vnode_pager_input_old(object, m[reqpage]); 733251881Speter VM_OBJECT_UNLOCK(object); 734251881Speter return (error); 735251881Speter } else if (error != 0) { 736251881Speter VM_OBJECT_LOCK(object); 737251881Speter vm_page_lock_queues(); 738251881Speter for (i = 0; i < count; i++) 739251881Speter if (i != reqpage) 740251881Speter vm_page_free(m[i]); 741251881Speter vm_page_unlock_queues(); 742251881Speter VM_OBJECT_UNLOCK(object); 743251881Speter return (VM_PAGER_ERROR); 744251881Speter 745251881Speter /* 746251881Speter * if the blocksize is smaller than a page size, then use 747251881Speter * special small filesystem code. NFS sometimes has a small 748251881Speter * blocksize, but it can handle large reads itself. 749251881Speter */ 750251881Speter } else if ((PAGE_SIZE / bsize) > 1 && 751251881Speter (vp->v_mount->mnt_stat.f_type != nfs_mount_type)) { 752251881Speter VM_OBJECT_LOCK(object); 753251881Speter vm_page_lock_queues(); 754251881Speter for (i = 0; i < count; i++) 755251881Speter if (i != reqpage) 756251881Speter vm_page_free(m[i]); 757251881Speter vm_page_unlock_queues(); 758251881Speter VM_OBJECT_UNLOCK(object); 759251881Speter PCPU_INC(cnt.v_vnodein); 760251881Speter PCPU_INC(cnt.v_vnodepgsin); 761251881Speter return vnode_pager_input_smlfs(object, m[reqpage]); 762251881Speter } 763251881Speter 764251881Speter /* 765251881Speter * If we have a completely valid page available to us, we can 766251881Speter * clean up and return. Otherwise we have to re-read the 767251881Speter * media. 768251881Speter */ 769251881Speter VM_OBJECT_LOCK(object); 770251881Speter if (m[reqpage]->valid == VM_PAGE_BITS_ALL) { 771251881Speter vm_page_lock_queues(); 772251881Speter for (i = 0; i < count; i++) 773251881Speter if (i != reqpage) 774251881Speter vm_page_free(m[i]); 775251881Speter vm_page_unlock_queues(); 776251881Speter VM_OBJECT_UNLOCK(object); 777251881Speter return VM_PAGER_OK; 778251881Speter } else if (reqblock == -1) { 779251881Speter pmap_zero_page(m[reqpage]); 780251881Speter vm_page_undirty(m[reqpage]); 781251881Speter m[reqpage]->valid = VM_PAGE_BITS_ALL; 782251881Speter vm_page_lock_queues(); 783251881Speter for (i = 0; i < count; i++) 784251881Speter if (i != reqpage) 785251881Speter vm_page_free(m[i]); 786251881Speter vm_page_unlock_queues(); 787251881Speter VM_OBJECT_UNLOCK(object); 788251881Speter return (VM_PAGER_OK); 789251881Speter } 790251881Speter m[reqpage]->valid = 0; 791251881Speter VM_OBJECT_UNLOCK(object); 792251881Speter 793251881Speter /* 794251881Speter * here on direct device I/O 795251881Speter */ 796251881Speter firstaddr = -1; 797251881Speter 798251881Speter /* 799251881Speter * calculate the run that includes the required page 800251881Speter */ 801251881Speter for (first = 0, i = 0; i < count; i = runend) { 802251881Speter if (vnode_pager_addr(vp, IDX_TO_OFF(m[i]->pindex), &firstaddr, 803251881Speter &runpg) != 0) { 804251881Speter VM_OBJECT_LOCK(object); 805251881Speter vm_page_lock_queues(); 806251881Speter for (; i < count; i++) 807251881Speter if (i != reqpage) 808251881Speter vm_page_free(m[i]); 809251881Speter vm_page_unlock_queues(); 810251881Speter VM_OBJECT_UNLOCK(object); 811251881Speter return (VM_PAGER_ERROR); 812251881Speter } 813251881Speter if (firstaddr == -1) { 814251881Speter VM_OBJECT_LOCK(object); 815251881Speter if (i == reqpage && foff < object->un_pager.vnp.vnp_size) { 816251881Speter panic("vnode_pager_getpages: unexpected missing page: firstaddr: %jd, foff: 0x%jx%08jx, vnp_size: 0x%jx%08jx", 817251881Speter (intmax_t)firstaddr, (uintmax_t)(foff >> 32), 818251881Speter (uintmax_t)foff, 819251881Speter (uintmax_t) 820251881Speter (object->un_pager.vnp.vnp_size >> 32), 821251881Speter (uintmax_t)object->un_pager.vnp.vnp_size); 822251881Speter } 823251881Speter vm_page_lock_queues(); 824251881Speter vm_page_free(m[i]); 825251881Speter vm_page_unlock_queues(); 826251881Speter VM_OBJECT_UNLOCK(object); 827251881Speter runend = i + 1; 828251881Speter first = runend; 829251881Speter continue; 830251881Speter } 831251881Speter runend = i + runpg; 832251881Speter if (runend <= reqpage) { 833251881Speter VM_OBJECT_LOCK(object); 834251881Speter vm_page_lock_queues(); 835251881Speter for (j = i; j < runend; j++) 836251881Speter vm_page_free(m[j]); 837251881Speter vm_page_unlock_queues(); 838251881Speter VM_OBJECT_UNLOCK(object); 839251881Speter } else { 840251881Speter if (runpg < (count - first)) { 841251881Speter VM_OBJECT_LOCK(object); 842251881Speter vm_page_lock_queues(); 843251881Speter for (i = first + runpg; i < count; i++) 844251881Speter vm_page_free(m[i]); 845251881Speter vm_page_unlock_queues(); 846251881Speter VM_OBJECT_UNLOCK(object); 847251881Speter count = first + runpg; 848251881Speter } 849251881Speter break; 850251881Speter } 851251881Speter first = runend; 852251881Speter } 853251881Speter 854251881Speter /* 855251881Speter * the first and last page have been calculated now, move input pages 856251881Speter * to be zero based... 857251881Speter */ 858251881Speter if (first != 0) { 859251881Speter m += first; 860251881Speter count -= first; 861251881Speter reqpage -= first; 862251881Speter } 863251881Speter 864251881Speter /* 865251881Speter * calculate the file virtual address for the transfer 866251881Speter */ 867251881Speter foff = IDX_TO_OFF(m[0]->pindex); 868251881Speter 869251881Speter /* 870251881Speter * calculate the size of the transfer 871251881Speter */ 872251881Speter size = count * PAGE_SIZE; 873251881Speter KASSERT(count > 0, ("zero count")); 874251881Speter if ((foff + size) > object->un_pager.vnp.vnp_size) 875251881Speter size = object->un_pager.vnp.vnp_size - foff; 876251881Speter KASSERT(size > 0, ("zero size")); 877251881Speter 878251881Speter /* 879251881Speter * round up physical size for real devices. 880251881Speter */ 881251881Speter if (1) { 882251881Speter int secmask = bo->bo_bsize - 1; 883251881Speter KASSERT(secmask < PAGE_SIZE && secmask > 0, 884251881Speter ("vnode_pager_generic_getpages: sector size %d too large", 885251881Speter secmask + 1)); 886251881Speter size = (size + secmask) & ~secmask; 887251881Speter } 888251881Speter 889251881Speter bp = getpbuf(&vnode_pbuf_freecnt); 890251881Speter kva = (vm_offset_t) bp->b_data; 891251881Speter 892251881Speter /* 893251881Speter * and map the pages to be read into the kva 894251881Speter */ 895251881Speter pmap_qenter(kva, m, count); 896251881Speter 897251881Speter /* build a minimal buffer header */ 898251881Speter bp->b_iocmd = BIO_READ; 899251881Speter bp->b_iodone = bdone; 900251881Speter KASSERT(bp->b_rcred == NOCRED, ("leaking read ucred")); 901251881Speter KASSERT(bp->b_wcred == NOCRED, ("leaking write ucred")); 902251881Speter bp->b_rcred = crhold(curthread->td_ucred); 903251881Speter bp->b_wcred = crhold(curthread->td_ucred); 904251881Speter bp->b_blkno = firstaddr; 905251881Speter pbgetbo(bo, bp); 906251881Speter bp->b_bcount = size; 907251881Speter bp->b_bufsize = size; 908251881Speter bp->b_runningbufspace = bp->b_bufsize; 909251881Speter atomic_add_int(&runningbufspace, bp->b_runningbufspace); 910251881Speter 911251881Speter PCPU_INC(cnt.v_vnodein); 912251881Speter PCPU_ADD(cnt.v_vnodepgsin, count); 913251881Speter 914251881Speter /* do the input */ 915251881Speter bp->b_iooffset = dbtob(bp->b_blkno); 916251881Speter bstrategy(bp); 917251881Speter 918251881Speter bwait(bp, PVM, "vnread"); 919251881Speter 920251881Speter if ((bp->b_ioflags & BIO_ERROR) != 0) 921251881Speter error = EIO; 922251881Speter 923251881Speter if (!error) { 924251881Speter if (size != count * PAGE_SIZE) 925251881Speter bzero((caddr_t) kva + size, PAGE_SIZE * count - size); 926251881Speter } 927251881Speter pmap_qremove(kva, count); 928251881Speter 929251881Speter /* 930251881Speter * free the buffer header back to the swap buffer pool 931251881Speter */ 932251881Speter pbrelbo(bp); 933251881Speter relpbuf(bp, &vnode_pbuf_freecnt); 934251881Speter 935251881Speter VM_OBJECT_LOCK(object); 936251881Speter vm_page_lock_queues(); 937251881Speter for (i = 0, tfoff = foff; i < count; i++, tfoff = nextoff) { 938251881Speter vm_page_t mt; 939251881Speter 940251881Speter nextoff = tfoff + PAGE_SIZE; 941251881Speter mt = m[i]; 942251881Speter 943251881Speter if (nextoff <= object->un_pager.vnp.vnp_size) { 944251881Speter /* 945251881Speter * Read filled up entire page. 946251881Speter */ 947251881Speter mt->valid = VM_PAGE_BITS_ALL; 948251881Speter vm_page_undirty(mt); /* should be an assert? XXX */ 949251881Speter pmap_clear_modify(mt); 950251881Speter } else { 951251881Speter /* 952251881Speter * Read did not fill up entire page. Since this 953251881Speter * is getpages, the page may be mapped, so we have 954251881Speter * to zero the invalid portions of the page even 955251881Speter * though we aren't setting them valid. 956251881Speter * 957251881Speter * Currently we do not set the entire page valid, 958251881Speter * we just try to clear the piece that we couldn't 959251881Speter * read. 960251881Speter */ 961251881Speter vm_page_set_validclean(mt, 0, 962251881Speter object->un_pager.vnp.vnp_size - tfoff); 963251881Speter /* handled by vm_fault now */ 964251881Speter /* vm_page_zero_invalid(mt, FALSE); */ 965251881Speter } 966251881Speter 967251881Speter if (i != reqpage) { 968251881Speter 969251881Speter /* 970251881Speter * whether or not to leave the page activated is up in 971251881Speter * the air, but we should put the page on a page queue 972251881Speter * somewhere. (it already is in the object). Result: 973251881Speter * It appears that empirical results show that 974251881Speter * deactivating pages is best. 975251881Speter */ 976251881Speter 977251881Speter /* 978251881Speter * just in case someone was asking for this page we 979251881Speter * now tell them that it is ok to use 980251881Speter */ 981251881Speter if (!error) { 982251881Speter if (mt->oflags & VPO_WANTED) 983251881Speter vm_page_activate(mt); 984251881Speter else 985251881Speter vm_page_deactivate(mt); 986251881Speter vm_page_wakeup(mt); 987251881Speter } else { 988251881Speter vm_page_free(mt); 989251881Speter } 990251881Speter } 991251881Speter } 992251881Speter vm_page_unlock_queues(); 993251881Speter VM_OBJECT_UNLOCK(object); 994251881Speter if (error) { 995251881Speter printf("vnode_pager_getpages: I/O read error\n"); 996251881Speter } 997251881Speter return (error ? VM_PAGER_ERROR : VM_PAGER_OK); 998251881Speter} 999251881Speter 1000251881Speter/* 1001251881Speter * EOPNOTSUPP is no longer legal. For local media VFS's that do not 1002251881Speter * implement their own VOP_PUTPAGES, their VOP_PUTPAGES should call to 1003251881Speter * vnode_pager_generic_putpages() to implement the previous behaviour. 1004251881Speter * 1005251881Speter * All other FS's should use the bypass to get to the local media 1006251881Speter * backing vp's VOP_PUTPAGES. 1007251881Speter */ 1008251881Speterstatic void 1009251881Spetervnode_pager_putpages(object, m, count, sync, rtvals) 1010251881Speter vm_object_t object; 1011251881Speter vm_page_t *m; 1012251881Speter int count; 1013251881Speter boolean_t sync; 1014251881Speter int *rtvals; 1015251881Speter{ 1016251881Speter int rtval; 1017251881Speter struct vnode *vp; 1018251881Speter struct mount *mp; 1019251881Speter int bytes = count * PAGE_SIZE; 1020251881Speter 1021251881Speter /* 1022251881Speter * Force synchronous operation if we are extremely low on memory 1023251881Speter * to prevent a low-memory deadlock. VOP operations often need to 1024251881Speter * allocate more memory to initiate the I/O ( i.e. do a BMAP 1025251881Speter * operation ). The swapper handles the case by limiting the amount 1026251881Speter * of asynchronous I/O, but that sort of solution doesn't scale well 1027251881Speter * for the vnode pager without a lot of work. 1028251881Speter * 1029251881Speter * Also, the backing vnode's iodone routine may not wake the pageout 1030251881Speter * daemon up. This should be probably be addressed XXX. 1031251881Speter */ 1032251881Speter 1033251881Speter if ((cnt.v_free_count + cnt.v_cache_count) < cnt.v_pageout_free_min) 1034251881Speter sync |= OBJPC_SYNC; 1035251881Speter 1036251881Speter /* 1037251881Speter * Call device-specific putpages function 1038251881Speter */ 1039251881Speter vp = object->handle; 1040251881Speter VM_OBJECT_UNLOCK(object); 1041251881Speter if (vp->v_type != VREG) 1042251881Speter mp = NULL; 1043251881Speter rtval = VOP_PUTPAGES(vp, m, bytes, sync, rtvals, 0); 1044251881Speter KASSERT(rtval != EOPNOTSUPP, 1045251881Speter ("vnode_pager: stale FS putpages\n")); 1046251881Speter VM_OBJECT_LOCK(object); 1047251881Speter} 1048251881Speter 1049251881Speter 1050251881Speter/* 1051251881Speter * This is now called from local media FS's to operate against their 1052251881Speter * own vnodes if they fail to implement VOP_PUTPAGES. 1053251881Speter * 1054251881Speter * This is typically called indirectly via the pageout daemon and 1055251881Speter * clustering has already typically occured, so in general we ask the 1056251881Speter * underlying filesystem to write the data out asynchronously rather 1057251881Speter * then delayed. 1058251881Speter */ 1059251881Speterint 1060251881Spetervnode_pager_generic_putpages(vp, m, bytecount, flags, rtvals) 1061251881Speter struct vnode *vp; 1062251881Speter vm_page_t *m; 1063251881Speter int bytecount; 1064251881Speter int flags; 1065251881Speter int *rtvals; 1066251881Speter{ 1067251881Speter int i; 1068251881Speter vm_object_t object; 1069251881Speter int count; 1070251881Speter 1071251881Speter int maxsize, ncount; 1072251881Speter vm_ooffset_t poffset; 1073299742Sdim struct uio auio; 1074299742Sdim struct iovec aiov; 1075299742Sdim int error; 1076299742Sdim int ioflags; 1077299742Sdim int ppscheck = 0; 1078299742Sdim static struct timeval lastfail; 1079299742Sdim static int curfail; 1080299742Sdim 1081299742Sdim object = vp->v_object; 1082299742Sdim count = bytecount / PAGE_SIZE; 1083299742Sdim 1084299742Sdim for (i = 0; i < count; i++) 1085299742Sdim rtvals[i] = VM_PAGER_AGAIN; 1086299742Sdim 1087299742Sdim if ((int64_t)m[0]->pindex < 0) { 1088299742Sdim printf("vnode_pager_putpages: attempt to write meta-data!!! -- 0x%lx(%lx)\n", 1089299742Sdim (long)m[0]->pindex, (u_long)m[0]->dirty); 1090299742Sdim rtvals[0] = VM_PAGER_BAD; 1091299742Sdim return VM_PAGER_BAD; 1092299742Sdim } 1093299742Sdim 1094299742Sdim maxsize = count * PAGE_SIZE; 1095299742Sdim ncount = count; 1096299742Sdim 1097299742Sdim poffset = IDX_TO_OFF(m[0]->pindex); 1098299742Sdim 1099299742Sdim /* 1100299742Sdim * If the page-aligned write is larger then the actual file we 1101299742Sdim * have to invalidate pages occuring beyond the file EOF. However, 1102299742Sdim * there is an edge case where a file may not be page-aligned where 1103299742Sdim * the last page is partially invalid. In this case the filesystem 1104299742Sdim * may not properly clear the dirty bits for the entire page (which 1105299742Sdim * could be VM_PAGE_BITS_ALL due to the page having been mmap()d). 1106299742Sdim * With the page locked we are free to fix-up the dirty bits here. 1107299742Sdim * 1108299742Sdim * We do not under any circumstances truncate the valid bits, as 1109299742Sdim * this will screw up bogus page replacement. 1110299742Sdim */ 1111299742Sdim if (maxsize + poffset > object->un_pager.vnp.vnp_size) { 1112299742Sdim if (object->un_pager.vnp.vnp_size > poffset) { 1113299742Sdim int pgoff; 1114299742Sdim 1115299742Sdim maxsize = object->un_pager.vnp.vnp_size - poffset; 1116299742Sdim ncount = btoc(maxsize); 1117299742Sdim if ((pgoff = (int)maxsize & PAGE_MASK) != 0) { 1118299742Sdim vm_page_lock_queues(); 1119299742Sdim vm_page_clear_dirty(m[ncount - 1], pgoff, 1120299742Sdim PAGE_SIZE - pgoff); 1121299742Sdim vm_page_unlock_queues(); 1122299742Sdim } 1123299742Sdim } else { 1124299742Sdim maxsize = 0; 1125299742Sdim ncount = 0; 1126299742Sdim } 1127299742Sdim if (ncount < count) { 1128299742Sdim for (i = ncount; i < count; i++) { 1129299742Sdim rtvals[i] = VM_PAGER_BAD; 1130299742Sdim } 1131299742Sdim } 1132299742Sdim } 1133299742Sdim 1134299742Sdim /* 1135299742Sdim * pageouts are already clustered, use IO_ASYNC t o force a bawrite() 1136299742Sdim * rather then a bdwrite() to prevent paging I/O from saturating 1137299742Sdim * the buffer cache. Dummy-up the sequential heuristic to cause 1138299742Sdim * large ranges to cluster. If neither IO_SYNC or IO_ASYNC is set, 1139299742Sdim * the system decides how to cluster. 1140299742Sdim */ 1141299742Sdim ioflags = IO_VMIO; 1142299742Sdim if (flags & (VM_PAGER_PUT_SYNC | VM_PAGER_PUT_INVAL)) 1143299742Sdim ioflags |= IO_SYNC; 1144299742Sdim else if ((flags & VM_PAGER_CLUSTER_OK) == 0) 1145299742Sdim ioflags |= IO_ASYNC; 1146299742Sdim ioflags |= (flags & VM_PAGER_PUT_INVAL) ? IO_INVAL: 0; 1147299742Sdim ioflags |= IO_SEQMAX << IO_SEQSHIFT; 1148299742Sdim 1149299742Sdim aiov.iov_base = (caddr_t) 0; 1150299742Sdim aiov.iov_len = maxsize; 1151299742Sdim auio.uio_iov = &aiov; 1152299742Sdim auio.uio_iovcnt = 1; 1153299742Sdim auio.uio_offset = poffset; 1154299742Sdim auio.uio_segflg = UIO_NOCOPY; 1155299742Sdim auio.uio_rw = UIO_WRITE; 1156299742Sdim auio.uio_resid = maxsize; 1157299742Sdim auio.uio_td = (struct thread *) 0; 1158299742Sdim error = VOP_WRITE(vp, &auio, ioflags, curthread->td_ucred); 1159299742Sdim PCPU_INC(cnt.v_vnodeout); 1160299742Sdim PCPU_ADD(cnt.v_vnodepgsout, ncount); 1161299742Sdim 1162299742Sdim if (error) { 1163299742Sdim if ((ppscheck = ppsratecheck(&lastfail, &curfail, 1))) 1164299742Sdim printf("vnode_pager_putpages: I/O error %d\n", error); 1165299742Sdim } 1166299742Sdim if (auio.uio_resid) { 1167299742Sdim if (ppscheck || ppsratecheck(&lastfail, &curfail, 1)) 1168299742Sdim printf("vnode_pager_putpages: residual I/O %d at %lu\n", 1169299742Sdim auio.uio_resid, (u_long)m[0]->pindex); 1170299742Sdim } 1171299742Sdim for (i = 0; i < ncount; i++) { 1172299742Sdim rtvals[i] = VM_PAGER_OK; 1173299742Sdim } 1174299742Sdim return rtvals[0]; 1175299742Sdim} 1176299742Sdim 1177299742Sdimstruct vnode * 1178299742Sdimvnode_pager_lock(vm_object_t first_object) 1179299742Sdim{ 1180299742Sdim struct vnode *vp; 1181299742Sdim vm_object_t backing_object, object; 1182299742Sdim int locked, lockf; 1183299742Sdim 1184299742Sdim VM_OBJECT_LOCK_ASSERT(first_object, MA_OWNED); 1185299742Sdim for (object = first_object; object != NULL; object = backing_object) { 1186251881Speter if (object->type != OBJT_VNODE) { 1187251881Speter if ((backing_object = object->backing_object) != NULL) 1188251881Speter VM_OBJECT_LOCK(backing_object); 1189251881Speter if (object != first_object) 1190251881Speter VM_OBJECT_UNLOCK(object); 1191251881Speter continue; 1192251881Speter } 1193251881Speter retry: 1194251881Speter if (object->flags & OBJ_DEAD) { 1195251881Speter if (object != first_object) 1196251881Speter VM_OBJECT_UNLOCK(object); 1197251881Speter return NULL; 1198251881Speter } 1199251881Speter vp = object->handle; 1200251881Speter locked = VOP_ISLOCKED(vp); 1201251881Speter VI_LOCK(vp); 1202251881Speter VM_OBJECT_UNLOCK(object); 1203251881Speter if (first_object != object) 1204251881Speter VM_OBJECT_UNLOCK(first_object); 1205251881Speter VFS_ASSERT_GIANT(vp->v_mount); 1206251881Speter if (locked == LK_EXCLUSIVE) 1207251881Speter lockf = LK_CANRECURSE | LK_INTERLOCK | LK_RETRY | 1208251881Speter LK_EXCLUSIVE; 1209251881Speter else 1210251881Speter lockf = LK_CANRECURSE | LK_INTERLOCK | LK_RETRY | 1211251881Speter LK_SHARED; 1212251881Speter if (vget(vp, lockf, curthread)) { 1213251881Speter VM_OBJECT_LOCK(first_object); 1214251881Speter if (object != first_object) 1215251881Speter VM_OBJECT_LOCK(object); 1216251881Speter if (object->type != OBJT_VNODE) { 1217251881Speter if (object != first_object) 1218251881Speter VM_OBJECT_UNLOCK(object); 1219251881Speter return NULL; 1220251881Speter } 1221251881Speter printf("vnode_pager_lock: retrying\n"); 1222251881Speter goto retry; 1223251881Speter } 1224251881Speter VM_OBJECT_LOCK(first_object); 1225251881Speter return (vp); 1226251881Speter } 1227251881Speter return NULL; 1228251881Speter} 1229251881Speter