/*- * Copyright (c) 2008 Jeffrey Roberson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vm_page_t vm_page_grab_next(vm_object_t object, vm_page_t prev, vm_pindex_t pindex); int vnode_pager_read_cluster(struct vnode *vp, vm_page_t m); /* * Grab a page, waiting until we are woken up due to the page * changing state. We keep on waiting, if the page continues * to be in the object. If the page doesn't exist allocate it. * * This routine may block. */ vm_page_t vm_page_grab_next(vm_object_t object, vm_page_t prev, vm_pindex_t pindex) { vm_page_t m; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); m = NULL; if (prev) { m = TAILQ_NEXT(prev, listq); if (m && m->pindex != pindex) m = NULL; } for (;;) { if (m == NULL) m = vm_page_lookup(object, pindex); if (m != NULL) { if (vm_page_sleep_if_busy(m, TRUE, "pgrnbwt") == 0) break; m = NULL; continue; } m = vm_page_alloc(object, pindex, VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY); if (m != NULL) break; VM_OBJECT_UNLOCK(object); VM_WAIT; VM_OBJECT_LOCK(object); } return (m); } /* * Read a cluster starting at 'm'. */ int vnode_pager_read_cluster(struct vnode *vp, vm_page_t m) { vm_page_t pa[MAXPHYS / PAGE_SIZE]; vm_object_t obj; vm_pindex_t idx; daddr_t blkno; int bsize; int error; int run; int i; obj = vp->v_object; idx = m->pindex; bsize = vp->v_mount->mnt_stat.f_iosize; VM_OBJECT_UNLOCK(obj); error = VOP_BMAP(vp, IDX_TO_OFF(idx)/bsize, NULL, &blkno, NULL, &run); VM_OBJECT_LOCK(obj); if (error || run == 0 || blkno == -1) return vm_pager_get_pages(obj, &m, 1, 0); run = (run + 1) * bsize / PAGE_SIZE; run = MIN(run, vp->v_mount->mnt_iosize_max / PAGE_SIZE); pa[0] = m; for (i = 1; i < run; i++) { m = vm_page_grab_next(obj, m, idx + i); if (m->valid) { run = i; break; } vm_page_busy(m); pa[i] = m; } return vm_pager_get_pages(obj, pa, run, 0); } int vnode_pager_read(struct vnode *vp, struct uio *uio) { vm_object_t obj; vm_offset_t off; vm_pindex_t idx; vm_page_t m; int error; int size; error = 0; obj = vp->v_object; m = NULL; VM_OBJECT_LOCK(obj); for (; uio->uio_resid > 0;) { size = obj->un_pager.vnp.vnp_size - uio->uio_offset; if (size <= 0) break; idx = OFF_TO_IDX(uio->uio_offset); off = uio->uio_offset - IDX_TO_OFF(idx); size = MIN(MIN(PAGE_SIZE - off, uio->uio_resid), size); m = vm_page_grab_next(obj, m, idx); if (!vm_page_is_valid(m, off, size)) { vm_page_busy(m); error = vnode_pager_read_cluster(vp, m); m = vm_page_lookup(obj, idx); if (m == NULL) { if (error == VM_PAGER_OK) continue; error = EIO; break; } if (m->valid == 0 || error != VM_PAGER_OK) { vm_page_lock_queues(); vm_page_free(m); vm_page_unlock_queues(); error = EIO; break; } vm_page_wakeup(m); } vm_page_io_start(m); VM_OBJECT_UNLOCK(obj); error = uiomove_fromphys(&m, off, size, uio); if (error) break; VM_OBJECT_LOCK(obj); vm_page_io_finish(m); } VM_OBJECT_UNLOCK(obj); return (error); }