diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c index 1640eff..75b6e37 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c @@ -419,6 +419,66 @@ update_pages(vnode_t *vp, int64_t start, int len, objset_t *os, uint64_t oid, } /* + * Read with UIO_NOCOPY flag means that sendfile(2) requests + * ZFS to populate a range of page cache pages with data. + * + * NOTE: this function could be optimized to pre-allocate + * all pages in advance, drain VPO_BUSY on all of them, + * map them into contiguous KVA region and populate them + * in one single dmu_read() call. + */ +static int +mappedread_sf(vnode_t *vp, int nbytes, uio_t *uio) +{ + znode_t *zp = VTOZ(vp); + objset_t *os = zp->z_zfsvfs->z_os; + struct sf_buf *sf; + vm_object_t obj; + vm_page_t pp; + int64_t start; + caddr_t va; + int len = nbytes; + int off; + int error = 0; + + ASSERT(uio->uio_segflg == UIO_NOCOPY); + ASSERT(vp->v_mount != NULL); + obj = vp->v_object; + ASSERT(obj != NULL); + ASSERT((uio->uio_loffset & PAGEOFFSET) == 0); + + VM_OBJECT_LOCK(obj); + for (start = uio->uio_loffset; len > 0; start += PAGESIZE) { + int bytes = MIN(PAGESIZE, len); + + pp = vm_page_grab(obj, OFF_TO_IDX(start), VM_ALLOC_NOBUSY | + VM_ALLOC_NORMAL | VM_ALLOC_RETRY | VM_ALLOC_IGN_SBUSY); + if (pp->valid == 0) { + vm_page_io_start(pp); + VM_OBJECT_UNLOCK(obj); + + va = zfs_map_page(pp, &sf); + error = dmu_read(os, zp->z_id, start, bytes, va, + DMU_READ_PREFETCH); + if (bytes != PAGE_SIZE && error == 0) + bzero(va + bytes, PAGE_SIZE - bytes); + zfs_unmap_page(sf); + + VM_OBJECT_LOCK(obj); + vm_page_io_finish(pp); + if (error) + break; + pp->valid = VM_PAGE_BITS_ALL; + } + uio->uio_resid -= bytes; + uio->uio_offset += bytes; + len -= bytes; + } + VM_OBJECT_UNLOCK(obj); + return (error); +} + +/* * When a file is memory mapped, we must keep the IO data synchronized * between the DMU cache and the memory mapped pages. What this means: * @@ -466,54 +526,6 @@ mappedread(vnode_t *vp, int nbytes, uio_t *uio) error = dmu_read_uio(os, zp->z_id, uio, bytes); VM_OBJECT_LOCK(obj); } - /* TODO: sendfile(2) */ -#ifdef TODO - } else if (pp != NULL && uio->uio_segflg == UIO_NOCOPY) { - /* - * The code below is here to make sendfile(2) work - * correctly with ZFS. As pointed out by ups@ - * sendfile(2) should be changed to use VOP_GETPAGES(), - * but it pessimize performance of sendfile/UFS, that's - * why I handle this special case in ZFS code. - */ - KASSERT(off == 0, - ("unexpected offset in mappedread for sendfile")); - if ((pp->oflags & VPO_BUSY) != 0) { - /* - * Reference the page before unlocking and - * sleeping so that the page daemon is less - * likely to reclaim it. - */ - vm_page_lock_queues(); - vm_page_flag_set(pp, PG_REFERENCED); - vm_page_sleep(pp, "zfsmrb"); - goto again; - } - vm_page_busy(pp); - VM_OBJECT_UNLOCK(obj); - if (dirbytes > 0) { - error = dmu_read_uio(os, zp->z_id, uio, - dirbytes); - dirbytes = 0; - } - if (error == 0) { - va = zfs_map_page(pp, &sf); - error = dmu_read(os, zp->z_id, start, - bytes, va); - if (bytes != PAGE_SIZE) - bzero(va + bytes, PAGE_SIZE - bytes); - zfs_unmap_page(sf); - } - VM_OBJECT_LOCK(obj); - if (error == 0) - pp->valid = VM_PAGE_BITS_ALL; - vm_page_wakeup(pp); - if (error == 0) { - uio->uio_resid -= bytes; - uio->uio_offset += bytes; - } - } -#endif len -= bytes; off = 0; if (error) @@ -648,7 +660,11 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) while (n > 0) { nbytes = MIN(n, zfs_read_chunk_size - P2PHASE(uio->uio_loffset, zfs_read_chunk_size)); - +#ifdef __FreeBSD__ + if (uio->uio_segflg == UIO_NOCOPY) + error = mappedread_sf(vp, nbytes, uio); + else +#endif /* __FreeBSD__ */ if (vn_has_cached_data(vp)) error = mappedread(vp, nbytes, uio); else