Return correct type and offset from zfs_readdir
[zfs.git] / module / zfs / zfs_vnops.c
index aa43c06..db5d385 100644 (file)
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 /* Portions Copyright 2007 Jeremy Teo */
 /* Portions Copyright 2010 Robert Milkowski */
 
+
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/time.h>
@@ -33,7 +35,6 @@
 #include <sys/resource.h>
 #include <sys/vfs.h>
 #include <sys/vfs_opreg.h>
-#include <sys/vnode.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/kmem.h>
 #include <sys/uio.h>
 #include <sys/vmsystm.h>
 #include <sys/atomic.h>
-#include <sys/vm.h>
-#include <vm/seg_vn.h>
 #include <vm/pvn.h>
-#include <vm/as.h>
-#include <vm/kpm.h>
-#include <vm/seg_kpm.h>
-#include <sys/mman.h>
 #include <sys/pathname.h>
 #include <sys/cmn_err.h>
 #include <sys/errno.h>
 #include <sys/dirent.h>
 #include <sys/policy.h>
 #include <sys/sunddi.h>
-#include <sys/filio.h>
 #include <sys/sid.h>
+#include <sys/mode.h>
 #include "fs/fs_subr.h"
 #include <sys/zfs_ctldir.h>
 #include <sys/zfs_fuid.h>
 #include <sys/zfs_sa.h>
+#include <sys/zfs_vnops.h>
 #include <sys/dnlc.h>
 #include <sys/zfs_rlock.h>
 #include <sys/extdirent.h>
 #include <sys/kidmap.h>
 #include <sys/cred.h>
 #include <sys/attr.h>
+#include <sys/zpl.h>
 
 /*
  * Programming rules.
  * to freed memory.  The example below illustrates the following Big Rules:
  *
  *  (1) A check must be made in each zfs thread for a mounted file system.
- *     This is done avoiding races using ZFS_ENTER(zfsvfs).
- *      A ZFS_EXIT(zfsvfs) is needed before all returns.  Any znodes
+ *     This is done avoiding races using ZFS_ENTER(zsb).
+ *      A ZFS_EXIT(zsb) is needed before all returns.  Any znodes
  *      must be checked with ZFS_VERIFY_ZP(zp).  Both of these macros
  *      can return EIO from the calling function.
  *
- *  (2)        VN_RELE() should always be the last thing except for zil_commit()
+ *  (2)        iput() should always be the last thing except for zil_commit()
  *     (if necessary) and ZFS_EXIT(). This is for 3 reasons:
  *     First, if it's the last reference, the vnode/znode
  *     can be freed, so the zp may point to freed memory.  Second, the last
  *     pushing cached pages (which acquires range locks) and syncing out
  *     cached atime changes.  Third, zfs_zinactive() may require a new tx,
  *     which could deadlock the system if you were already holding one.
- *     If you must call VN_RELE() within a tx then use VN_RELE_ASYNC().
+ *     If you must call iput() within a tx then use iput_ASYNC().
  *
  *  (3)        All range locks must be grabbed before calling dmu_tx_assign(),
  *     as they can span dmu_tx_assign() calls.
  *     Thread A calls dmu_tx_assign(TXG_WAIT) and blocks in txg_wait_open()
  *     forever, because the previous txg can't quiesce until B's tx commits.
  *
- *     If dmu_tx_assign() returns ERESTART and zfsvfs->z_assign is TXG_NOWAIT,
+ *     If dmu_tx_assign() returns ERESTART and zsb->z_assign is TXG_NOWAIT,
  *     then drop all locks, call dmu_tx_wait(), and try again.
  *
  *  (5)        If the operation succeeded, generate the intent log entry for it
  *  (6)        At the end of each vnode op, the DMU tx must always commit,
  *     regardless of whether there were any errors.
  *
- *  (7)        After dropping all locks, invoke zil_commit(zilog, seq, foid)
+ *  (7)        After dropping all locks, invoke zil_commit(zilog, foid)
  *     to ensure that synchronous semantics are provided when necessary.
  *
  * In general, this is how things should be ordered in each vnode op:
  *
- *     ZFS_ENTER(zfsvfs);              // exit if unmounted
+ *     ZFS_ENTER(zsb);         // exit if unmounted
  * top:
- *     zfs_dirent_lock(&dl, ...)       // lock directory entry (may VN_HOLD())
+ *     zfs_dirent_lock(&dl, ...)       // lock directory entry (may igrab())
  *     rw_enter(...);                  // grab any other locks you need
  *     tx = dmu_tx_create(...);        // get DMU tx
  *     dmu_tx_hold_*();                // hold each object you might modify
  *     if (error) {
  *             rw_exit(...);           // drop locks
  *             zfs_dirent_unlock(dl);  // unlock directory entry
- *             VN_RELE(...);           // release held vnodes
+ *             iput(...);              // release held vnodes
  *             if (error == ERESTART) {
  *                     dmu_tx_wait(tx);
  *                     dmu_tx_abort(tx);
  *                     goto top;
  *             }
  *             dmu_tx_abort(tx);       // abort DMU tx
- *             ZFS_EXIT(zfsvfs);       // finished in zfs
+ *             ZFS_EXIT(zsb);  // finished in zfs
  *             return (error);         // really out of space
  *     }
  *     error = do_real_work();         // do whatever this VOP does
  *     dmu_tx_commit(tx);              // commit DMU tx -- error or not
  *     rw_exit(...);                   // drop locks
  *     zfs_dirent_unlock(dl);          // unlock directory entry
- *     VN_RELE(...);                   // release held vnodes
- *     zil_commit(zilog, seq, foid);   // synchronous when necessary
- *     ZFS_EXIT(zfsvfs);               // finished in zfs
+ *     iput(...);                      // release held vnodes
+ *     zil_commit(zilog, foid);        // synchronous when necessary
+ *     ZFS_EXIT(zsb);          // finished in zfs
  *     return (error);                 // done, report error
  */
 
-/* ARGSUSED */
+/*
+ * Virus scanning is unsupported.  It would be possible to add a hook
+ * here to performance the required virus scan.  This could be done
+ * entirely in the kernel or potentially as an update to invoke a
+ * scanning utility.
+ */
 static int
-zfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+zfs_vscan(struct inode *ip, cred_t *cr, int async)
+{
+       return (0);
+}
+
+/* ARGSUSED */
+int
+zfs_open(struct inode *ip, int mode, int flag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(*vpp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       if ((flag & FWRITE) && (zp->z_pflags & ZFS_APPENDONLY) &&
-           ((flag & FAPPEND) == 0)) {
-               ZFS_EXIT(zfsvfs);
+       /* Honor ZFS_APPENDONLY file attribute */
+       if ((mode & FMODE_WRITE) && (zp->z_pflags & ZFS_APPENDONLY) &&
+           ((flag & O_APPEND) == 0)) {
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan &&
-           ZTOV(zp)->v_type == VREG &&
+       /* Virus scan eligible files on open */
+       if (!zfs_has_ctldir(zp) && zsb->z_vscan && S_ISREG(ip->i_mode) &&
            !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0) {
-               if (fs_vscan(*vpp, cr, 0) != 0) {
-                       ZFS_EXIT(zfsvfs);
+               if (zfs_vscan(ip, cr, 0) != 0) {
+                       ZFS_EXIT(zsb);
                        return (EACCES);
                }
        }
 
        /* Keep a count of the synchronous opens in the znode */
-       if (flag & (FSYNC | FDSYNC))
+       if (flag & O_SYNC)
                atomic_inc_32(&zp->z_sync_cnt);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (0);
 }
+EXPORT_SYMBOL(zfs_open);
 
 /* ARGSUSED */
-static int
-zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_close(struct inode *ip, int flag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
 
-       /*
-        * Clean up any locks held by this process on the vp.
-        */
-       cleanlocks(vp, ddi_get_pid(), 0);
-       cleanshares(vp, ddi_get_pid());
-
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       /* Decrement the synchronous opens in the znode */
-       if ((flag & (FSYNC | FDSYNC)) && (count == 1))
-               atomic_dec_32(&zp->z_sync_cnt);
+       /*
+        * Zero the synchronous opens in the znode.  Under Linux the
+        * zfs_close() hook is not symmetric with zfs_open(), it is
+        * only called once when the last reference is dropped.
+        */
+       if (flag & O_SYNC)
+               zp->z_sync_cnt = 0;
 
-       if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan &&
-           ZTOV(zp)->v_type == VREG &&
+       if (!zfs_has_ctldir(zp) && zsb->z_vscan && S_ISREG(ip->i_mode) &&
            !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0)
-               VERIFY(fs_vscan(vp, cr, 1) == 0);
+               VERIFY(zfs_vscan(ip, cr, 1) == 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (0);
 }
+EXPORT_SYMBOL(zfs_close);
 
+#if defined(SEEK_HOLE) && defined(SEEK_DATA)
 /*
- * Lseek support for finding holes (cmd == _FIO_SEEK_HOLE) and
- * data (cmd == _FIO_SEEK_DATA). "off" is an in/out parameter.
+ * Lseek support for finding holes (cmd == SEEK_HOLE) and
+ * data (cmd == SEEK_DATA). "off" is an in/out parameter.
  */
 static int
-zfs_holey(vnode_t *vp, int cmd, offset_t *off)
+zfs_holey_common(struct inode *ip, int cmd, loff_t *off)
 {
-       znode_t *zp = VTOZ(vp);
+       znode_t *zp = ITOZ(ip);
        uint64_t noff = (uint64_t)*off; /* new offset */
        uint64_t file_sz;
        int error;
@@ -250,12 +259,12 @@ zfs_holey(vnode_t *vp, int cmd, offset_t *off)
                return (ENXIO);
        }
 
-       if (cmd == _FIO_SEEK_HOLE)
+       if (cmd == SEEK_HOLE)
                hole = B_TRUE;
        else
                hole = B_FALSE;
 
-       error = dmu_offset_next(zp->z_zfsvfs->z_os, zp->z_id, hole, &noff);
+       error = dmu_offset_next(ZTOZSB(zp)->z_os, zp->z_id, hole, &noff);
 
        /* end of file? */
        if ((error == ESRCH) || (noff > file_sz)) {
@@ -275,75 +284,25 @@ zfs_holey(vnode_t *vp, int cmd, offset_t *off)
        return (error);
 }
 
-/* ARGSUSED */
-static int
-zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred,
-    int *rvalp, caller_context_t *ct)
+int
+zfs_holey(struct inode *ip, int cmd, loff_t *off)
 {
-       offset_t off;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int error;
-       zfsvfs_t *zfsvfs;
-       znode_t *zp;
-
-       switch (com) {
-       case _FIOFFS:
-               return (zfs_sync(vp->v_vfsp, 0, cred));
-
-               /*
-                * The following two ioctls are used by bfu.  Faking out,
-                * necessary to avoid bfu errors.
-                */
-       case _FIOGDIO:
-       case _FIOSDIO:
-               return (0);
-
-       case _FIO_SEEK_DATA:
-       case _FIO_SEEK_HOLE:
-               if (ddi_copyin((void *)data, &off, sizeof (off), flag))
-                       return (EFAULT);
-
-               zp = VTOZ(vp);
-               zfsvfs = zp->z_zfsvfs;
-               ZFS_ENTER(zfsvfs);
-               ZFS_VERIFY_ZP(zp);
 
-               /* offset parameter is in/out */
-               error = zfs_holey(vp, com, &off);
-               ZFS_EXIT(zfsvfs);
-               if (error)
-                       return (error);
-               if (ddi_copyout(&off, (void *)data, sizeof (off), flag))
-                       return (EFAULT);
-               return (0);
-       }
-       return (ENOTTY);
-}
+       ZFS_ENTER(zsb);
+       ZFS_VERIFY_ZP(zp);
 
-/*
- * Utility functions to map and unmap a single physical page.  These
- * are used to manage the mappable copies of ZFS file data, and therefore
- * do not update ref/mod bits.
- */
-caddr_t
-zfs_map_page(page_t *pp, enum seg_rw rw)
-{
-       if (kpm_enable)
-               return (hat_kpm_mapin(pp, 0));
-       ASSERT(rw == S_READ || rw == S_WRITE);
-       return (ppmapin(pp, PROT_READ | ((rw == S_WRITE) ? PROT_WRITE : 0),
-           (caddr_t)-1));
-}
+       error = zfs_holey_common(ip, cmd, off);
 
-void
-zfs_unmap_page(page_t *pp, caddr_t addr)
-{
-       if (kpm_enable) {
-               hat_kpm_mapout(pp, 0, addr);
-       } else {
-               ppmapout(addr);
-       }
+       ZFS_EXIT(zsb);
+       return (error);
 }
+EXPORT_SYMBOL(zfs_holey);
+#endif /* SEEK_HOLE && SEEK_DATA */
 
+#if defined(_KERNEL)
 /*
  * 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:
@@ -352,24 +311,39 @@ zfs_unmap_page(page_t *pp, caddr_t addr)
  *             the page and the dmu buffer.
  */
 static void
-update_pages(vnode_t *vp, int64_t start, int len, objset_t *os, uint64_t oid)
+update_pages(struct inode *ip, int64_t start, int len,
+    objset_t *os, uint64_t oid)
 {
+       struct address_space *mp = ip->i_mapping;
+       struct page *pp;
+       uint64_t nbytes;
        int64_t off;
+       void *pb;
 
-       off = start & PAGEOFFSET;
-       for (start &= PAGEMASK; len > 0; start += PAGESIZE) {
-               page_t *pp;
-               uint64_t nbytes = MIN(PAGESIZE - off, len);
+       off = start & (PAGE_CACHE_SIZE-1);
+       for (start &= PAGE_CACHE_MASK; len > 0; start += PAGE_CACHE_SIZE) {
+               nbytes = MIN(PAGE_CACHE_SIZE - off, len);
 
-               if (pp = page_lookup(vp, start, SE_SHARED)) {
-                       caddr_t va;
+               pp = find_lock_page(mp, start >> PAGE_CACHE_SHIFT);
+               if (pp) {
+                       if (mapping_writably_mapped(mp))
+                               flush_dcache_page(pp);
 
-                       va = zfs_map_page(pp, S_WRITE);
-                       (void) dmu_read(os, oid, start+off, nbytes, va+off,
+                       pb = kmap(pp);
+                       (void) dmu_read(os, oid, start+off, nbytes, pb+off,
                            DMU_READ_PREFETCH);
-                       zfs_unmap_page(pp, va);
-                       page_unlock(pp);
+                       kunmap(pp);
+
+                       if (mapping_writably_mapped(mp))
+                               flush_dcache_page(pp);
+
+                       mark_page_accessed(pp);
+                       SetPageUptodate(pp);
+                       ClearPageError(pp);
+                       unlock_page(pp);
+                       page_cache_release(pp);
                }
+
                len -= nbytes;
                off = 0;
        }
@@ -386,30 +360,41 @@ update_pages(vnode_t *vp, int64_t start, int len, objset_t *os, uint64_t oid)
  *     the file is memory mapped.
  */
 static int
-mappedread(vnode_t *vp, int nbytes, uio_t *uio)
+mappedread(struct inode *ip, int nbytes, uio_t *uio)
 {
-       znode_t *zp = VTOZ(vp);
-       objset_t *os = zp->z_zfsvfs->z_os;
+       struct address_space *mp = ip->i_mapping;
+       struct page *pp;
+       znode_t *zp = ITOZ(ip);
+       objset_t *os = ITOZSB(ip)->z_os;
        int64_t start, off;
+       uint64_t bytes;
        int len = nbytes;
        int error = 0;
+       void *pb;
 
        start = uio->uio_loffset;
-       off = start & PAGEOFFSET;
-       for (start &= PAGEMASK; len > 0; start += PAGESIZE) {
-               page_t *pp;
-               uint64_t bytes = MIN(PAGESIZE - off, len);
-
-               if (pp = page_lookup(vp, start, SE_SHARED)) {
-                       caddr_t va;
-
-                       va = zfs_map_page(pp, S_READ);
-                       error = uiomove(va + off, bytes, UIO_READ, uio);
-                       zfs_unmap_page(pp, va);
-                       page_unlock(pp);
+       off = start & (PAGE_CACHE_SIZE-1);
+       for (start &= PAGE_CACHE_MASK; len > 0; start += PAGE_CACHE_SIZE) {
+               bytes = MIN(PAGE_CACHE_SIZE - off, len);
+
+               pp = find_lock_page(mp, start >> PAGE_CACHE_SHIFT);
+               if (pp) {
+                       ASSERT(PageUptodate(pp));
+
+                       pb = kmap(pp);
+                       error = uiomove(pb + off, bytes, UIO_READ, uio);
+                       kunmap(pp);
+
+                       if (mapping_writably_mapped(mp))
+                               flush_dcache_page(pp);
+
+                       mark_page_accessed(pp);
+                       unlock_page(pp);
+                       page_cache_release(pp);
                } else {
                        error = dmu_read_uio(os, zp->z_id, uio, bytes);
                }
+
                len -= bytes;
                off = 0;
                if (error)
@@ -417,18 +402,19 @@ mappedread(vnode_t *vp, int nbytes, uio_t *uio)
        }
        return (error);
 }
+#endif /* _KERNEL */
 
-offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */
+unsigned long zfs_read_chunk_size = 1024 * 1024; /* Tunable */
 
 /*
  * Read bytes from specified file into supplied buffer.
  *
- *     IN:     vp      - vnode of file to be read from.
+ *     IN:     ip      - inode of file to be read from.
  *             uio     - structure supplying read location, range info,
  *                       and return buffer.
- *             ioflag  - SYNC flags; used to provide FRSYNC semantics.
+ *             ioflag  - FSYNC flags; used to provide FRSYNC semantics.
+ *                       O_DIRECT flag; used to bypass page cache.
  *             cr      - credentials of caller.
- *             ct      - caller context
  *
  *     OUT:    uio     - updated offset and range, buffer filled.
  *
@@ -436,26 +422,28 @@ offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */
  *             error code if failure
  *
  * Side Effects:
- *     vp - atime updated if byte count > 0
+ *     inode - atime updated if byte count > 0
  */
 /* ARGSUSED */
-static int
-zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
+int
+zfs_read(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        objset_t        *os;
        ssize_t         n, nbytes;
-       int             error;
+       int             error = 0;
        rl_t            *rl;
+#ifdef HAVE_UIO_ZEROCOPY
        xuio_t          *xuio = NULL;
+#endif /* HAVE_UIO_ZEROCOPY */
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
-       os = zfsvfs->z_os;
+       os = zsb->z_os;
 
        if (zp->z_pflags & ZFS_AV_QUARANTINED) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EACCES);
        }
 
@@ -463,7 +451,7 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
         * Validate file offset
         */
        if (uio->uio_loffset < (offset_t)0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
@@ -471,26 +459,24 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
         * Fasttrack empty reads
         */
        if (uio->uio_resid == 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (0);
        }
 
        /*
         * Check for mandatory locks
         */
-       if (MANDMODE(zp->z_mode)) {
-               if (error = chklock(vp, FREAD,
-                   uio->uio_loffset, uio->uio_resid, uio->uio_fmode, ct)) {
-                       ZFS_EXIT(zfsvfs);
-                       return (error);
-               }
+       if (mandatory_lock(ip) &&
+           !lock_may_read(ip, uio->uio_loffset, uio->uio_resid)) {
+               ZFS_EXIT(zsb);
+               return (EAGAIN);
        }
 
        /*
         * If we're in FRSYNC mode, sync out this znode before reading it.
         */
-       if (ioflag & FRSYNC || zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zfsvfs->z_log, zp->z_last_itx, zp->z_id);
+       if (ioflag & FRSYNC || zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zsb->z_log, zp->z_id);
 
        /*
         * Lock the range against changes.
@@ -509,6 +495,7 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
        ASSERT(uio->uio_loffset < zp->z_size);
        n = MIN(uio->uio_resid, zp->z_size - uio->uio_loffset);
 
+#ifdef HAVE_UIO_ZEROCOPY
        if ((uio->uio_extflg == UIO_XUIO) &&
            (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY)) {
                int nblk;
@@ -525,7 +512,7 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
                }
                (void) dmu_xuio_init(xuio, nblk);
 
-               if (vn_has_cached_data(vp)) {
+               if (vn_has_cached_data(ip)) {
                        /*
                         * For simplicity, we always allocate a full buffer
                         * even if we only expect to read a portion of a block.
@@ -537,15 +524,17 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
                        }
                }
        }
+#endif /* HAVE_UIO_ZEROCOPY */
 
        while (n > 0) {
                nbytes = MIN(n, zfs_read_chunk_size -
                    P2PHASE(uio->uio_loffset, zfs_read_chunk_size));
 
-               if (vn_has_cached_data(vp))
-                       error = mappedread(vp, nbytes, uio);
+               if (zp->z_is_mapped && !(ioflag & O_DIRECT))
+                       error = mappedread(ip, nbytes, uio);
                else
                        error = dmu_read_uio(os, zp->z_id, uio, nbytes);
+
                if (error) {
                        /* convert checksum errors into IO errors */
                        if (error == ECKSUM)
@@ -558,20 +547,22 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
 out:
        zfs_range_unlock(rl);
 
-       ZFS_ACCESSTIME_STAMP(zfsvfs, zp);
-       ZFS_EXIT(zfsvfs);
+       ZFS_ACCESSTIME_STAMP(zsb, zp);
+       zfs_inode_update(zp);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_read);
 
 /*
  * Write the bytes to a file.
  *
- *     IN:     vp      - vnode of file to be written to.
+ *     IN:     ip      - inode of file to be written to.
  *             uio     - structure supplying write location, range info,
  *                       and data buffer.
  *             ioflag  - FAPPEND flag set if in append mode.
+ *                       O_DIRECT flag; used to bypass page cache.
  *             cr      - credentials of caller.
- *             ct      - caller context (NFS/CIFS fem monitor only)
  *
  *     OUT:    uio     - updated offset and range.
  *
@@ -579,36 +570,36 @@ out:
  *             error code if failure
  *
  * Timestamps:
- *     vp - ctime|mtime updated if byte count > 0
+ *     ip - ctime|mtime updated if byte count > 0
  */
 
 /* ARGSUSED */
-static int
-zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
+int
+zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       rlim64_t        limit = uio->uio_llimit;
+       znode_t         *zp = ITOZ(ip);
+       rlim64_t        limit = uio->uio_limit;
        ssize_t         start_resid = uio->uio_resid;
        ssize_t         tx_bytes;
        uint64_t        end_size;
        dmu_tx_t        *tx;
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       zfs_sb_t        *zsb = ZTOZSB(zp);
        zilog_t         *zilog;
        offset_t        woff;
        ssize_t         n, nbytes;
        rl_t            *rl;
-       int             max_blksz = zfsvfs->z_max_blksz;
-       int             error;
+       int             max_blksz = zsb->z_max_blksz;
+       int             error = 0;
        arc_buf_t       *abuf;
-       iovec_t         *aiov;
+       iovec_t         *aiov = NULL;
        xuio_t          *xuio = NULL;
        int             i_iov = 0;
-       int             iovcnt = uio->uio_iovcnt;
        iovec_t         *iovp = uio->uio_iov;
        int             write_eof;
        int             count = 0;
        sa_bulk_attr_t  bulk[4];
        uint64_t        mtime[2], ctime[2];
+       ASSERTV(int     iovcnt = uio->uio_iovcnt);
 
        /*
         * Fasttrack empty write
@@ -620,14 +611,13 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
        if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
                limit = MAXOFFSET_T;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16);
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16);
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), NULL,
-           &zp->z_size, 8);
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zsb), NULL, &zp->z_size, 8);
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zsb), NULL,
            &zp->z_pflags, 8);
 
        /*
@@ -636,18 +626,18 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
        if ((zp->z_pflags & (ZFS_IMMUTABLE | ZFS_READONLY)) ||
            ((zp->z_pflags & ZFS_APPENDONLY) && !(ioflag & FAPPEND) &&
            (uio->uio_loffset < zp->z_size))) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
        /*
         * Validate file offset
         */
        woff = ioflag & FAPPEND ? zp->z_size : uio->uio_loffset;
        if (woff < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
@@ -655,12 +645,12 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
         * Check for mandatory locks before calling zfs_range_lock()
         * in order to prevent a deadlock with locks set via fcntl().
         */
-       if (MANDMODE((mode_t)zp->z_mode) &&
-           (error = chklock(vp, FWRITE, woff, n, uio->uio_fmode, ct)) != 0) {
-               ZFS_EXIT(zfsvfs);
-               return (error);
+       if (mandatory_lock(ip) && !lock_may_write(ip, woff, n)) {
+               ZFS_EXIT(zsb);
+               return (EAGAIN);
        }
 
+#ifdef HAVE_UIO_ZEROCOPY
        /*
         * Pre-fault the pages to ensure slow (eg NFS) pages
         * don't hold up txg.
@@ -670,7 +660,8 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
            (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY))
                xuio = (xuio_t *)uio;
        else
-               uio_prefaultpages(n, uio);
+               uio_prefaultpages(MIN(n, max_blksz), uio);
+#endif /* HAVE_UIO_ZEROCOPY */
 
        /*
         * If in append mode, set the io offset pointer to eof.
@@ -702,7 +693,7 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
 
        if (woff >= limit) {
                zfs_range_unlock(rl);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EFBIG);
        }
 
@@ -723,8 +714,8 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
                abuf = NULL;
                woff = uio->uio_loffset;
 again:
-               if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) ||
-                   zfs_owner_overquota(zfsvfs, zp, B_TRUE)) {
+               if (zfs_owner_overquota(zsb, zp, B_FALSE) ||
+                   zfs_owner_overquota(zsb, zp, B_TRUE)) {
                        if (abuf != NULL)
                                dmu_return_arcbuf(abuf);
                        error = EDQUOT;
@@ -736,8 +727,6 @@ again:
                        aiov = &iovp[i_iov];
                        abuf = dmu_xuio_arcbuf(xuio, i_iov);
                        dmu_xuio_clear(xuio, i_iov);
-                       DTRACE_PROBE3(zfs_cp_write, int, i_iov,
-                           iovec_t *, aiov, arc_buf_t *, abuf);
                        ASSERT((aiov->iov_base == abuf->b_data) ||
                            ((char *)aiov->iov_base - (char *)abuf->b_data +
                            aiov->iov_len == arc_buf_size(abuf)));
@@ -759,8 +748,8 @@ again:
                            max_blksz);
                        ASSERT(abuf != NULL);
                        ASSERT(arc_buf_size(abuf) == max_blksz);
-                       if (error = uiocopy(abuf->b_data, max_blksz,
-                           UIO_WRITE, uio, &cbytes)) {
+                       if ((error = uiocopy(abuf->b_data, max_blksz,
+                           UIO_WRITE, uio, &cbytes))) {
                                dmu_return_arcbuf(abuf);
                                break;
                        }
@@ -770,7 +759,7 @@ again:
                /*
                 * Start a transaction.
                 */
-               tx = dmu_tx_create(zfsvfs->z_os);
+               tx = dmu_tx_create(zsb->z_os);
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
                dmu_tx_hold_write(tx, zp->z_id, woff, MIN(n, max_blksz));
                zfs_sa_upgrade_txholds(tx, zp);
@@ -829,7 +818,7 @@ again:
                        if (tx_bytes < max_blksz && (!write_eof ||
                            aiov->iov_base != abuf->b_data)) {
                                ASSERT(xuio);
-                               dmu_write(zfsvfs->z_os, zp->z_id, woff,
+                               dmu_write(zsb->z_os, zp->z_id, woff,
                                    aiov->iov_len, aiov->iov_base, tx);
                                dmu_return_arcbuf(abuf);
                                xuio_stat_wbuf_copied();
@@ -841,17 +830,16 @@ again:
                        ASSERT(tx_bytes <= uio->uio_resid);
                        uioskip(uio, tx_bytes);
                }
-               if (tx_bytes && vn_has_cached_data(vp)) {
-                       update_pages(vp, woff,
-                           tx_bytes, zfsvfs->z_os, zp->z_id);
-               }
+
+               if (tx_bytes && zp->z_is_mapped && !(ioflag & O_DIRECT))
+                       update_pages(ip, woff, tx_bytes, zsb->z_os, zp->z_id);
 
                /*
                 * If we made no progress, we're done.  If we made even
                 * partial progress, update the znode and ZIL accordingly.
                 */
                if (tx_bytes == 0) {
-                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs),
+                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zsb),
                            (void *)&zp->z_size, sizeof (uint64_t), tx);
                        dmu_tx_commit(tx);
                        ASSERT(error != 0);
@@ -866,6 +854,8 @@ again:
                 * been done, but that would still expose the ISUID/ISGID
                 * to another app after the partial write is committed.
                 *
+                * Note: we don't call zfs_fuid_map_id() here because
+                * user 0 is not an ephemeral uid.
                 */
                mutex_enter(&zp->z_acl_lock);
                if ((zp->z_mode & (S_IXUSR | (S_IXUSR >> 3) |
@@ -876,7 +866,7 @@ again:
                        uint64_t newmode;
                        zp->z_mode &= ~(S_ISUID | S_ISGID);
                        newmode = zp->z_mode;
-                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_MODE(zfsvfs),
+                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_MODE(zsb),
                            (void *)&newmode, sizeof (uint64_t), tx);
                }
                mutex_exit(&zp->z_acl_lock);
@@ -893,6 +883,14 @@ again:
                            uio->uio_loffset);
                        ASSERT(error == 0);
                }
+               /*
+                * If we are replaying and eof is non zero then force
+                * the file size to the specified eof. Note, there's no
+                * concurrency during replay.
+                */
+               if (zsb->z_replay && zsb->z_replay_eof != 0)
+                       zp->z_size = zsb->z_replay_eof;
+
                error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
 
                zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag);
@@ -902,6 +900,9 @@ again:
                        break;
                ASSERT(tx_bytes == nbytes);
                n -= nbytes;
+
+               if (!xuio && n > 0)
+                       uio_prefaultpages(MIN(n, max_blksz), uio);
        }
 
        zfs_range_unlock(rl);
@@ -910,24 +911,36 @@ again:
         * If we're in replay mode, or we made no progress, return error.
         * Otherwise, it's at least a partial write, so it's successful.
         */
-       if (zfsvfs->z_replay || uio->uio_resid == start_resid) {
-               ZFS_EXIT(zfsvfs);
+       if (zsb->z_replay || uio->uio_resid == start_resid) {
+               ZFS_EXIT(zsb);
                return (error);
        }
 
        if (ioflag & (FSYNC | FDSYNC) ||
-           zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, zp->z_last_itx, zp->z_id);
+           zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, zp->z_id);
 
-       ZFS_EXIT(zfsvfs);
+       zfs_inode_update(zp);
+       ZFS_EXIT(zsb);
        return (0);
 }
+EXPORT_SYMBOL(zfs_write);
+
+static void
+iput_async(struct inode *ip, taskq_t *taskq)
+{
+       ASSERT(atomic_read(&ip->i_count) > 0);
+       if (atomic_read(&ip->i_count) == 1)
+               taskq_dispatch(taskq, (task_func_t *)iput, ip, TQ_PUSHPAGE);
+       else
+               iput(ip);
+}
 
 void
 zfs_get_done(zgd_t *zgd, int error)
 {
        znode_t *zp = zgd->zgd_private;
-       objset_t *os = zp->z_zfsvfs->z_os;
+       objset_t *os = ZTOZSB(zp)->z_os;
 
        if (zgd->zgd_db)
                dmu_buf_rele(zgd->zgd_db, zgd);
@@ -938,7 +951,7 @@ zfs_get_done(zgd_t *zgd, int error)
         * Release the vnode asynchronously as we currently have the
         * txg stopped from syncing.
         */
-       VN_RELE_ASYNC(ZTOV(zp), dsl_pool_vnrele_taskq(dmu_objset_pool(os)));
+       iput_async(ZTOI(zp), dsl_pool_iput_taskq(dmu_objset_pool(os)));
 
        if (error == 0 && zgd->zgd_bp)
                zil_add_block(zgd->zgd_zilog, zgd->zgd_bp);
@@ -956,8 +969,8 @@ static int zil_fault_io = 0;
 int
 zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
 {
-       zfsvfs_t *zfsvfs = arg;
-       objset_t *os = zfsvfs->z_os;
+       zfs_sb_t *zsb = arg;
+       objset_t *os = zsb->z_os;
        znode_t *zp;
        uint64_t object = lr->lr_foid;
        uint64_t offset = lr->lr_offset;
@@ -973,20 +986,19 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
        /*
         * Nothing to do if the file has been removed
         */
-       if (zfs_zget(zfsvfs, object, &zp) != 0)
+       if (zfs_zget(zsb, object, &zp) != 0)
                return (ENOENT);
        if (zp->z_unlinked) {
                /*
                 * Release the vnode asynchronously as we currently have the
                 * txg stopped from syncing.
                 */
-               VN_RELE_ASYNC(ZTOV(zp),
-                   dsl_pool_vnrele_taskq(dmu_objset_pool(os)));
+               iput_async(ZTOI(zp), dsl_pool_iput_taskq(dmu_objset_pool(os)));
                return (ENOENT);
        }
 
-       zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_SLEEP);
-       zgd->zgd_zilog = zfsvfs->z_log;
+       zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_PUSHPAGE);
+       zgd->zgd_zilog = zsb->z_log;
        zgd->zgd_private = zp;
 
        /*
@@ -1071,15 +1083,14 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
 }
 
 /*ARGSUSED*/
-static int
-zfs_access(vnode_t *vp, int mode, int flag, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_access(struct inode *ip, int mode, int flag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int error;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
        if (flag & V_ACE_MASK)
@@ -1087,46 +1098,23 @@ zfs_access(vnode_t *vp, int mode, int flag, cred_t *cr,
        else
                error = zfs_zaccess_rwx(zp, mode, flag, cr);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
-
-/*
- * If vnode is for a device return a specfs vnode instead.
- */
-static int
-specvp_check(vnode_t **vpp, cred_t *cr)
-{
-       int error = 0;
-
-       if (IS_DEVVP(*vpp)) {
-               struct vnode *svp;
-
-               svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
-               VN_RELE(*vpp);
-               if (svp == NULL)
-                       error = ENOSYS;
-               *vpp = svp;
-       }
-       return (error);
-}
-
+EXPORT_SYMBOL(zfs_access);
 
 /*
  * Lookup an entry in a directory, or an extended attribute directory.
- * If it exists, return a held vnode reference for it.
+ * If it exists, return a held inode reference for it.
  *
- *     IN:     dvp     - vnode of directory to search.
+ *     IN:     dip     - inode of directory to search.
  *             nm      - name of entry to lookup.
- *             pnp     - full pathname to lookup [UNUSED].
  *             flags   - LOOKUP_XATTR set if looking for an attribute.
- *             rdir    - root directory vnode [UNUSED].
  *             cr      - credentials of caller.
- *             ct      - caller context
  *             direntflags - directory lookup flags
  *             realpnp - returned pathname.
  *
- *     OUT:    vpp     - vnode of located entry, NULL if not found.
+ *     OUT:    ipp     - inode of located entry, NULL if not found.
  *
  *     RETURN: 0 if success
  *             error code if failure
@@ -1135,19 +1123,18 @@ specvp_check(vnode_t **vpp, cred_t *cr)
  *     NA
  */
 /* ARGSUSED */
-static int
-zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
-    int flags, vnode_t *rdir, cred_t *cr,  caller_context_t *ct,
-    int *direntflags, pathname_t *realpnp)
+int
+zfs_lookup(struct inode *dip, char *nm, struct inode **ipp, int flags,
+    cred_t *cr, int *direntflags, pathname_t *realpnp)
 {
-       znode_t *zdp = VTOZ(dvp);
-       zfsvfs_t *zfsvfs = zdp->z_zfsvfs;
-       int     error = 0;
+       znode_t *zdp = ITOZ(dip);
+       zfs_sb_t *zsb = ITOZSB(dip);
+       int error = 0;
 
        /* fast path */
        if (!(flags & (LOOKUP_XATTR | FIGNORECASE))) {
 
-               if (dvp->v_type != VDIR) {
+               if (!S_ISDIR(dip->i_mode)) {
                        return (ENOTDIR);
                } else if (zdp->z_sa_hdl == NULL) {
                        return (EIO);
@@ -1156,58 +1143,50 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
                if (nm[0] == 0 || (nm[0] == '.' && nm[1] == '\0')) {
                        error = zfs_fastaccesschk_execute(zdp, cr);
                        if (!error) {
-                               *vpp = dvp;
-                               VN_HOLD(*vpp);
+                               *ipp = dip;
+                               igrab(*ipp);
                                return (0);
                        }
                        return (error);
+#ifdef HAVE_DNLC
                } else {
                        vnode_t *tvp = dnlc_lookup(dvp, nm);
 
                        if (tvp) {
                                error = zfs_fastaccesschk_execute(zdp, cr);
                                if (error) {
-                                       VN_RELE(tvp);
+                                       iput(tvp);
                                        return (error);
                                }
                                if (tvp == DNLC_NO_VNODE) {
-                                       VN_RELE(tvp);
+                                       iput(tvp);
                                        return (ENOENT);
                                } else {
                                        *vpp = tvp;
                                        return (specvp_check(vpp, cr));
                                }
                        }
+#endif /* HAVE_DNLC */
                }
        }
 
-       DTRACE_PROBE2(zfs__fastpath__lookup__miss, vnode_t *, dvp, char *, nm);
-
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zdp);
 
-       *vpp = NULL;
+       *ipp = NULL;
 
        if (flags & LOOKUP_XATTR) {
                /*
-                * If the xattr property is off, refuse the lookup request.
-                */
-               if (!(zfsvfs->z_vfs->vfs_flag & VFS_XATTR)) {
-                       ZFS_EXIT(zfsvfs);
-                       return (EINVAL);
-               }
-
-               /*
                 * We don't allow recursive attributes..
                 * Maybe someday we will.
                 */
                if (zdp->z_pflags & ZFS_XATTR) {
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (EINVAL);
                }
 
-               if (error = zfs_get_xattrdir(VTOZ(dvp), vpp, cr, flags)) {
-                       ZFS_EXIT(zfsvfs);
+               if ((error = zfs_get_xattrdir(zdp, ipp, cr, flags))) {
+                       ZFS_EXIT(zsb);
                        return (error);
                }
 
@@ -1215,18 +1194,18 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
                 * Do we have permission to get into attribute directory?
                 */
 
-               if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0,
-                   B_FALSE, cr)) {
-                       VN_RELE(*vpp);
-                       *vpp = NULL;
+               if ((error = zfs_zaccess(ITOZ(*ipp), ACE_EXECUTE, 0,
+                   B_FALSE, cr))) {
+                       iput(*ipp);
+                       *ipp = NULL;
                }
 
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (dvp->v_type != VDIR) {
-               ZFS_EXIT(zfsvfs);
+       if (!S_ISDIR(dip->i_mode)) {
+               ZFS_EXIT(zsb);
                return (ENOTDIR);
        }
 
@@ -1234,66 +1213,64 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
         * Check accessibility of directory.
         */
 
-       if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) {
-               ZFS_EXIT(zfsvfs);
+       if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr))) {
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm),
+       if (zsb->z_utf8 && u8_validate(nm, strlen(nm),
            NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
 
-       error = zfs_dirlook(zdp, nm, vpp, flags, direntflags, realpnp);
-       if (error == 0)
-               error = specvp_check(vpp, cr);
+       error = zfs_dirlook(zdp, nm, ipp, flags, direntflags, realpnp);
+       if ((error == 0) && (*ipp))
+               zfs_inode_update(ITOZ(*ipp));
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_lookup);
 
 /*
  * Attempt to create a new entry in a directory.  If the entry
  * already exists, truncate the file if permissible, else return
- * an error.  Return the vp of the created or trunc'd file.
+ * an error.  Return the ip of the created or trunc'd file.
  *
- *     IN:     dvp     - vnode of directory to put new file entry in.
+ *     IN:     dip     - inode of directory to put new file entry in.
  *             name    - name of new file entry.
  *             vap     - attributes of new file.
  *             excl    - flag indicating exclusive or non-exclusive mode.
  *             mode    - mode to open file with.
  *             cr      - credentials of caller.
  *             flag    - large file flag [UNUSED].
- *             ct      - caller context
- *             vsecp   - ACL to be set
+ *             vsecp   - ACL to be set
  *
- *     OUT:    vpp     - vnode of created or trunc'd entry.
+ *     OUT:    ipp     - inode of created or trunc'd entry.
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     dvp - ctime|mtime updated if new entry created
- *      vp - ctime|mtime always, atime if new
+ *     dip - ctime|mtime updated if new entry created
+ *      ip - ctime|mtime always, atime if new
  */
 
 /* ARGSUSED */
-static int
-zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
-    int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
-    vsecattr_t *vsecp)
+int
+zfs_create(struct inode *dip, char *name, vattr_t *vap, int excl,
+    int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp)
 {
-       znode_t         *zp, *dzp = VTOZ(dvp);
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       znode_t         *zp, *dzp = ITOZ(dip);
+       zfs_sb_t        *zsb = ITOZSB(dip);
        zilog_t         *zilog;
        objset_t        *os;
        zfs_dirlock_t   *dl;
        dmu_tx_t        *tx;
        int             error;
-       ksid_t          *ksid;
        uid_t           uid;
-       gid_t           gid = crgetgid(cr);
+       gid_t           gid;
        zfs_acl_ids_t   acl_ids;
        boolean_t       fuid_dirtied;
        boolean_t       have_acl = B_FALSE;
@@ -1303,51 +1280,44 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
         * make sure file system is at proper version
         */
 
-       ksid = crgetsid(cr, KSID_OWNER);
-       if (ksid)
-               uid = ksid_getid(ksid);
-       else
-               uid = crgetuid(cr);
+       gid = crgetgid(cr);
+       uid = crgetuid(cr);
 
-       if (zfsvfs->z_use_fuids == B_FALSE &&
-           (vsecp || (vap->va_mask & AT_XVATTR) ||
-           IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid)))
+       if (zsb->z_use_fuids == B_FALSE &&
+           (vsecp || IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid)))
                return (EINVAL);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       os = zfsvfs->z_os;
-       zilog = zfsvfs->z_log;
+       os = zsb->z_os;
+       zilog = zsb->z_log;
 
-       if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
+       if (zsb->z_utf8 && u8_validate(name, strlen(name),
            NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
 
-       if (vap->va_mask & AT_XVATTR) {
+       if (vap->va_mask & ATTR_XVATTR) {
                if ((error = secpolicy_xvattr((xvattr_t *)vap,
-                   crgetuid(cr), cr, vap->va_type)) != 0) {
-                       ZFS_EXIT(zfsvfs);
+                   crgetuid(cr), cr, vap->va_mode)) != 0) {
+                       ZFS_EXIT(zsb);
                        return (error);
                }
        }
-top:
-       *vpp = NULL;
-
-       if ((vap->va_mode & VSVTX) && secpolicy_vnode_stky_modify(cr))
-               vap->va_mode &= ~VSVTX;
 
+top:
+       *ipp = NULL;
        if (*name == '\0') {
                /*
                 * Null component name refers to the directory itself.
                 */
-               VN_HOLD(dvp);
+               igrab(dip);
                zp = dzp;
                dl = NULL;
                error = 0;
        } else {
-               /* possible VN_HOLD(zp) */
+               /* possible igrab(zp) */
                int zflg = 0;
 
                if (flag & FIGNORECASE)
@@ -1356,9 +1326,11 @@ top:
                error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
                    NULL, NULL);
                if (error) {
+                       if (have_acl)
+                               zfs_acl_ids_free(&acl_ids);
                        if (strcmp(name, "..") == 0)
                                error = EISDIR;
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (error);
                }
        }
@@ -1370,7 +1342,9 @@ top:
                 * Create a new file object and update the directory
                 * to reference it.
                 */
-               if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
+               if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
+                       if (have_acl)
+                               zfs_acl_ids_free(&acl_ids);
                        goto out;
                }
 
@@ -1379,8 +1353,9 @@ top:
                 * extended attribute directories.
                 */
 
-               if ((dzp->z_pflags & ZFS_XATTR) &&
-                   (vap->va_type != VREG)) {
+               if ((dzp->z_pflags & ZFS_XATTR) && !S_ISREG(vap->va_mode)) {
+                       if (have_acl)
+                               zfs_acl_ids_free(&acl_ids);
                        error = EINVAL;
                        goto out;
                }
@@ -1390,7 +1365,7 @@ top:
                        goto out;
                have_acl = B_TRUE;
 
-               if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+               if (zfs_acl_ids_overquota(zsb, &acl_ids)) {
                        zfs_acl_ids_free(&acl_ids);
                        error = EDQUOT;
                        goto out;
@@ -1401,12 +1376,12 @@ top:
                dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes +
                    ZFS_SA_BASE_ATTR_SIZE);
 
-               fuid_dirtied = zfsvfs->z_fuid_dirty;
+               fuid_dirtied = zsb->z_fuid_dirty;
                if (fuid_dirtied)
-                       zfs_fuid_txhold(zfsvfs, tx);
+                       zfs_fuid_txhold(zsb, tx);
                dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name);
                dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE);
-               if (!zfsvfs->z_use_sa &&
+               if (!zsb->z_use_sa &&
                    acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
                        dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
                            0, acl_ids.z_aclp->z_acl_bytes);
@@ -1421,13 +1396,13 @@ top:
                        }
                        zfs_acl_ids_free(&acl_ids);
                        dmu_tx_abort(tx);
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (error);
                }
                zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids);
 
                if (fuid_dirtied)
-                       zfs_fuid_sync(zfsvfs, tx);
+                       zfs_fuid_sync(zsb, tx);
 
                (void) zfs_link_create(dl, zp, tx, ZNEW);
                txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap);
@@ -1440,20 +1415,24 @@ top:
        } else {
                int aflags = (flag & FAPPEND) ? V_APPEND : 0;
 
+               if (have_acl)
+                       zfs_acl_ids_free(&acl_ids);
+               have_acl = B_FALSE;
+
                /*
                 * A directory entry already exists for this name.
                 */
                /*
                 * Can't truncate an existing file if in exclusive mode.
                 */
-               if (excl == EXCL) {
+               if (excl) {
                        error = EEXIST;
                        goto out;
                }
                /*
                 * Can't open a directory for writing.
                 */
-               if ((ZTOV(zp)->v_type == VDIR) && (mode & S_IWRITE)) {
+               if (S_ISDIR(ZTOI(zp)->i_mode)) {
                        error = EISDIR;
                        goto out;
                }
@@ -1471,15 +1450,12 @@ top:
                /*
                 * Truncate regular files if requested.
                 */
-               if ((ZTOV(zp)->v_type == VREG) &&
-                   (vap->va_mask & AT_SIZE) && (vap->va_size == 0)) {
+               if (S_ISREG(ZTOI(zp)->i_mode) &&
+                   (vap->va_mask & ATTR_SIZE) && (vap->va_size == 0)) {
                        /* we can't hold any locks when calling zfs_freesp() */
                        zfs_dirent_unlock(dl);
                        dl = NULL;
                        error = zfs_freesp(zp, 0, 0, mode, TRUE);
-                       if (error == 0) {
-                               vnevent_create(ZTOV(zp), ct);
-                       }
                }
        }
 out:
@@ -1489,156 +1465,152 @@ out:
 
        if (error) {
                if (zp)
-                       VN_RELE(ZTOV(zp));
+                       iput(ZTOI(zp));
        } else {
-               *vpp = ZTOV(zp);
-               error = specvp_check(vpp, cr);
+               zfs_inode_update(dzp);
+               zfs_inode_update(zp);
+               *ipp = ZTOI(zp);
        }
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_create);
 
 /*
  * Remove an entry from a directory.
  *
- *     IN:     dvp     - vnode of directory to remove entry from.
+ *     IN:     dip     - inode of directory to remove entry from.
  *             name    - name of entry to remove.
  *             cr      - credentials of caller.
- *             ct      - caller context
- *             flags   - case flags
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     dvp - ctime|mtime
- *      vp - ctime (if nlink > 0)
+ *     dip - ctime|mtime
+ *      ip - ctime (if nlink > 0)
  */
 
 uint64_t null_xattr = 0;
 
 /*ARGSUSED*/
-static int
-zfs_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
-    int flags)
+int
+zfs_remove(struct inode *dip, char *name, cred_t *cr)
 {
-       znode_t         *zp, *dzp = VTOZ(dvp);
-       znode_t         *xzp = NULL;
-       vnode_t         *vp;
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       znode_t         *zp, *dzp = ITOZ(dip);
+       znode_t         *xzp;
+       struct inode    *ip;
+       zfs_sb_t        *zsb = ITOZSB(dip);
        zilog_t         *zilog;
-       uint64_t        acl_obj, xattr_obj = 0;
-       uint64_t        xattr_obj_unlinked = 0;
+       uint64_t        xattr_obj;
+       uint64_t        xattr_obj_unlinked = 0;
+       uint64_t        obj = 0;
        zfs_dirlock_t   *dl;
        dmu_tx_t        *tx;
-       boolean_t       may_delete_now, delete_now = FALSE;
-       boolean_t       unlinked, toobig = FALSE;
+       boolean_t       unlinked;
        uint64_t        txtype;
        pathname_t      *realnmp = NULL;
+#ifdef HAVE_PN_UTILS
        pathname_t      realnm;
+#endif /* HAVE_PN_UTILS */
        int             error;
        int             zflg = ZEXISTS;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
+#ifdef HAVE_PN_UTILS
        if (flags & FIGNORECASE) {
                zflg |= ZCILOOK;
                pn_alloc(&realnm);
                realnmp = &realnm;
        }
+#endif /* HAVE_PN_UTILS */
 
 top:
+       xattr_obj = 0;
+       xzp = NULL;
        /*
         * Attempt to lock directory; fail if entry doesn't exist.
         */
-       if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
-           NULL, realnmp)) {
+       if ((error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
+           NULL, realnmp))) {
+#ifdef HAVE_PN_UTILS
                if (realnmp)
                        pn_free(realnmp);
-               ZFS_EXIT(zfsvfs);
+#endif /* HAVE_PN_UTILS */
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       vp = ZTOV(zp);
+       ip = ZTOI(zp);
 
-       if (error = zfs_zaccess_delete(dzp, zp, cr)) {
+       if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
                goto out;
        }
 
        /*
         * Need to use rmdir for removing directories.
         */
-       if (vp->v_type == VDIR) {
+       if (S_ISDIR(ip->i_mode)) {
                error = EPERM;
                goto out;
        }
 
-       vnevent_remove(vp, dvp, name, ct);
-
+#ifdef HAVE_DNLC
        if (realnmp)
                dnlc_remove(dvp, realnmp->pn_buf);
        else
                dnlc_remove(dvp, name);
-
-       mutex_enter(&vp->v_lock);
-       may_delete_now = vp->v_count == 1 && !vn_has_cached_data(vp);
-       mutex_exit(&vp->v_lock);
+#endif /* HAVE_DNLC */
 
        /*
-        * We may delete the znode now, or we may put it in the unlinked set;
-        * it depends on whether we're the last link, and on whether there are
-        * other holds on the vnode.  So we dmu_tx_hold() the right things to
-        * allow for either case.
+        * We never delete the znode and always place it in the unlinked
+        * set.  The dentry cache will always hold the last reference and
+        * is responsible for safely freeing the znode.
         */
-       tx = dmu_tx_create(zfsvfs->z_os);
+       obj = zp->z_id;
+       tx = dmu_tx_create(zsb->z_os);
        dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name);
        dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
        zfs_sa_upgrade_txholds(tx, zp);
        zfs_sa_upgrade_txholds(tx, dzp);
-       if (may_delete_now) {
-               toobig =
-                   zp->z_size > zp->z_blksz * DMU_MAX_DELETEBLKCNT;
-               /* if the file is too big, only hold_free a token amount */
-               dmu_tx_hold_free(tx, zp->z_id, 0,
-                   (toobig ? DMU_MAX_ACCESS : DMU_OBJECT_END));
-       }
 
        /* are there any extended attributes? */
-       error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs),
+       error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zsb),
            &xattr_obj, sizeof (xattr_obj));
-       if (xattr_obj) {
-               error = zfs_zget(zfsvfs, xattr_obj, &xzp);
-               ASSERT3U(error, ==, 0);
+       if (error == 0 && xattr_obj) {
+               error = zfs_zget(zsb, xattr_obj, &xzp);
+               ASSERT0(error);
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
                dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE);
        }
 
-       /* are there any additional acls */
-       if ((acl_obj = ZFS_EXTERNAL_ACL(zp)) != 0 && may_delete_now)
-               dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END);
-
        /* charge as an update -- would be nice not to charge at all */
-       dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL);
+       dmu_tx_hold_zap(tx, zsb->z_unlinkedobj, FALSE, NULL);
 
        error = dmu_tx_assign(tx, TXG_NOWAIT);
        if (error) {
                zfs_dirent_unlock(dl);
-               VN_RELE(vp);
+               iput(ip);
+               if (xzp)
+                       iput(ZTOI(xzp));
                if (error == ERESTART) {
                        dmu_tx_wait(tx);
                        dmu_tx_abort(tx);
                        goto top;
                }
+#ifdef HAVE_PN_UTILS
                if (realnmp)
                        pn_free(realnmp);
+#endif /* HAVE_PN_UTILS */
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -1653,157 +1625,127 @@ top:
        }
 
        if (unlinked) {
-
-               mutex_enter(&vp->v_lock);
-
-               (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs),
-                   &xattr_obj_unlinked, sizeof (xattr_obj_unlinked));
-               delete_now = may_delete_now && !toobig &&
-                   vp->v_count == 1 && !vn_has_cached_data(vp) &&
-                   xattr_obj == xattr_obj_unlinked && ZFS_EXTERNAL_ACL(zp) ==
-                   acl_obj;
-               mutex_exit(&vp->v_lock);
-       }
-
-       if (delete_now) {
-               if (xattr_obj_unlinked) {
-                       ASSERT3U(xzp->z_links, ==, 2);
-                       mutex_enter(&xzp->z_lock);
-                       xzp->z_unlinked = 1;
-                       xzp->z_links = 0;
-                       error = sa_update(xzp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs),
-                           &xzp->z_links, sizeof (xzp->z_links), tx);
-                       ASSERT3U(error,  ==,  0);
-                       mutex_exit(&xzp->z_lock);
-                       zfs_unlinked_add(xzp, tx);
-                       if (zp->z_is_sa)
-                               error = sa_remove(zp->z_sa_hdl,
-                                   SA_ZPL_XATTR(zfsvfs), tx);
-                       else
-                               error = sa_update(zp->z_sa_hdl,
-                                   SA_ZPL_XATTR(zfsvfs), &null_xattr,
-                                   sizeof (uint64_t), tx);
-                       ASSERT3U(error, ==, 0);
-               }
+               /*
+                * Hold z_lock so that we can make sure that the ACL obj
+                * hasn't changed.  Could have been deleted due to
+                * zfs_sa_upgrade().
+                */
                mutex_enter(&zp->z_lock);
-               mutex_enter(&vp->v_lock);
-               vp->v_count--;
-               ASSERT3U(vp->v_count, ==, 0);
-               mutex_exit(&vp->v_lock);
+               (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zsb),
+                   &xattr_obj_unlinked, sizeof (xattr_obj_unlinked));
                mutex_exit(&zp->z_lock);
-               zfs_znode_delete(zp, tx);
-       } else if (unlinked) {
                zfs_unlinked_add(zp, tx);
        }
 
        txtype = TX_REMOVE;
+#ifdef HAVE_PN_UTILS
        if (flags & FIGNORECASE)
                txtype |= TX_CI;
-       zfs_log_remove(zilog, tx, txtype, dzp, name);
+#endif /* HAVE_PN_UTILS */
+       zfs_log_remove(zilog, tx, txtype, dzp, name, obj);
 
        dmu_tx_commit(tx);
 out:
+#ifdef HAVE_PN_UTILS
        if (realnmp)
                pn_free(realnmp);
+#endif /* HAVE_PN_UTILS */
 
        zfs_dirent_unlock(dl);
+       zfs_inode_update(dzp);
+       zfs_inode_update(zp);
+       if (xzp)
+               zfs_inode_update(xzp);
 
-       if (!delete_now)
-               VN_RELE(vp);
+       iput(ip);
        if (xzp)
-               VN_RELE(ZTOV(xzp));
+               iput(ZTOI(xzp));
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_remove);
 
 /*
- * Create a new directory and insert it into dvp using the name
+ * Create a new directory and insert it into dip using the name
  * provided.  Return a pointer to the inserted directory.
  *
- *     IN:     dvp     - vnode of directory to add subdir to.
+ *     IN:     dip     - inode of directory to add subdir to.
  *             dirname - name of new directory.
  *             vap     - attributes of new directory.
  *             cr      - credentials of caller.
- *             ct      - caller context
  *             vsecp   - ACL to be set
  *
- *     OUT:    vpp     - vnode of created directory.
+ *     OUT:    ipp     - inode of created directory.
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     dvp - ctime|mtime updated
- *      vp - ctime|mtime|atime updated
+ *     dip - ctime|mtime updated
+ *     ipp - ctime|mtime|atime updated
  */
 /*ARGSUSED*/
-static int
-zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr,
-    caller_context_t *ct, int flags, vsecattr_t *vsecp)
+int
+zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp,
+    cred_t *cr, int flags, vsecattr_t *vsecp)
 {
-       znode_t         *zp, *dzp = VTOZ(dvp);
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       znode_t         *zp, *dzp = ITOZ(dip);
+       zfs_sb_t        *zsb = ITOZSB(dip);
        zilog_t         *zilog;
        zfs_dirlock_t   *dl;
        uint64_t        txtype;
        dmu_tx_t        *tx;
        int             error;
        int             zf = ZNEW;
-       ksid_t          *ksid;
        uid_t           uid;
        gid_t           gid = crgetgid(cr);
        zfs_acl_ids_t   acl_ids;
        boolean_t       fuid_dirtied;
 
-       ASSERT(vap->va_type == VDIR);
+       ASSERT(S_ISDIR(vap->va_mode));
 
        /*
         * If we have an ephemeral id, ACL, or XVATTR then
         * make sure file system is at proper version
         */
 
-       ksid = crgetsid(cr, KSID_OWNER);
-       if (ksid)
-               uid = ksid_getid(ksid);
-       else
-               uid = crgetuid(cr);
-       if (zfsvfs->z_use_fuids == B_FALSE &&
-           (vsecp || (vap->va_mask & AT_XVATTR) ||
-           IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid)))
+       uid = crgetuid(cr);
+       if (zsb->z_use_fuids == B_FALSE &&
+           (vsecp || IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid)))
                return (EINVAL);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
        if (dzp->z_pflags & ZFS_XATTR) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
-       if (zfsvfs->z_utf8 && u8_validate(dirname,
+       if (zsb->z_utf8 && u8_validate(dirname,
            strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
        if (flags & FIGNORECASE)
                zf |= ZCILOOK;
 
-       if (vap->va_mask & AT_XVATTR) {
+       if (vap->va_mask & ATTR_XVATTR) {
                if ((error = secpolicy_xvattr((xvattr_t *)vap,
-                   crgetuid(cr), cr, vap->va_type)) != 0) {
-                       ZFS_EXIT(zfsvfs);
+                   crgetuid(cr), cr, vap->va_mode)) != 0) {
+                       ZFS_EXIT(zsb);
                        return (error);
                }
        }
 
        if ((error = zfs_acl_ids_create(dzp, 0, vap, cr,
            vsecp, &acl_ids)) != 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
        /*
@@ -1814,39 +1756,39 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr,
         * to fail.
         */
 top:
-       *vpp = NULL;
+       *ipp = NULL;
 
-       if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf,
-           NULL, NULL)) {
+       if ((error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf,
+           NULL, NULL))) {
                zfs_acl_ids_free(&acl_ids);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) {
+       if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr))) {
                zfs_acl_ids_free(&acl_ids);
                zfs_dirent_unlock(dl);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+       if (zfs_acl_ids_overquota(zsb, &acl_ids)) {
                zfs_acl_ids_free(&acl_ids);
                zfs_dirent_unlock(dl);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EDQUOT);
        }
 
        /*
         * Add a new entry to the directory.
         */
-       tx = dmu_tx_create(zfsvfs->z_os);
+       tx = dmu_tx_create(zsb->z_os);
        dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname);
        dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
-       fuid_dirtied = zfsvfs->z_fuid_dirty;
+       fuid_dirtied = zsb->z_fuid_dirty;
        if (fuid_dirtied)
-               zfs_fuid_txhold(zfsvfs, tx);
-       if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
+               zfs_fuid_txhold(zsb, tx);
+       if (!zsb->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
                dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
                    acl_ids.z_aclp->z_acl_bytes);
        }
@@ -1864,7 +1806,7 @@ top:
                }
                zfs_acl_ids_free(&acl_ids);
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -1874,14 +1816,14 @@ top:
        zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids);
 
        if (fuid_dirtied)
-               zfs_fuid_sync(zfsvfs, tx);
+               zfs_fuid_sync(zsb, tx);
 
        /*
         * Now put new name in parent dir.
         */
        (void) zfs_link_create(dl, zp, tx, ZNEW);
 
-       *vpp = ZTOV(zp);
+       *ipp = ZTOI(zp);
 
        txtype = zfs_log_create_txtype(Z_DIR, vsecp, vap);
        if (flags & FIGNORECASE)
@@ -1895,49 +1837,51 @@ top:
 
        zfs_dirent_unlock(dl);
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       zfs_inode_update(dzp);
+       zfs_inode_update(zp);
+       ZFS_EXIT(zsb);
        return (0);
 }
+EXPORT_SYMBOL(zfs_mkdir);
 
 /*
  * Remove a directory subdir entry.  If the current working
  * directory is the same as the subdir to be removed, the
  * remove will fail.
  *
- *     IN:     dvp     - vnode of directory to remove from.
+ *     IN:     dip     - inode of directory to remove from.
  *             name    - name of directory to be removed.
- *             cwd     - vnode of current working directory.
+ *             cwd     - inode of current working directory.
  *             cr      - credentials of caller.
- *             ct      - caller context
  *             flags   - case flags
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     dvp - ctime|mtime updated
+ *     dip - ctime|mtime updated
  */
 /*ARGSUSED*/
-static int
-zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
-    caller_context_t *ct, int flags)
+int
+zfs_rmdir(struct inode *dip, char *name, struct inode *cwd, cred_t *cr,
+    int flags)
 {
-       znode_t         *dzp = VTOZ(dvp);
+       znode_t         *dzp = ITOZ(dip);
        znode_t         *zp;
-       vnode_t         *vp;
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       struct inode    *ip;
+       zfs_sb_t        *zsb = ITOZSB(dip);
        zilog_t         *zilog;
        zfs_dirlock_t   *dl;
        dmu_tx_t        *tx;
        int             error;
        int             zflg = ZEXISTS;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
        if (flags & FIGNORECASE)
                zflg |= ZCILOOK;
@@ -1947,30 +1891,28 @@ top:
        /*
         * Attempt to lock directory; fail if entry doesn't exist.
         */
-       if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
-           NULL, NULL)) {
-               ZFS_EXIT(zfsvfs);
+       if ((error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
+           NULL, NULL))) {
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       vp = ZTOV(zp);
+       ip = ZTOI(zp);
 
-       if (error = zfs_zaccess_delete(dzp, zp, cr)) {
+       if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
                goto out;
        }
 
-       if (vp->v_type != VDIR) {
+       if (!S_ISDIR(ip->i_mode)) {
                error = ENOTDIR;
                goto out;
        }
 
-       if (vp == cwd) {
+       if (ip == cwd) {
                error = EINVAL;
                goto out;
        }
 
-       vnevent_rmdir(vp, dvp, name, ct);
-
        /*
         * Grab a lock on the directory to make sure that noone is
         * trying to add (or lookup) entries while we are removing it.
@@ -1983,10 +1925,10 @@ top:
         */
        rw_enter(&zp->z_parent_lock, RW_WRITER);
 
-       tx = dmu_tx_create(zfsvfs->z_os);
+       tx = dmu_tx_create(zsb->z_os);
        dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name);
        dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
-       dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL);
+       dmu_tx_hold_zap(tx, zsb->z_unlinkedobj, FALSE, NULL);
        zfs_sa_upgrade_txholds(tx, zp);
        zfs_sa_upgrade_txholds(tx, dzp);
        error = dmu_tx_assign(tx, TXG_NOWAIT);
@@ -1994,14 +1936,14 @@ top:
                rw_exit(&zp->z_parent_lock);
                rw_exit(&zp->z_name_lock);
                zfs_dirent_unlock(dl);
-               VN_RELE(vp);
+               iput(ip);
                if (error == ERESTART) {
                        dmu_tx_wait(tx);
                        dmu_tx_abort(tx);
                        goto top;
                }
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -2011,7 +1953,7 @@ top:
                uint64_t txtype = TX_RMDIR;
                if (flags & FIGNORECASE)
                        txtype |= TX_CI;
-               zfs_log_remove(zilog, tx, txtype, dzp, name);
+               zfs_log_remove(zilog, tx, txtype, dzp, name, ZFS_NO_OBJECT);
        }
 
        dmu_tx_commit(tx);
@@ -2021,35 +1963,32 @@ top:
 out:
        zfs_dirent_unlock(dl);
 
-       VN_RELE(vp);
+       zfs_inode_update(dzp);
+       zfs_inode_update(zp);
+       iput(ip);
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_rmdir);
 
 /*
  * Read as many directory entries as will fit into the provided
- * buffer from the given directory cursor position (specified in
- * the uio structure.
+ * dirent buffer from the given directory cursor position.
  *
- *     IN:     vp      - vnode of directory to read.
- *             uio     - structure supplying read location, range info,
- *                       and return buffer.
- *             cr      - credentials of caller.
- *             ct      - caller context
- *             flags   - case flags
+ *     IN:     ip      - inode of directory to read.
+ *             dirent  - buffer for directory entries.
  *
- *     OUT:    uio     - updated offset and range, buffer filled.
- *             eofp    - set to true if end-of-file detected.
+ *     OUT:    dirent  - filler buffer of directory entries.
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     vp - atime updated
+ *     ip - atime updated
  *
  * Note that the low 4 bits of the cookie returned by zap is always zero.
  * This allows us to use the low range for "special" directory entries:
@@ -2057,70 +1996,42 @@ out:
  * we use the offset 2 for the '.zfs' directory.
  */
 /* ARGSUSED */
-static int
-zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
-    caller_context_t *ct, int flags)
+int
+zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir,
+    loff_t *pos, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       iovec_t         *iovp;
-       edirent_t       *eodp;
-       dirent64_t      *odp;
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        objset_t        *os;
-       caddr_t         outbuf;
-       size_t          bufsize;
        zap_cursor_t    zc;
        zap_attribute_t zap;
-       uint_t          bytes_wanted;
-       uint64_t        offset; /* must be unsigned; checks for < 1 */
-       uint64_t        parent;
-       int             local_eof;
        int             outcount;
        int             error;
        uint8_t         prefetch;
-       boolean_t       check_sysattrs;
+       int             done = 0;
+       uint64_t        parent;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs),
-           &parent, sizeof (parent))) != 0) {
-               ZFS_EXIT(zfsvfs);
-               return (error);
-       }
-
-       /*
-        * If we are not given an eof variable,
-        * use a local one.
-        */
-       if (eofp == NULL)
-               eofp = &local_eof;
-
-       /*
-        * Check for valid iov_len.
-        */
-       if (uio->uio_iov->iov_len <= 0) {
-               ZFS_EXIT(zfsvfs);
-               return (EINVAL);
-       }
+       if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zsb),
+           &parent, sizeof (parent))) != 0)
+               goto out;
 
        /*
         * Quit if directory has been removed (posix)
         */
-       if ((*eofp = zp->z_unlinked) != 0) {
-               ZFS_EXIT(zfsvfs);
-               return (0);
-       }
-
        error = 0;
-       os = zfsvfs->z_os;
-       offset = uio->uio_loffset;
+       if (zp->z_unlinked)
+               goto out;
+
+       os = zsb->z_os;
        prefetch = zp->z_zn_prefetch;
 
        /*
         * Initialize the iterator cursor.
         */
-       if (offset <= 3) {
+       if (*pos <= 3) {
                /*
                 * Start iteration from the beginning of the directory.
                 */
@@ -2129,55 +2040,28 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
                /*
                 * The offset is a serialized cursor.
                 */
-               zap_cursor_init_serialized(&zc, os, zp->z_id, offset);
-       }
-
-       /*
-        * Get space to change directory entries into fs independent format.
-        */
-       iovp = uio->uio_iov;
-       bytes_wanted = iovp->iov_len;
-       if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) {
-               bufsize = bytes_wanted;
-               outbuf = kmem_alloc(bufsize, KM_SLEEP);
-               odp = (struct dirent64 *)outbuf;
-       } else {
-               bufsize = bytes_wanted;
-               odp = (struct dirent64 *)iovp->iov_base;
+               zap_cursor_init_serialized(&zc, os, zp->z_id, *pos);
        }
-       eodp = (struct edirent *)odp;
-
-       /*
-        * If this VFS supports the system attribute view interface; and
-        * we're looking at an extended attribute directory; and we care
-        * about normalization conflicts on this vfs; then we must check
-        * for normalization conflicts with the sysattr name space.
-        */
-       check_sysattrs = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
-           (vp->v_flag & V_XATTRDIR) && zfsvfs->z_norm &&
-           (flags & V_RDDIR_ENTFLAGS);
 
        /*
         * Transform to file-system independent format
         */
        outcount = 0;
-       while (outcount < bytes_wanted) {
-               ino64_t objnum;
-               ushort_t reclen;
-               off64_t *next;
 
+       while (!done) {
+               uint64_t objnum;
                /*
                 * Special case `.', `..', and `.zfs'.
                 */
-               if (offset == 0) {
+               if (*pos == 0) {
                        (void) strcpy(zap.za_name, ".");
                        zap.za_normalization_conflict = 0;
                        objnum = zp->z_id;
-               } else if (offset == 1) {
+               } else if (*pos == 1) {
                        (void) strcpy(zap.za_name, "..");
                        zap.za_normalization_conflict = 0;
                        objnum = parent;
-               } else if (offset == 2 && zfs_show_ctldir(zp)) {
+               } else if (*pos == 2 && zfs_show_ctldir(zp)) {
                        (void) strcpy(zap.za_name, ZFS_CTLDIR_NAME);
                        zap.za_normalization_conflict = 0;
                        objnum = ZFSCTL_INO_ROOT;
@@ -2185,193 +2069,111 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
                        /*
                         * Grab next entry.
                         */
-                       if (error = zap_cursor_retrieve(&zc, &zap)) {
-                               if ((*eofp = (error == ENOENT)) != 0)
+                       if ((error = zap_cursor_retrieve(&zc, &zap))) {
+                               if (error == ENOENT)
                                        break;
                                else
                                        goto update;
                        }
 
+                       /*
+                        * Allow multiple entries provided the first entry is
+                        * the object id.  Non-zpl consumers may safely make
+                        * use of the additional space.
+                        *
+                        * XXX: This should be a feature flag for compatibility
+                        */
                        if (zap.za_integer_length != 8 ||
-                           zap.za_num_integers != 1) {
+                           zap.za_num_integers == 0) {
                                cmn_err(CE_WARN, "zap_readdir: bad directory "
-                                   "entry, obj = %lld, offset = %lld\n",
+                                   "entry, obj = %lld, offset = %lld, "
+                                   "length = %d, num = %lld\n",
                                    (u_longlong_t)zp->z_id,
-                                   (u_longlong_t)offset);
+                                   (u_longlong_t)*pos,
+                                   zap.za_integer_length,
+                                   (u_longlong_t)zap.za_num_integers);
                                error = ENXIO;
                                goto update;
                        }
 
                        objnum = ZFS_DIRENT_OBJ(zap.za_first_integer);
-                       /*
-                        * MacOS X can extract the object type here such as:
-                        * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer);
-                        */
-
-                       if (check_sysattrs && !zap.za_normalization_conflict) {
-                               zap.za_normalization_conflict =
-                                   xattr_sysattr_casechk(zap.za_name);
-                       }
-               }
-
-               if (flags & V_RDDIR_ACCFILTER) {
-                       /*
-                        * If we have no access at all, don't include
-                        * this entry in the returned information
-                        */
-                       znode_t *ezp;
-                       if (zfs_zget(zp->z_zfsvfs, objnum, &ezp) != 0)
-                               goto skip_entry;
-                       if (!zfs_has_access(ezp, cr)) {
-                               VN_RELE(ZTOV(ezp));
-                               goto skip_entry;
-                       }
-                       VN_RELE(ZTOV(ezp));
                }
-
-               if (flags & V_RDDIR_ENTFLAGS)
-                       reclen = EDIRENT_RECLEN(strlen(zap.za_name));
-               else
-                       reclen = DIRENT64_RECLEN(strlen(zap.za_name));
-
-               /*
-                * Will this entry fit in the buffer?
-                */
-               if (outcount + reclen > bufsize) {
-                       /*
-                        * Did we manage to fit anything in the buffer?
-                        */
-                       if (!outcount) {
-                               error = EINVAL;
-                               goto update;
-                       }
+               done = filldir(dirent, zap.za_name, strlen(zap.za_name),
+                              *pos, objnum, ZFS_DIRENT_TYPE(zap.za_first_integer));
+               if (done) {
                        break;
                }
-               if (flags & V_RDDIR_ENTFLAGS) {
-                       /*
-                        * Add extended flag entry:
-                        */
-                       eodp->ed_ino = objnum;
-                       eodp->ed_reclen = reclen;
-                       /* NOTE: ed_off is the offset for the *next* entry */
-                       next = &(eodp->ed_off);
-                       eodp->ed_eflags = zap.za_normalization_conflict ?
-                           ED_CASE_CONFLICT : 0;
-                       (void) strncpy(eodp->ed_name, zap.za_name,
-                           EDIRENT_NAMELEN(reclen));
-                       eodp = (edirent_t *)((intptr_t)eodp + reclen);
-               } else {
-                       /*
-                        * Add normal entry:
-                        */
-                       odp->d_ino = objnum;
-                       odp->d_reclen = reclen;
-                       /* NOTE: d_off is the offset for the *next* entry */
-                       next = &(odp->d_off);
-                       (void) strncpy(odp->d_name, zap.za_name,
-                           DIRENT64_NAMELEN(reclen));
-                       odp = (dirent64_t *)((intptr_t)odp + reclen);
-               }
-               outcount += reclen;
-
-               ASSERT(outcount <= bufsize);
 
                /* Prefetch znode */
-               if (prefetch)
+               if (prefetch) {
                        dmu_prefetch(os, objnum, 0, 0);
+               }
 
-       skip_entry:
-               /*
-                * Move to the next entry, fill in the previous offset.
-                */
-               if (offset > 2 || (offset == 2 && !zfs_show_ctldir(zp))) {
+               if (*pos > 2 || (*pos == 2 && !zfs_show_ctldir(zp))) {
                        zap_cursor_advance(&zc);
-                       offset = zap_cursor_serialize(&zc);
+                       *pos = zap_cursor_serialize(&zc);
                } else {
-                       offset += 1;
+                       (*pos)++;
                }
-               *next = offset;
        }
        zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */
 
-       if (uio->uio_segflg == UIO_SYSSPACE && uio->uio_iovcnt == 1) {
-               iovp->iov_base += outcount;
-               iovp->iov_len -= outcount;
-               uio->uio_resid -= outcount;
-       } else if (error = uiomove(outbuf, (long)outcount, UIO_READ, uio)) {
-               /*
-                * Reset the pointer.
-                */
-               offset = uio->uio_loffset;
-       }
-
 update:
        zap_cursor_fini(&zc);
-       if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
-               kmem_free(outbuf, bufsize);
-
        if (error == ENOENT)
                error = 0;
 
-       ZFS_ACCESSTIME_STAMP(zfsvfs, zp);
+       ZFS_ACCESSTIME_STAMP(zsb, zp);
+       zfs_inode_update(zp);
+
+out:
+       ZFS_EXIT(zsb);
 
-       uio->uio_loffset = offset;
-       ZFS_EXIT(zfsvfs);
        return (error);
 }
+EXPORT_SYMBOL(zfs_readdir);
 
 ulong_t zfs_fsync_sync_cnt = 4;
 
-static int
-zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
+int
+zfs_fsync(struct inode *ip, int syncflag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
-
-       /*
-        * Regardless of whether this is required for standards conformance,
-        * this is the logical behavior when fsync() is called on a file with
-        * dirty pages.  We use B_ASYNC since the ZIL transactions are already
-        * going to be pushed out as part of the zil_commit().
-        */
-       if (vn_has_cached_data(vp) && !(syncflag & FNODSYNC) &&
-           (vp->v_type == VREG) && !(IS_SWAPVP(vp)))
-               (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_ASYNC, cr, ct);
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
 
        (void) tsd_set(zfs_fsyncer_key, (void *)zfs_fsync_sync_cnt);
 
-       if (zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED) {
-               ZFS_ENTER(zfsvfs);
+       if (zsb->z_os->os_sync != ZFS_SYNC_DISABLED) {
+               ZFS_ENTER(zsb);
                ZFS_VERIFY_ZP(zp);
-               zil_commit(zfsvfs->z_log, zp->z_last_itx, zp->z_id);
-               ZFS_EXIT(zfsvfs);
+               zil_commit(zsb->z_log, zp->z_id);
+               ZFS_EXIT(zsb);
        }
        return (0);
 }
+EXPORT_SYMBOL(zfs_fsync);
 
 
 /*
  * Get the requested file attributes and place them in the provided
  * vattr structure.
  *
- *     IN:     vp      - vnode of file.
+ *     IN:     ip      - inode of file.
  *             vap     - va_mask identifies requested attributes.
- *                       If AT_XVATTR set, then optional attrs are requested
+ *                       If ATTR_XVATTR set, then optional attrs are requested
  *             flags   - ATTR_NOACLCHECK (CIFS server context)
  *             cr      - credentials of caller.
- *             ct      - caller context
  *
  *     OUT:    vap     - attribute values.
  *
  *     RETURN: 0 (always succeeds)
  */
 /* ARGSUSED */
-static int
-zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_getattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int     error = 0;
        uint64_t links;
        uint64_t mtime[2], ctime[2];
@@ -2381,14 +2183,16 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
        sa_bulk_attr_t bulk[2];
        int count = 0;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16);
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16);
+       zfs_fuid_map_ids(zp, cr, &vap->va_uid, &vap->va_gid);
+
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
 
        if ((error = sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) != 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -2397,10 +2201,11 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
         * Also, if we are the owner don't bother, since owner should
         * always be allowed to read basic attributes of file.
         */
-       if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) && (zp->z_uid != crgetuid(cr))) {
-               if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
-                   skipaclchk, cr)) {
-                       ZFS_EXIT(zfsvfs);
+       if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) &&
+           (vap->va_uid != crgetuid(cr))) {
+               if ((error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
+                   skipaclchk, cr))) {
+                       ZFS_EXIT(zsb);
                        return (error);
                }
        }
@@ -2411,26 +2216,24 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
         */
 
        mutex_enter(&zp->z_lock);
-       vap->va_type = vp->v_type;
-       vap->va_mode = zp->z_mode & MODEMASK;
-       vap->va_uid = zp->z_uid;
-       vap->va_gid = zp->z_gid;
-       vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev;
+       vap->va_type = vn_mode_to_vtype(zp->z_mode);
+       vap->va_mode = zp->z_mode;
+       vap->va_fsid = ZTOI(zp)->i_sb->s_dev;
        vap->va_nodeid = zp->z_id;
-       if ((vp->v_flag & VROOT) && zfs_show_ctldir(zp))
+       if ((zp->z_id == zsb->z_root) && zfs_show_ctldir(zp))
                links = zp->z_links + 1;
        else
                links = zp->z_links;
-       vap->va_nlink = MIN(links, UINT32_MAX); /* nlink_t limit! */
-       vap->va_size = zp->z_size;
-       vap->va_rdev = vp->v_rdev;
-       vap->va_seq = zp->z_seq;
+       vap->va_nlink = MIN(links, ZFS_LINK_MAX);
+       vap->va_size = i_size_read(ip);
+       vap->va_rdev = ip->i_rdev;
+       vap->va_seq = ip->i_generation;
 
        /*
         * Add in any requested optional attributes and the create time.
         * Also set the corresponding bits in the returned attribute bitmap.
         */
-       if ((xoap = xva_getxoptattr(xvap)) != NULL && zfsvfs->z_use_fuids) {
+       if ((xoap = xva_getxoptattr(xvap)) != NULL && zsb->z_use_fuids) {
                if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
                        xoap->xoa_archive =
                            ((zp->z_pflags & ZFS_ARCHIVE) != 0);
@@ -2498,14 +2301,14 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
                }
 
                if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) &&
-                   vp->v_type == VREG) {
+                   S_ISREG(ip->i_mode)) {
                        zfs_sa_get_scanstamp(zp, xvap);
                }
 
                if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
                        uint64_t times[2];
 
-                       (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_CRTIME(zfsvfs),
+                       (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_CRTIME(zsb),
                            times, sizeof (times));
                        ZFS_TIME_DECODE(&xoap->xoa_createtime, times);
                        XVA_SET_RTN(xvap, XAT_CREATETIME);
@@ -2515,6 +2318,22 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
                        xoap->xoa_reparse = ((zp->z_pflags & ZFS_REPARSE) != 0);
                        XVA_SET_RTN(xvap, XAT_REPARSE);
                }
+               if (XVA_ISSET_REQ(xvap, XAT_GEN)) {
+                       xoap->xoa_generation = zp->z_gen;
+                       XVA_SET_RTN(xvap, XAT_GEN);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) {
+                       xoap->xoa_offline =
+                           ((zp->z_pflags & ZFS_OFFLINE) != 0);
+                       XVA_SET_RTN(xvap, XAT_OFFLINE);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) {
+                       xoap->xoa_sparse =
+                           ((zp->z_pflags & ZFS_SPARSE) != 0);
+                       XVA_SET_RTN(xvap, XAT_SPARSE);
+               }
        }
 
        ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime);
@@ -2529,48 +2348,90 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
                /*
                 * Block size hasn't been set; suggest maximal I/O transfers.
                 */
-               vap->va_blksize = zfsvfs->z_max_blksz;
+               vap->va_blksize = zsb->z_max_blksz;
+       }
+
+       ZFS_EXIT(zsb);
+       return (0);
+}
+EXPORT_SYMBOL(zfs_getattr);
+
+/*
+ * Get the basic file attributes and place them in the provided kstat
+ * structure.  The inode is assumed to be the authoritative source
+ * for most of the attributes.  However, the znode currently has the
+ * authoritative atime, blksize, and block count.
+ *
+ *     IN:     ip      - inode of file.
+ *
+ *     OUT:    sp      - kstat values.
+ *
+ *     RETURN: 0 (always succeeds)
+ */
+/* ARGSUSED */
+int
+zfs_getattr_fast(struct inode *ip, struct kstat *sp)
+{
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
+
+       ZFS_ENTER(zsb);
+       ZFS_VERIFY_ZP(zp);
+
+       mutex_enter(&zp->z_lock);
+
+       generic_fillattr(ip, sp);
+       ZFS_TIME_DECODE(&sp->atime, zp->z_atime);
+
+       sa_object_size(zp->z_sa_hdl, (uint32_t *)&sp->blksize, &sp->blocks);
+       if (unlikely(zp->z_blksz == 0)) {
+               /*
+                * Block size hasn't been set; suggest maximal I/O transfers.
+                */
+               sp->blksize = zsb->z_max_blksz;
        }
 
-       ZFS_EXIT(zfsvfs);
+       mutex_exit(&zp->z_lock);
+
+       ZFS_EXIT(zsb);
+
        return (0);
 }
+EXPORT_SYMBOL(zfs_getattr_fast);
 
 /*
  * Set the file attributes to the values contained in the
  * vattr structure.
  *
- *     IN:     vp      - vnode of file to be modified.
+ *     IN:     ip      - inode of file to be modified.
  *             vap     - new attribute values.
- *                       If AT_XVATTR set, then optional attrs are being set
+ *                       If ATTR_XVATTR set, then optional attrs are being set
  *             flags   - ATTR_UTIME set if non-default time values provided.
  *                     - ATTR_NOACLCHECK (CIFS context only).
  *             cr      - credentials of caller.
- *             ct      - caller context
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     vp - ctime updated, mtime updated if size changed.
+ *     ip - ctime updated, mtime updated if size changed.
  */
 /* ARGSUSED */
-static int
-zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
-       caller_context_t *ct)
+int
+zfs_setattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        zilog_t         *zilog;
        dmu_tx_t        *tx;
        vattr_t         oldva;
-       xvattr_t        tmpxvattr;
+       xvattr_t        *tmpxvattr;
        uint_t          mask = vap->va_mask;
        uint_t          saved_mask;
        int             trim_mask = 0;
        uint64_t        new_mode;
        uint64_t        new_uid, new_gid;
-       uint64_t        xattr_obj = 0;
+       uint64_t        xattr_obj;
        uint64_t        mtime[2], ctime[2];
        znode_t         *attrzp;
        int             need_policy = FALSE;
@@ -2578,43 +2439,40 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
        zfs_fuid_info_t *fuidp = NULL;
        xvattr_t *xvap = (xvattr_t *)vap;       /* vap may be an xvattr_t * */
        xoptattr_t      *xoap;
-       zfs_acl_t       *aclp = NULL;
+       zfs_acl_t       *aclp;
        boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
        boolean_t       fuid_dirtied = B_FALSE;
-       sa_bulk_attr_t  bulk[7], xattr_bulk[7];
+       sa_bulk_attr_t  *bulk, *xattr_bulk;
        int             count = 0, xattr_count = 0;
 
        if (mask == 0)
                return (0);
 
-       if (mask & AT_NOSET)
-               return (EINVAL);
-
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
        /*
         * Make sure that if we have ephemeral uid/gid or xvattr specified
         * that file system is at proper version level
         */
 
-       if (zfsvfs->z_use_fuids == B_FALSE &&
-           (((mask & AT_UID) && IS_EPHEMERAL(vap->va_uid)) ||
-           ((mask & AT_GID) && IS_EPHEMERAL(vap->va_gid)) ||
-           (mask & AT_XVATTR))) {
-               ZFS_EXIT(zfsvfs);
+       if (zsb->z_use_fuids == B_FALSE &&
+           (((mask & ATTR_UID) && IS_EPHEMERAL(vap->va_uid)) ||
+           ((mask & ATTR_GID) && IS_EPHEMERAL(vap->va_gid)) ||
+           (mask & ATTR_XVATTR))) {
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
-       if (mask & AT_SIZE && vp->v_type == VDIR) {
-               ZFS_EXIT(zfsvfs);
+       if (mask & ATTR_SIZE && S_ISDIR(ip->i_mode)) {
+               ZFS_EXIT(zsb);
                return (EISDIR);
        }
 
-       if (mask & AT_SIZE && vp->v_type != VREG && vp->v_type != VFIFO) {
-               ZFS_EXIT(zfsvfs);
+       if (mask & ATTR_SIZE && !S_ISREG(ip->i_mode) && !S_ISFIFO(ip->i_mode)) {
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
@@ -2624,21 +2482,25 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
         */
        xoap = xva_getxoptattr(xvap);
 
-       xva_init(&tmpxvattr);
+       tmpxvattr = kmem_alloc(sizeof(xvattr_t), KM_SLEEP);
+       xva_init(tmpxvattr);
+
+       bulk = kmem_alloc(sizeof(sa_bulk_attr_t) * 7, KM_SLEEP);
+       xattr_bulk = kmem_alloc(sizeof(sa_bulk_attr_t) * 7, KM_SLEEP);
 
        /*
         * Immutable files can only alter immutable bit and atime
         */
        if ((zp->z_pflags & ZFS_IMMUTABLE) &&
-           ((mask & (AT_SIZE|AT_UID|AT_GID|AT_MTIME|AT_MODE)) ||
-           ((mask & AT_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) {
-               ZFS_EXIT(zfsvfs);
-               return (EPERM);
+           ((mask & (ATTR_SIZE|ATTR_UID|ATTR_GID|ATTR_MTIME|ATTR_MODE)) ||
+           ((mask & ATTR_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) {
+               err = EPERM;
+               goto out3;
        }
 
-       if ((mask & AT_SIZE) && (zp->z_pflags & ZFS_READONLY)) {
-               ZFS_EXIT(zfsvfs);
-               return (EPERM);
+       if ((mask & ATTR_SIZE) && (zp->z_pflags & ZFS_READONLY)) {
+               err = EPERM;
+               goto out3;
        }
 
        /*
@@ -2647,33 +2509,35 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
         * handle times greater than 2039.  This check should be removed
         * once large timestamps are fully supported.
         */
-       if (mask & (AT_ATIME | AT_MTIME)) {
-               if (((mask & AT_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) ||
-                   ((mask & AT_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) {
-                       ZFS_EXIT(zfsvfs);
-                       return (EOVERFLOW);
+       if (mask & (ATTR_ATIME | ATTR_MTIME)) {
+               if (((mask & ATTR_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) ||
+                   ((mask & ATTR_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) {
+                       err = EOVERFLOW;
+                       goto out3;
                }
        }
 
 top:
        attrzp = NULL;
+       aclp = NULL;
 
        /* Can this be moved to before the top label? */
-       if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) {
-               ZFS_EXIT(zfsvfs);
-               return (EROFS);
+       if (zfs_is_readonly(zsb)) {
+               err = EROFS;
+               goto out3;
        }
 
        /*
         * First validate permissions
         */
 
-       if (mask & AT_SIZE) {
+       if (mask & ATTR_SIZE) {
                err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr);
-               if (err) {
-                       ZFS_EXIT(zfsvfs);
-                       return (err);
-               }
+               if (err)
+                       goto out3;
+
+               truncate_setsize(ip, vap->va_size);
+
                /*
                 * XXX - Note, we are not providing any open
                 * mode flags here (like FNDELAY), so we may
@@ -2682,24 +2546,24 @@ top:
                 */
                /* XXX - would it be OK to generate a log record here? */
                err = zfs_freesp(zp, vap->va_size, 0, 0, FALSE);
-               if (err) {
-                       ZFS_EXIT(zfsvfs);
-                       return (err);
-               }
+               if (err)
+                       goto out3;
        }
 
-       if (mask & (AT_ATIME|AT_MTIME) ||
-           ((mask & AT_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) ||
+       if (mask & (ATTR_ATIME|ATTR_MTIME) ||
+           ((mask & ATTR_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) ||
            XVA_ISSET_REQ(xvap, XAT_READONLY) ||
            XVA_ISSET_REQ(xvap, XAT_ARCHIVE) ||
+           XVA_ISSET_REQ(xvap, XAT_OFFLINE) ||
+           XVA_ISSET_REQ(xvap, XAT_SPARSE) ||
            XVA_ISSET_REQ(xvap, XAT_CREATETIME) ||
            XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) {
                need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0,
                    skipaclchk, cr);
        }
 
-       if (mask & (AT_UID|AT_GID)) {
-               int     idmask = (mask & (AT_UID|AT_GID));
+       if (mask & (ATTR_UID|ATTR_GID)) {
+               int     idmask = (mask & (ATTR_UID|ATTR_GID));
                int     take_owner;
                int     take_group;
 
@@ -2708,19 +2572,19 @@ top:
                 * we may clear S_ISUID/S_ISGID bits.
                 */
 
-               if (!(mask & AT_MODE))
+               if (!(mask & ATTR_MODE))
                        vap->va_mode = zp->z_mode;
 
                /*
                 * Take ownership or chgrp to group we are a member of
                 */
 
-               take_owner = (mask & AT_UID) && (vap->va_uid == crgetuid(cr));
-               take_group = (mask & AT_GID) &&
-                   zfs_groupmember(zfsvfs, vap->va_gid, cr);
+               take_owner = (mask & ATTR_UID) && (vap->va_uid == crgetuid(cr));
+               take_group = (mask & ATTR_GID) &&
+                   zfs_groupmember(zsb, vap->va_gid, cr);
 
                /*
-                * If both AT_UID and AT_GID are set then take_owner and
+                * If both ATTR_UID and ATTR_GID are set then take_owner and
                 * take_group must both be set in order to allow taking
                 * ownership.
                 *
@@ -2728,16 +2592,17 @@ top:
                 *
                 */
 
-               if (((idmask == (AT_UID|AT_GID)) && take_owner && take_group) ||
-                   ((idmask == AT_UID) && take_owner) ||
-                   ((idmask == AT_GID) && take_group)) {
+               if (((idmask == (ATTR_UID|ATTR_GID)) &&
+                   take_owner && take_group) ||
+                   ((idmask == ATTR_UID) && take_owner) ||
+                   ((idmask == ATTR_GID) && take_group)) {
                        if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0,
                            skipaclchk, cr) == 0) {
                                /*
                                 * Remove setuid/setgid for non-privileged users
                                 */
-                               secpolicy_setid_clear(vap, cr);
-                               trim_mask = (mask & (AT_UID|AT_GID));
+                               (void) secpolicy_setid_clear(vap, cr);
+                               trim_mask = (mask & (ATTR_UID|ATTR_GID));
                        } else {
                                need_policy =  TRUE;
                        }
@@ -2748,9 +2613,8 @@ top:
 
        mutex_enter(&zp->z_lock);
        oldva.va_mode = zp->z_mode;
-       oldva.va_uid = zp->z_uid;
-       oldva.va_gid = zp->z_gid;
-       if (mask & AT_XVATTR) {
+       zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid);
+       if (mask & ATTR_XVATTR) {
                /*
                 * Update xvattr mask to include only those attributes
                 * that are actually changing.
@@ -2764,7 +2628,7 @@ top:
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_APPENDONLY);
-                               XVA_SET_REQ(&tmpxvattr, XAT_APPENDONLY);
+                               XVA_SET_REQ(tmpxvattr, XAT_APPENDONLY);
                        }
                }
 
@@ -2774,7 +2638,7 @@ top:
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_NOUNLINK);
-                               XVA_SET_REQ(&tmpxvattr, XAT_NOUNLINK);
+                               XVA_SET_REQ(tmpxvattr, XAT_NOUNLINK);
                        }
                }
 
@@ -2784,7 +2648,7 @@ top:
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_IMMUTABLE);
-                               XVA_SET_REQ(&tmpxvattr, XAT_IMMUTABLE);
+                               XVA_SET_REQ(tmpxvattr, XAT_IMMUTABLE);
                        }
                }
 
@@ -2794,7 +2658,7 @@ top:
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_NODUMP);
-                               XVA_SET_REQ(&tmpxvattr, XAT_NODUMP);
+                               XVA_SET_REQ(tmpxvattr, XAT_NODUMP);
                        }
                }
 
@@ -2804,26 +2668,26 @@ top:
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_AV_MODIFIED);
-                               XVA_SET_REQ(&tmpxvattr, XAT_AV_MODIFIED);
+                               XVA_SET_REQ(tmpxvattr, XAT_AV_MODIFIED);
                        }
                }
 
                if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) {
-                       if ((vp->v_type != VREG &&
+                       if ((!S_ISREG(ip->i_mode) &&
                            xoap->xoa_av_quarantined) ||
                            xoap->xoa_av_quarantined !=
                            ((zp->z_pflags & ZFS_AV_QUARANTINED) != 0)) {
                                need_policy = TRUE;
                        } else {
                                XVA_CLR_REQ(xvap, XAT_AV_QUARANTINED);
-                               XVA_SET_REQ(&tmpxvattr, XAT_AV_QUARANTINED);
+                               XVA_SET_REQ(tmpxvattr, XAT_AV_QUARANTINED);
                        }
                }
 
                if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) {
                        mutex_exit(&zp->z_lock);
-                       ZFS_EXIT(zfsvfs);
-                       return (EPERM);
+                       err = EPERM;
+                       goto out3;
                }
 
                if (need_policy == FALSE &&
@@ -2835,15 +2699,14 @@ top:
 
        mutex_exit(&zp->z_lock);
 
-       if (mask & AT_MODE) {
+       if (mask & ATTR_MODE) {
                if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) {
-                       err = secpolicy_setid_setsticky_clear(vp, vap,
+                       err = secpolicy_setid_setsticky_clear(ip, vap,
                            &oldva, cr);
-                       if (err) {
-                               ZFS_EXIT(zfsvfs);
-                               return (err);
-                       }
-                       trim_mask |= AT_MODE;
+                       if (err)
+                               goto out3;
+
+                       trim_mask |= ATTR_MODE;
                } else {
                        need_policy = TRUE;
                }
@@ -2862,12 +2725,10 @@ top:
                        saved_mask = vap->va_mask;
                        vap->va_mask &= ~trim_mask;
                }
-               err = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags,
+               err = secpolicy_vnode_setattr(cr, ip, vap, &oldva, flags,
                    (int (*)(void *, int, cred_t *))zfs_zaccess_unix, zp);
-               if (err) {
-                       ZFS_EXIT(zfsvfs);
-                       return (err);
-               }
+               if (err)
+                       goto out3;
 
                if (trim_mask)
                        vap->va_mask |= saved_mask;
@@ -2879,68 +2740,73 @@ top:
         */
        mask = vap->va_mask;
 
-       if ((mask & (AT_UID | AT_GID))) {
-               (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &xattr_obj,
-                   sizeof (xattr_obj));
+       if ((mask & (ATTR_UID | ATTR_GID))) {
+               err = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zsb),
+                   &xattr_obj, sizeof (xattr_obj));
 
-               if (xattr_obj) {
-                       err = zfs_zget(zp->z_zfsvfs, xattr_obj, &attrzp);
+               if (err == 0 && xattr_obj) {
+                       err = zfs_zget(ZTOZSB(zp), xattr_obj, &attrzp);
                        if (err)
                                goto out2;
                }
-               if (mask & AT_UID) {
-                       new_uid = zfs_fuid_create(zfsvfs,
+               if (mask & ATTR_UID) {
+                       new_uid = zfs_fuid_create(zsb,
                            (uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp);
-                       if (vap->va_uid != zp->z_uid &&
-                           zfs_fuid_overquota(zfsvfs, B_FALSE, new_uid)) {
+                       if (new_uid != zp->z_uid &&
+                           zfs_fuid_overquota(zsb, B_FALSE, new_uid)) {
+                               if (attrzp)
+                                       iput(ZTOI(attrzp));
                                err = EDQUOT;
                                goto out2;
                        }
                }
 
-               if (mask & AT_GID) {
-                       new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid,
+               if (mask & ATTR_GID) {
+                       new_gid = zfs_fuid_create(zsb, (uint64_t)vap->va_gid,
                            cr, ZFS_GROUP, &fuidp);
                        if (new_gid != zp->z_gid &&
-                           zfs_fuid_overquota(zfsvfs, B_TRUE, new_gid)) {
+                           zfs_fuid_overquota(zsb, B_TRUE, new_gid)) {
+                               if (attrzp)
+                                       iput(ZTOI(attrzp));
                                err = EDQUOT;
                                goto out2;
                        }
                }
        }
-       tx = dmu_tx_create(zfsvfs->z_os);
+       tx = dmu_tx_create(zsb->z_os);
 
-       if (mask & AT_MODE) {
+       if (mask & ATTR_MODE) {
                uint64_t pmode = zp->z_mode;
+               uint64_t acl_obj;
                new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT);
 
-               if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode))
-                       goto out;
+               zfs_acl_chmod_setattr(zp, &aclp, new_mode);
 
-               if (!zp->z_is_sa && ZFS_EXTERNAL_ACL(zp)) {
+               mutex_enter(&zp->z_lock);
+               if (!zp->z_is_sa && ((acl_obj = zfs_external_acl(zp)) != 0)) {
                        /*
                         * Are we upgrading ACL from old V0 format
                         * to V1 format?
                         */
-                       if (zfsvfs->z_version <= ZPL_VERSION_FUID &&
-                           ZNODE_ACL_VERSION(zp) ==
+                       if (zsb->z_version >= ZPL_VERSION_FUID &&
+                           zfs_znode_acl_version(zp) ==
                            ZFS_ACL_VERSION_INITIAL) {
-                               dmu_tx_hold_free(tx,
-                                   ZFS_EXTERNAL_ACL(zp), 0,
+                               dmu_tx_hold_free(tx, acl_obj, 0,
                                    DMU_OBJECT_END);
                                dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
                                    0, aclp->z_acl_bytes);
                        } else {
-                               dmu_tx_hold_write(tx, ZFS_EXTERNAL_ACL(zp), 0,
+                               dmu_tx_hold_write(tx, acl_obj, 0,
                                    aclp->z_acl_bytes);
                        }
                } else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) {
                        dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
                            0, aclp->z_acl_bytes);
                }
+               mutex_exit(&zp->z_lock);
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
        } else {
-               if ((mask & AT_XVATTR) &&
+               if ((mask & ATTR_XVATTR) &&
                    XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
                        dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
                else
@@ -2951,9 +2817,9 @@ top:
                dmu_tx_hold_sa(tx, attrzp->z_sa_hdl, B_FALSE);
        }
 
-       fuid_dirtied = zfsvfs->z_fuid_dirty;
+       fuid_dirtied = zsb->z_fuid_dirty;
        if (fuid_dirtied)
-               zfs_fuid_txhold(zfsvfs, tx);
+               zfs_fuid_txhold(zsb, tx);
 
        zfs_sa_upgrade_txholds(tx, zp);
 
@@ -2973,47 +2839,50 @@ top:
         * updated as a side-effect of calling this function.
         */
 
+
+       if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))
+               mutex_enter(&zp->z_acl_lock);
        mutex_enter(&zp->z_lock);
 
-       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
+       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zsb), NULL,
            &zp->z_pflags, sizeof (zp->z_pflags));
 
        if (attrzp) {
+               if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))
+                       mutex_enter(&attrzp->z_acl_lock);
                mutex_enter(&attrzp->z_lock);
                SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
-                   SA_ZPL_FLAGS(zfsvfs), NULL, &attrzp->z_pflags,
+                   SA_ZPL_FLAGS(zsb), NULL, &attrzp->z_pflags,
                    sizeof (attrzp->z_pflags));
        }
 
-       if (mask & (AT_UID|AT_GID)) {
+       if (mask & (ATTR_UID|ATTR_GID)) {
 
-               if (mask & AT_UID) {
-                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL,
+               if (mask & ATTR_UID) {
+                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zsb), NULL,
                            &new_uid, sizeof (new_uid));
-                       zp->z_uid = zfs_fuid_map_id(zfsvfs, new_uid,
-                           cr, ZFS_OWNER);
+                       zp->z_uid = new_uid;
                        if (attrzp) {
                                SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
-                                   SA_ZPL_UID(zfsvfs), NULL, &new_uid,
+                                   SA_ZPL_UID(zsb), NULL, &new_uid,
                                    sizeof (new_uid));
-                               attrzp->z_uid = zp->z_uid;
+                               attrzp->z_uid = new_uid;
                        }
                }
 
-               if (mask & AT_GID) {
-                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs),
+               if (mask & ATTR_GID) {
+                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zsb),
                            NULL, &new_gid, sizeof (new_gid));
-                       zp->z_gid = zfs_fuid_map_id(zfsvfs, new_gid, cr,
-                           ZFS_GROUP);
+                       zp->z_gid = new_gid;
                        if (attrzp) {
                                SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
-                                   SA_ZPL_GID(zfsvfs), NULL, &new_gid,
+                                   SA_ZPL_GID(zsb), NULL, &new_gid,
                                    sizeof (new_gid));
-                               attrzp->z_gid = zp->z_gid;
+                               attrzp->z_gid = new_gid;
                        }
                }
-               if (!(mask & AT_MODE)) {
-                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs),
+               if (!(mask & ATTR_MODE)) {
+                       SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zsb),
                            NULL, &new_mode, sizeof (new_mode));
                        new_mode = zp->z_mode;
                }
@@ -3025,50 +2894,48 @@ top:
                }
        }
 
-       if (mask & AT_MODE) {
-               mutex_enter(&zp->z_acl_lock);
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL,
+       if (mask & ATTR_MODE) {
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zsb), NULL,
                    &new_mode, sizeof (new_mode));
                zp->z_mode = new_mode;
-               ASSERT3U((uintptr_t)aclp, !=, NULL);
+               ASSERT3P(aclp, !=, NULL);
                err = zfs_aclset_common(zp, aclp, cr, tx);
-               ASSERT3U(err, ==, 0);
+               ASSERT0(err);
+               if (zp->z_acl_cached)
+                       zfs_acl_free(zp->z_acl_cached);
                zp->z_acl_cached = aclp;
                aclp = NULL;
-               mutex_exit(&zp->z_acl_lock);
        }
 
-       if (attrzp)
-               mutex_exit(&attrzp->z_lock);
 
-       if (mask & AT_ATIME) {
+       if (mask & ATTR_ATIME) {
                ZFS_TIME_ENCODE(&vap->va_atime, zp->z_atime);
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL,
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zsb), NULL,
                    &zp->z_atime, sizeof (zp->z_atime));
        }
 
-       if (mask & AT_MTIME) {
+       if (mask & ATTR_MTIME) {
                ZFS_TIME_ENCODE(&vap->va_mtime, mtime);
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL,
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL,
                    mtime, sizeof (mtime));
        }
 
        /* XXX - shouldn't this be done *before* the ATIME/MTIME checks? */
-       if (mask & AT_SIZE && !(mask & AT_MTIME)) {
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs),
+       if (mask & ATTR_SIZE && !(mask & ATTR_MTIME)) {
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb),
                    NULL, mtime, sizeof (mtime));
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL,
                    &ctime, sizeof (ctime));
                zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime,
                    B_TRUE);
        } else if (mask != 0) {
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
+               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL,
                    &ctime, sizeof (ctime));
                zfs_tstamp_update_setup(zp, STATE_CHANGED, mtime, ctime,
                    B_TRUE);
                if (attrzp) {
                        SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
-                           SA_ZPL_CTIME(zfsvfs), NULL,
+                           SA_ZPL_CTIME(zsb), NULL,
                            &ctime, sizeof (ctime));
                        zfs_tstamp_update_setup(attrzp, STATE_CHANGED,
                            mtime, ctime, B_TRUE);
@@ -3079,46 +2946,53 @@ top:
         * update from toggling bit
         */
 
-       if (xoap && (mask & AT_XVATTR)) {
+       if (xoap && (mask & ATTR_XVATTR)) {
 
                /*
                 * restore trimmed off masks
                 * so that return masks can be set for caller.
                 */
 
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_APPENDONLY)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_APPENDONLY)) {
                        XVA_SET_REQ(xvap, XAT_APPENDONLY);
                }
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_NOUNLINK)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_NOUNLINK)) {
                        XVA_SET_REQ(xvap, XAT_NOUNLINK);
                }
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_IMMUTABLE)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_IMMUTABLE)) {
                        XVA_SET_REQ(xvap, XAT_IMMUTABLE);
                }
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_NODUMP)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_NODUMP)) {
                        XVA_SET_REQ(xvap, XAT_NODUMP);
                }
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_AV_MODIFIED)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_AV_MODIFIED)) {
                        XVA_SET_REQ(xvap, XAT_AV_MODIFIED);
                }
-               if (XVA_ISSET_REQ(&tmpxvattr, XAT_AV_QUARANTINED)) {
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_AV_QUARANTINED)) {
                        XVA_SET_REQ(xvap, XAT_AV_QUARANTINED);
                }
 
                if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
-                       ASSERT(vp->v_type == VREG);
+                       ASSERT(S_ISREG(ip->i_mode));
 
                zfs_xvattr_set(zp, xvap, tx);
        }
 
        if (fuid_dirtied)
-               zfs_fuid_sync(zfsvfs, tx);
+               zfs_fuid_sync(zsb, tx);
 
        if (mask != 0)
                zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp);
 
        mutex_exit(&zp->z_lock);
+       if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))
+               mutex_exit(&zp->z_acl_lock);
 
+       if (attrzp) {
+               if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))
+                       mutex_exit(&attrzp->z_acl_lock);
+               mutex_exit(&attrzp->z_lock);
+       }
 out:
        if (err == 0 && attrzp) {
                err2 = sa_bulk_update(attrzp->z_sa_hdl, xattr_bulk,
@@ -3127,7 +3001,7 @@ out:
        }
 
        if (attrzp)
-               VN_RELE(ZTOV(attrzp));
+               iput(ZTOI(attrzp));
        if (aclp)
                zfs_acl_free(aclp);
 
@@ -3143,16 +3017,21 @@ out:
        } else {
                err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
                dmu_tx_commit(tx);
+               zfs_inode_update(zp);
        }
 
-
 out2:
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
-
-       ZFS_EXIT(zfsvfs);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
+
+out3:
+       kmem_free(xattr_bulk, sizeof(sa_bulk_attr_t) * 7);
+       kmem_free(bulk, sizeof(sa_bulk_attr_t) * 7);
+       kmem_free(tmpxvattr, sizeof(xvattr_t));
+       ZFS_EXIT(zsb);
        return (err);
 }
+EXPORT_SYMBOL(zfs_setattr);
 
 typedef struct zfs_zlock {
        krwlock_t       *zl_rwlock;     /* lock we acquired */
@@ -3170,7 +3049,7 @@ zfs_rename_unlock(zfs_zlock_t **zlpp)
 
        while ((zl = *zlpp) != NULL) {
                if (zl->zl_znode != NULL)
-                       VN_RELE(ZTOV(zl->zl_znode));
+                       iput(ZTOI(zl->zl_znode));
                rw_exit(zl->zl_rwlock);
                *zlpp = zl->zl_next;
                kmem_free(zl, sizeof (*zl));
@@ -3188,7 +3067,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
 {
        zfs_zlock_t     *zl;
        znode_t         *zp = tdzp;
-       uint64_t        rootid = zp->z_zfsvfs->z_root;
+       uint64_t        rootid = ZTOZSB(zp)->z_root;
        uint64_t        oidp = zp->z_id;
        krwlock_t       *rwlp = &szp->z_parent_lock;
        krw_t           rw = RW_WRITER;
@@ -3236,12 +3115,12 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
                        return (0);
 
                if (rw == RW_READER) {          /* i.e. not the first pass */
-                       int error = zfs_zget(zp->z_zfsvfs, oidp, &zp);
+                       int error = zfs_zget(ZTOZSB(zp), oidp, &zp);
                        if (error)
                                return (error);
                        zl->zl_znode = zp;
                }
-               (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zp->z_zfsvfs),
+               (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(ZTOZSB(zp)),
                    &oidp, sizeof (oidp));
                rwlp = &zp->z_parent_lock;
                rw = RW_READER;
@@ -3255,30 +3134,28 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
  * Move an entry from the provided source directory to the target
  * directory.  Change the entry name as indicated.
  *
- *     IN:     sdvp    - Source directory containing the "old entry".
+ *     IN:     sdip    - Source directory containing the "old entry".
  *             snm     - Old entry name.
- *             tdvp    - Target directory to contain the "new entry".
+ *             tdip    - Target directory to contain the "new entry".
  *             tnm     - New entry name.
  *             cr      - credentials of caller.
- *             ct      - caller context
  *             flags   - case flags
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     sdvp,tdvp - ctime|mtime updated
+ *     sdip,tdip - ctime|mtime updated
  */
 /*ARGSUSED*/
-static int
-zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
-    caller_context_t *ct, int flags)
+int
+zfs_rename(struct inode *sdip, char *snm, struct inode *tdip, char *tnm,
+    cred_t *cr, int flags)
 {
        znode_t         *tdzp, *szp, *tzp;
-       znode_t         *sdzp = VTOZ(sdvp);
-       zfsvfs_t        *zfsvfs = sdzp->z_zfsvfs;
+       znode_t         *sdzp = ITOZ(sdip);
+       zfs_sb_t        *zsb = ITOZSB(sdip);
        zilog_t         *zilog;
-       vnode_t         *realvp;
        zfs_dirlock_t   *sdl, *tdl;
        dmu_tx_t        *tx;
        zfs_zlock_t     *zl;
@@ -3286,26 +3163,20 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
        int             error = 0;
        int             zflg = 0;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(sdzp);
-       zilog = zfsvfs->z_log;
-
-       /*
-        * Make sure we have the real vp for the target directory.
-        */
-       if (VOP_REALVP(tdvp, &realvp, ct) == 0)
-               tdvp = realvp;
+       zilog = zsb->z_log;
 
-       if (tdvp->v_vfsp != sdvp->v_vfsp || zfsctl_is_node(tdvp)) {
-               ZFS_EXIT(zfsvfs);
+       if (tdip->i_sb != sdip->i_sb) {
+               ZFS_EXIT(zsb);
                return (EXDEV);
        }
 
-       tdzp = VTOZ(tdvp);
+       tdzp = ITOZ(tdip);
        ZFS_VERIFY_ZP(tdzp);
-       if (zfsvfs->z_utf8 && u8_validate(tnm,
+       if (zsb->z_utf8 && u8_validate(tnm,
            strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
 
@@ -3323,7 +3194,7 @@ top:
         * See the comment in zfs_link() for why this is considered bad.
         */
        if ((tdzp->z_pflags & ZFS_XATTR) != (sdzp->z_pflags & ZFS_XATTR)) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
@@ -3342,10 +3213,10 @@ top:
                 * First compare the two name arguments without
                 * considering any case folding.
                 */
-               int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER);
+               int nofold = (zsb->z_norm & ~U8_TEXTPREP_TOUPPER);
 
                cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error);
-               ASSERT(error == 0 || !zfsvfs->z_utf8);
+               ASSERT(error == 0 || !zsb->z_utf8);
                if (cmp == 0) {
                        /*
                         * POSIX: "If the old argument and the new argument
@@ -3353,7 +3224,7 @@ top:
                         * the rename() function shall return successfully
                         * and perform no other action."
                         */
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (0);
                }
                /*
@@ -3374,10 +3245,10 @@ top:
                 * is an exact match, we will allow this to proceed as
                 * a name-change request.
                 */
-               if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE ||
-                   (zfsvfs->z_case == ZFS_CASE_MIXED &&
+               if ((zsb->z_case == ZFS_CASE_INSENSITIVE ||
+                   (zsb->z_case == ZFS_CASE_MIXED &&
                    flags & FIGNORECASE)) &&
-                   u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST,
+                   u8_strcmp(snm, tnm, 0, zsb->z_norm, U8_UNICODE_LATEST,
                    &error) == 0) {
                        /*
                         * case preserving rename request, require exact
@@ -3417,7 +3288,7 @@ top:
                if (!terr) {
                        zfs_dirent_unlock(tdl);
                        if (tzp)
-                               VN_RELE(ZTOV(tzp));
+                               iput(ZTOI(tzp));
                }
 
                if (sdzp == tdzp)
@@ -3425,19 +3296,19 @@ top:
 
                if (strcmp(snm, "..") == 0)
                        serr = EINVAL;
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (serr);
        }
        if (terr) {
                zfs_dirent_unlock(sdl);
-               VN_RELE(ZTOV(szp));
+               iput(ZTOI(szp));
 
                if (sdzp == tdzp)
                        rw_exit(&sdzp->z_name_lock);
 
                if (strcmp(tnm, "..") == 0)
                        terr = EINVAL;
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (terr);
        }
 
@@ -3448,15 +3319,15 @@ top:
         * done in a single check.
         */
 
-       if (error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr))
+       if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)))
                goto out;
 
-       if (ZTOV(szp)->v_type == VDIR) {
+       if (S_ISDIR(ZTOI(szp)->i_mode)) {
                /*
                 * Check to make sure rename is valid.
                 * Can't do a move like this: /usr/a/b to /usr/a/b/c/d
                 */
-               if (error = zfs_rename_lock(szp, tdzp, sdzp, &zl))
+               if ((error = zfs_rename_lock(szp, tdzp, sdzp, &zl)))
                        goto out;
        }
 
@@ -3467,13 +3338,13 @@ top:
                /*
                 * Source and target must be the same type.
                 */
-               if (ZTOV(szp)->v_type == VDIR) {
-                       if (ZTOV(tzp)->v_type != VDIR) {
+               if (S_ISDIR(ZTOI(szp)->i_mode)) {
+                       if (!S_ISDIR(ZTOI(tzp)->i_mode)) {
                                error = ENOTDIR;
                                goto out;
                        }
                } else {
-                       if (ZTOV(tzp)->v_type == VDIR) {
+                       if (S_ISDIR(ZTOI(tzp)->i_mode)) {
                                error = EISDIR;
                                goto out;
                        }
@@ -3489,19 +3360,7 @@ top:
                }
        }
 
-       vnevent_rename_src(ZTOV(szp), sdvp, snm, ct);
-       if (tzp)
-               vnevent_rename_dest(ZTOV(tzp), tdvp, tnm, ct);
-
-       /*
-        * notify the target directory if it is not the same
-        * as source directory.
-        */
-       if (tdvp != sdvp) {
-               vnevent_rename_dest_dir(tdvp, ct);
-       }
-
-       tx = dmu_tx_create(zfsvfs->z_os);
+       tx = dmu_tx_create(zsb->z_os);
        dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE);
        dmu_tx_hold_sa(tx, sdzp->z_sa_hdl, B_FALSE);
        dmu_tx_hold_zap(tx, sdzp->z_id, FALSE, snm);
@@ -3516,7 +3375,7 @@ top:
        }
 
        zfs_sa_upgrade_txholds(tx, szp);
-       dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL);
+       dmu_tx_hold_zap(tx, zsb->z_unlinkedobj, FALSE, NULL);
        error = dmu_tx_assign(tx, TXG_NOWAIT);
        if (error) {
                if (zl != NULL)
@@ -3527,16 +3386,16 @@ top:
                if (sdzp == tdzp)
                        rw_exit(&sdzp->z_name_lock);
 
-               VN_RELE(ZTOV(szp));
+               iput(ZTOI(szp));
                if (tzp)
-                       VN_RELE(ZTOV(tzp));
+                       iput(ZTOI(tzp));
                if (error == ERESTART) {
                        dmu_tx_wait(tx);
                        dmu_tx_abort(tx);
                        goto top;
                }
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -3548,22 +3407,15 @@ top:
                if (error == 0) {
                        szp->z_pflags |= ZFS_AV_MODIFIED;
 
-                       error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs),
+                       error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zsb),
                            (void *)&szp->z_pflags, sizeof (uint64_t), tx);
-                       ASSERT3U(error, ==, 0);
+                       ASSERT0(error);
 
                        error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL);
                        if (error == 0) {
                                zfs_log_rename(zilog, tx, TX_RENAME |
-                                   (flags & FIGNORECASE ? TX_CI : 0),
-                                   sdzp, sdl->dl_name, tdzp, tdl->dl_name,
-                                   szp);
-
-                               /*
-                                * Update path information for the target vnode
-                                */
-                               vn_renamepath(tdvp, ZTOV(szp), tnm,
-                                   strlen(tnm));
+                                   (flags & FIGNORECASE ? TX_CI : 0), sdzp,
+                                   sdl->dl_name, tdzp, tdl->dl_name, szp);
                        } else {
                                /*
                                 * At this point, we have successfully created
@@ -3591,47 +3443,54 @@ out:
        zfs_dirent_unlock(sdl);
        zfs_dirent_unlock(tdl);
 
+       zfs_inode_update(sdzp);
        if (sdzp == tdzp)
                rw_exit(&sdzp->z_name_lock);
 
+       if (sdzp != tdzp)
+               zfs_inode_update(tdzp);
 
-       VN_RELE(ZTOV(szp));
-       if (tzp)
-               VN_RELE(ZTOV(tzp));
+       zfs_inode_update(szp);
+       iput(ZTOI(szp));
+       if (tzp) {
+               zfs_inode_update(tzp);
+               iput(ZTOI(tzp));
+       }
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_rename);
 
 /*
  * Insert the indicated symbolic reference entry into the directory.
  *
- *     IN:     dvp     - Directory to contain new symbolic link.
+ *     IN:     dip     - Directory to contain new symbolic link.
  *             link    - Name for new symlink entry.
  *             vap     - Attributes of new entry.
  *             target  - Target path of new symlink.
+ *
  *             cr      - credentials of caller.
- *             ct      - caller context
  *             flags   - case flags
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     dvp - ctime|mtime updated
+ *     dip - ctime|mtime updated
  */
 /*ARGSUSED*/
-static int
-zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr,
-    caller_context_t *ct, int flags)
+int
+zfs_symlink(struct inode *dip, char *name, vattr_t *vap, char *link,
+    struct inode **ipp, cred_t *cr, int flags)
 {
-       znode_t         *zp, *dzp = VTOZ(dvp);
+       znode_t         *zp, *dzp = ITOZ(dip);
        zfs_dirlock_t   *dl;
        dmu_tx_t        *tx;
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       zfs_sb_t        *zsb = ITOZSB(dip);
        zilog_t         *zilog;
        uint64_t        len = strlen(link);
        int             error;
@@ -3640,67 +3499,69 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr,
        boolean_t       fuid_dirtied;
        uint64_t        txtype = TX_SYMLINK;
 
-       ASSERT(vap->va_type == VLNK);
+       ASSERT(S_ISLNK(vap->va_mode));
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       zilog = zfsvfs->z_log;
+       zilog = zsb->z_log;
 
-       if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
+       if (zsb->z_utf8 && u8_validate(name, strlen(name),
            NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
        if (flags & FIGNORECASE)
                zflg |= ZCILOOK;
 
        if (len > MAXPATHLEN) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (ENAMETOOLONG);
        }
 
        if ((error = zfs_acl_ids_create(dzp, 0,
            vap, cr, NULL, &acl_ids)) != 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 top:
+       *ipp = NULL;
+
        /*
         * Attempt to lock directory; fail if entry already exists.
         */
        error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL);
        if (error) {
                zfs_acl_ids_free(&acl_ids);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
+       if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
                zfs_acl_ids_free(&acl_ids);
                zfs_dirent_unlock(dl);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+       if (zfs_acl_ids_overquota(zsb, &acl_ids)) {
                zfs_acl_ids_free(&acl_ids);
                zfs_dirent_unlock(dl);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EDQUOT);
        }
-       tx = dmu_tx_create(zfsvfs->z_os);
-       fuid_dirtied = zfsvfs->z_fuid_dirty;
+       tx = dmu_tx_create(zsb->z_os);
+       fuid_dirtied = zsb->z_fuid_dirty;
        dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len));
        dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name);
        dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes +
            ZFS_SA_BASE_ATTR_SIZE + len);
        dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE);
-       if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
+       if (!zsb->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
                dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
                    acl_ids.z_aclp->z_acl_bytes);
        }
        if (fuid_dirtied)
-               zfs_fuid_txhold(zfsvfs, tx);
+               zfs_fuid_txhold(zsb, tx);
        error = dmu_tx_assign(tx, TXG_NOWAIT);
        if (error) {
                zfs_dirent_unlock(dl);
@@ -3711,7 +3572,7 @@ top:
                }
                zfs_acl_ids_free(&acl_ids);
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -3722,16 +3583,18 @@ top:
        zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids);
 
        if (fuid_dirtied)
-               zfs_fuid_sync(zfsvfs, tx);
+               zfs_fuid_sync(zsb, tx);
 
+       mutex_enter(&zp->z_lock);
        if (zp->z_is_sa)
-               error = sa_update(zp->z_sa_hdl, SA_ZPL_SYMLINK(zfsvfs),
+               error = sa_update(zp->z_sa_hdl, SA_ZPL_SYMLINK(zsb),
                    link, len, tx);
        else
                zfs_sa_symlink(zp, link, len, tx);
+       mutex_exit(&zp->z_lock);
 
        zp->z_size = len;
-       (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs),
+       (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zsb),
            &zp->z_size, sizeof (zp->z_size), tx);
        /*
         * Insert the new object into the directory.
@@ -3742,138 +3605,139 @@ top:
                txtype |= TX_CI;
        zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link);
 
+       zfs_inode_update(dzp);
+       zfs_inode_update(zp);
+
        zfs_acl_ids_free(&acl_ids);
 
        dmu_tx_commit(tx);
 
        zfs_dirent_unlock(dl);
 
-       VN_RELE(ZTOV(zp));
+       *ipp = ZTOI(zp);
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_symlink);
 
 /*
  * Return, in the buffer contained in the provided uio structure,
- * the symbolic path referred to by vp.
+ * the symbolic path referred to by ip.
  *
- *     IN:     vp      - vnode of symbolic link.
- *             uoip    - structure to contain the link path.
+ *     IN:     ip      - inode of symbolic link
+ *             uio     - structure to contain the link path.
  *             cr      - credentials of caller.
- *             ct      - caller context
- *
- *     OUT:    uio     - structure to contain the link path.
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     vp - atime updated
+ *     ip - atime updated
  */
 /* ARGSUSED */
-static int
-zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr, caller_context_t *ct)
+int
+zfs_readlink(struct inode *ip, uio_t *uio, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        int             error;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
+       mutex_enter(&zp->z_lock);
        if (zp->z_is_sa)
                error = sa_lookup_uio(zp->z_sa_hdl,
-                   SA_ZPL_SYMLINK(zfsvfs), uio);
+                   SA_ZPL_SYMLINK(zsb), uio);
        else
                error = zfs_sa_readlink(zp, uio);
+       mutex_exit(&zp->z_lock);
 
-       ZFS_ACCESSTIME_STAMP(zfsvfs, zp);
-
-       ZFS_EXIT(zfsvfs);
+       ZFS_ACCESSTIME_STAMP(zsb, zp);
+       zfs_inode_update(zp);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_readlink);
 
 /*
- * Insert a new entry into directory tdvp referencing svp.
+ * Insert a new entry into directory tdip referencing sip.
  *
- *     IN:     tdvp    - Directory to contain new entry.
- *             svp     - vnode of new entry.
+ *     IN:     tdip    - Directory to contain new entry.
+ *             sip     - inode of new entry.
  *             name    - name of new entry.
  *             cr      - credentials of caller.
- *             ct      - caller context
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     tdvp - ctime|mtime updated
- *      svp - ctime updated
+ *     tdip - ctime|mtime updated
+ *      sip - ctime updated
  */
 /* ARGSUSED */
-static int
-zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
-    caller_context_t *ct, int flags)
+int
+zfs_link(struct inode *tdip, struct inode *sip, char *name, cred_t *cr)
 {
-       znode_t         *dzp = VTOZ(tdvp);
+       znode_t         *dzp = ITOZ(tdip);
        znode_t         *tzp, *szp;
-       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
+       zfs_sb_t        *zsb = ITOZSB(tdip);
        zilog_t         *zilog;
        zfs_dirlock_t   *dl;
        dmu_tx_t        *tx;
-       vnode_t         *realvp;
        int             error;
        int             zf = ZNEW;
        uint64_t        parent;
+       uid_t           owner;
 
-       ASSERT(tdvp->v_type == VDIR);
+       ASSERT(S_ISDIR(tdip->i_mode));
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(dzp);
-       zilog = zfsvfs->z_log;
-
-       if (VOP_REALVP(svp, &realvp, ct) == 0)
-               svp = realvp;
+       zilog = zsb->z_log;
 
        /*
         * POSIX dictates that we return EPERM here.
         * Better choices include ENOTSUP or EISDIR.
         */
-       if (svp->v_type == VDIR) {
-               ZFS_EXIT(zfsvfs);
+       if (S_ISDIR(sip->i_mode)) {
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       if (svp->v_vfsp != tdvp->v_vfsp || zfsctl_is_node(svp)) {
-               ZFS_EXIT(zfsvfs);
+       if (sip->i_sb != tdip->i_sb) {
+               ZFS_EXIT(zsb);
                return (EXDEV);
        }
 
-       szp = VTOZ(svp);
+       szp = ITOZ(sip);
        ZFS_VERIFY_ZP(szp);
 
        /* Prevent links to .zfs/shares files */
 
-       if ((error = sa_lookup(szp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs),
+       if ((error = sa_lookup(szp->z_sa_hdl, SA_ZPL_PARENT(zsb),
            &parent, sizeof (uint64_t))) != 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
-       if (parent == zfsvfs->z_shares_dir) {
-               ZFS_EXIT(zfsvfs);
+       if (parent == zsb->z_shares_dir) {
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       if (zfsvfs->z_utf8 && u8_validate(name,
+       if (zsb->z_utf8 && u8_validate(name,
            strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EILSEQ);
        }
+#ifdef HAVE_PN_UTILS
        if (flags & FIGNORECASE)
                zf |= ZCILOOK;
+#endif /* HAVE_PN_UTILS */
 
        /*
         * We do not support links between attributes and non-attributes
@@ -3882,19 +3746,18 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
         * imposed in attribute space.
         */
        if ((szp->z_pflags & ZFS_XATTR) != (dzp->z_pflags & ZFS_XATTR)) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
-
-       if (szp->z_uid != crgetuid(cr) &&
-           secpolicy_basic_link(cr) != 0) {
-               ZFS_EXIT(zfsvfs);
+       owner = zfs_fuid_map_id(zsb, szp->z_uid, cr, ZFS_OWNER);
+       if (owner != crgetuid(cr) && secpolicy_basic_link(cr) != 0) {
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
-               ZFS_EXIT(zfsvfs);
+       if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -3904,11 +3767,11 @@ top:
         */
        error = zfs_dirent_lock(&dl, dzp, name, &tzp, zf, NULL, NULL);
        if (error) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
-       tx = dmu_tx_create(zfsvfs->z_os);
+       tx = dmu_tx_create(zsb->z_os);
        dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE);
        dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name);
        zfs_sa_upgrade_txholds(tx, szp);
@@ -3922,7 +3785,7 @@ top:
                        goto top;
                }
                dmu_tx_abort(tx);
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
@@ -3930,8 +3793,10 @@ top:
 
        if (error == 0) {
                uint64_t txtype = TX_LINK;
+#ifdef HAVE_PN_UTILS
                if (flags & FIGNORECASE)
                        txtype |= TX_CI;
+#endif /* HAVE_PN_UTILS */
                zfs_log_link(zilog, tx, txtype, dzp, szp, name);
        }
 
@@ -3939,294 +3804,235 @@ top:
 
        zfs_dirent_unlock(dl);
 
-       if (error == 0) {
-               vnevent_link(svp, ct);
-       }
-
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       zfs_inode_update(dzp);
+       zfs_inode_update(szp);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_link);
 
-/*
- * zfs_null_putapage() is used when the file system has been force
- * unmounted. It just drops the pages.
- */
-/* ARGSUSED */
-static int
-zfs_null_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
-               size_t *lenp, int flags, cred_t *cr)
+static void
+zfs_putpage_commit_cb(void *arg, int error)
 {
-       pvn_write_done(pp, B_INVAL|B_FORCE|B_ERROR);
-       return (0);
+       struct page *pp = arg;
+
+       if (error) {
+               __set_page_dirty_nobuffers(pp);
+
+               if (error != ECANCELED)
+                       SetPageError(pp);
+       } else {
+               ClearPageError(pp);
+       }
+
+       end_page_writeback(pp);
 }
 
 /*
- * Push a page out to disk, klustering if possible.
- *
- *     IN:     vp      - file to push page to.
- *             pp      - page to push.
- *             flags   - additional flags.
- *             cr      - credentials of caller.
+ * Push a page out to disk, once the page is on stable storage the
+ * registered commit callback will be run as notification of completion.
  *
- *     OUT:    offp    - start of range pushed.
- *             lenp    - len of range pushed.
+ *     IN:     ip      - page mapped for inode.
+ *             pp      - page to push (page is locked)
+ *             wbc     - writeback control data
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
- * NOTE: callers must have locked the page to be pushed.  On
- * exit, the page (and all other pages in the kluster) must be
- * unlocked.
+ * Timestamps:
+ *     ip - ctime|mtime updated
  */
 /* ARGSUSED */
-static int
-zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
-               size_t *lenp, int flags, cred_t *cr)
+int
+zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
+       loff_t          offset;
+       loff_t          pgoff;
+       unsigned int    pglen;
+       rl_t            *rl;
        dmu_tx_t        *tx;
-       u_offset_t      off, koff;
-       size_t          len, klen;
-       int             err;
+       caddr_t         va;
+       int             err = 0;
+       uint64_t        mtime[2], ctime[2];
+       sa_bulk_attr_t  bulk[3];
+       int             cnt = 0;
+       int             sync;
 
-       off = pp->p_offset;
-       len = PAGESIZE;
-       /*
-        * If our blocksize is bigger than the page size, try to kluster
-        * multiple pages so that we write a full block (thus avoiding
-        * a read-modify-write).
-        */
-       if (off < zp->z_size && zp->z_blksz > PAGESIZE) {
-               klen = P2ROUNDUP((ulong_t)zp->z_blksz, PAGESIZE);
-               koff = ISP2(klen) ? P2ALIGN(off, (u_offset_t)klen) : 0;
-               ASSERT(koff <= zp->z_size);
-               if (koff + klen > zp->z_size)
-                       klen = P2ROUNDUP(zp->z_size - koff, (uint64_t)PAGESIZE);
-               pp = pvn_write_kluster(vp, pp, &off, &len, koff, klen, flags);
-       }
-       ASSERT3U(btop(len), ==, btopr(len));
+       ZFS_ENTER(zsb);
+       ZFS_VERIFY_ZP(zp);
 
-       /*
-        * Can't push pages past end-of-file.
-        */
-       if (off >= zp->z_size) {
-               /* ignore all pages */
-               err = 0;
-               goto out;
-       } else if (off + len > zp->z_size) {
-               int npages = btopr(zp->z_size - off);
-               page_t *trunc;
+       ASSERT(PageLocked(pp));
+
+       pgoff = page_offset(pp);     /* Page byte-offset in file */
+       offset = i_size_read(ip);    /* File length in bytes */
+       pglen = MIN(PAGE_CACHE_SIZE, /* Page length in bytes */
+           P2ROUNDUP(offset, PAGE_CACHE_SIZE)-pgoff);
 
-               page_list_break(&pp, &trunc, npages);
-               /* ignore pages past end of file */
-               if (trunc)
-                       pvn_write_done(trunc, flags);
-               len = zp->z_size - off;
+       /* Page is beyond end of file */
+       if (pgoff >= offset) {
+               unlock_page(pp);
+               ZFS_EXIT(zsb);
+               return (0);
        }
 
-       if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) ||
-           zfs_owner_overquota(zfsvfs, zp, B_TRUE)) {
+       /* Truncate page length to end of file */
+       if (pgoff + pglen > offset)
+               pglen = offset - pgoff;
+
+#if 0
+       /*
+        * FIXME: Allow mmap writes past its quota.  The correct fix
+        * is to register a page_mkwrite() handler to count the page
+        * against its quota when it is about to be dirtied.
+        */
+       if (zfs_owner_overquota(zsb, zp, B_FALSE) ||
+           zfs_owner_overquota(zsb, zp, B_TRUE)) {
                err = EDQUOT;
-               goto out;
        }
-top:
-       tx = dmu_tx_create(zfsvfs->z_os);
-       dmu_tx_hold_write(tx, zp->z_id, off, len);
+#endif
+
+       set_page_writeback(pp);
+       unlock_page(pp);
+
+       rl = zfs_range_lock(zp, pgoff, pglen, RL_WRITER);
+       tx = dmu_tx_create(zsb->z_os);
+
+       sync = ((zsb->z_os->os_sync == ZFS_SYNC_ALWAYS) ||
+               (wbc->sync_mode == WB_SYNC_ALL));
+       if (!sync)
+               dmu_tx_callback_register(tx, zfs_putpage_commit_cb, pp);
+
+       dmu_tx_hold_write(tx, zp->z_id, pgoff, pglen);
 
        dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
        zfs_sa_upgrade_txholds(tx, zp);
        err = dmu_tx_assign(tx, TXG_NOWAIT);
        if (err != 0) {
-               if (err == ERESTART) {
+               if (err == ERESTART)
                        dmu_tx_wait(tx);
-                       dmu_tx_abort(tx);
-                       goto top;
-               }
+
+               /* Will call all registered commit callbacks */
                dmu_tx_abort(tx);
-               goto out;
-       }
 
-       if (zp->z_blksz <= PAGESIZE) {
-               caddr_t va = zfs_map_page(pp, S_READ);
-               ASSERT3U(len, <=, PAGESIZE);
-               dmu_write(zfsvfs->z_os, zp->z_id, off, len, va, tx);
-               zfs_unmap_page(pp, va);
-       } else {
-               err = dmu_write_pages(zfsvfs->z_os, zp->z_id, off, len, pp, tx);
+               /*
+                * For the synchronous case the commit callback must be
+                * explicitly called because there is no registered callback.
+                */
+               if (sync)
+                       zfs_putpage_commit_cb(pp, ECANCELED);
+
+               zfs_range_unlock(rl);
+               ZFS_EXIT(zsb);
+               return (err);
        }
 
-       if (err == 0) {
-               uint64_t mtime[2], ctime[2];
-               sa_bulk_attr_t bulk[3];
-               int count = 0;
+       va = kmap(pp);
+       ASSERT3U(pglen, <=, PAGE_CACHE_SIZE);
+       dmu_write(zsb->z_os, zp->z_id, pgoff, pglen, va, tx);
+       kunmap(pp);
 
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL,
-                   &mtime, 16);
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
-                   &ctime, 16);
-               SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
-                   &zp->z_pflags, 8);
-               zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime,
-                   B_TRUE);
-               zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off, len, 0);
-       }
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_FLAGS(zsb), NULL, &zp->z_pflags, 8);
+
+       /* Preserve the mtime and ctime provided by the inode */
+       ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
+       ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
+       zp->z_atime_dirty = 0;
+       zp->z_seq++;
+
+       err = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);
+
+       zfs_log_write(zsb->z_log, tx, TX_WRITE, zp, pgoff, pglen, 0);
        dmu_tx_commit(tx);
 
-out:
-       pvn_write_done(pp, (err ? B_ERROR : 0) | flags);
-       if (offp)
-               *offp = off;
-       if (lenp)
-               *lenp = len;
+       zfs_range_unlock(rl);
 
+       if (sync) {
+               zil_commit(zsb->z_log, zp->z_id);
+               zfs_putpage_commit_cb(pp, err);
+       }
+
+       ZFS_EXIT(zsb);
        return (err);
 }
 
 /*
- * Copy the portion of the file indicated from pages into the file.
- * The pages are stored in a page list attached to the files vnode.
- *
- *     IN:     vp      - vnode of file to push page data to.
- *             off     - position in file to put data.
- *             len     - amount of data to write.
- *             flags   - flags to control the operation.
- *             cr      - credentials of caller.
- *             ct      - caller context.
- *
- *     RETURN: 0 if success
- *             error code if failure
- *
- * Timestamps:
- *     vp - ctime|mtime updated
+ * Update the system attributes when the inode has been dirtied.  For the
+ * moment we're conservative and only update the atime, mtime, and ctime.
  */
-/*ARGSUSED*/
-static int
-zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_dirty_inode(struct inode *ip, int flags)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
-       page_t          *pp;
-       size_t          io_len;
-       u_offset_t      io_off;
-       uint_t          blksz;
-       rl_t            *rl;
-       int             error = 0;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
+       dmu_tx_t        *tx;
+       uint64_t        atime[2], mtime[2], ctime[2];
+       sa_bulk_attr_t  bulk[3];
+       int             error;
+       int             cnt = 0;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       /*
-        * Align this request to the file block size in case we kluster.
-        * XXX - this can result in pretty aggresive locking, which can
-        * impact simultanious read/write access.  One option might be
-        * to break up long requests (len == 0) into block-by-block
-        * operations to get narrower locking.
-        */
-       blksz = zp->z_blksz;
-       if (ISP2(blksz))
-               io_off = P2ALIGN_TYPED(off, blksz, u_offset_t);
-       else
-               io_off = 0;
-       if (len > 0 && ISP2(blksz))
-               io_len = P2ROUNDUP_TYPED(len + (off - io_off), blksz, size_t);
-       else
-               io_len = 0;
+       tx = dmu_tx_create(zsb->z_os);
 
-       if (io_len == 0) {
-               /*
-                * Search the entire vp list for pages >= io_off.
-                */
-               rl = zfs_range_lock(zp, io_off, UINT64_MAX, RL_WRITER);
-               error = pvn_vplist_dirty(vp, io_off, zfs_putapage, flags, cr);
-               goto out;
-       }
-       rl = zfs_range_lock(zp, io_off, io_len, RL_WRITER);
+       dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
+       zfs_sa_upgrade_txholds(tx, zp);
 
-       if (off > zp->z_size) {
-               /* past end of file */
-               zfs_range_unlock(rl);
-               ZFS_EXIT(zfsvfs);
-               return (0);
+       error = dmu_tx_assign(tx, TXG_WAIT);
+       if (error) {
+               dmu_tx_abort(tx);
+               goto out;
        }
 
-       len = MIN(io_len, P2ROUNDUP(zp->z_size, PAGESIZE) - io_off);
+       mutex_enter(&zp->z_lock);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_ATIME(zsb), NULL, &atime, 16);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
 
-       for (off = io_off; io_off < off + len; io_off += io_len) {
-               if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) {
-                       pp = page_lookup(vp, io_off,
-                           (flags & (B_INVAL | B_FREE)) ? SE_EXCL : SE_SHARED);
-               } else {
-                       pp = page_lookup_nowait(vp, io_off,
-                           (flags & B_FREE) ? SE_EXCL : SE_SHARED);
-               }
+       /* Preserve the mtime and ctime provided by the inode */
+       ZFS_TIME_ENCODE(&ip->i_atime, atime);
+       ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
+       ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
+       zp->z_atime_dirty = 0;
 
-               if (pp != NULL && pvn_getdirty(pp, flags)) {
-                       int err;
+       error = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);
+       mutex_exit(&zp->z_lock);
 
-                       /*
-                        * Found a dirty page to push
-                        */
-                       err = zfs_putapage(vp, pp, &io_off, &io_len, flags, cr);
-                       if (err)
-                               error = err;
-               } else {
-                       io_len = PAGESIZE;
-               }
-       }
+       dmu_tx_commit(tx);
 out:
-       zfs_range_unlock(rl);
-       if ((flags & B_ASYNC) == 0 || zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zfsvfs->z_log, UINT64_MAX, zp->z_id);
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_dirty_inode);
 
 /*ARGSUSED*/
 void
-zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+zfs_inactive(struct inode *ip)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int error;
 
-       rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER);
-       if (zp->z_sa_hdl == NULL) {
-               /*
-                * The fs has been unmounted, or we did a
-                * suspend/resume and this file no longer exists.
-                */
-               if (vn_has_cached_data(vp)) {
-                       (void) pvn_vplist_dirty(vp, 0, zfs_null_putapage,
-                           B_INVAL, cr);
-               }
-
-               mutex_enter(&zp->z_lock);
-               mutex_enter(&vp->v_lock);
-               ASSERT(vp->v_count == 1);
-               vp->v_count = 0;
-               mutex_exit(&vp->v_lock);
-               mutex_exit(&zp->z_lock);
-               rw_exit(&zfsvfs->z_teardown_inactive_lock);
-               zfs_znode_free(zp);
+       if (zfsctl_is_node(ip)) {
+               zfsctl_inode_inactive(ip);
                return;
        }
 
-       /*
-        * Attempt to push any data in the page cache.  If this fails
-        * we will get kicked out later in zfs_zinactive().
-        */
-       if (vn_has_cached_data(vp)) {
-               (void) pvn_vplist_dirty(vp, 0, zfs_putapage, B_INVAL|B_ASYNC,
-                   cr);
+       rw_enter(&zsb->z_teardown_inactive_lock, RW_READER);
+       if (zp->z_sa_hdl == NULL) {
+               rw_exit(&zsb->z_teardown_inactive_lock);
+               return;
        }
 
        if (zp->z_atime_dirty && zp->z_unlinked == 0) {
-               dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os);
+               dmu_tx_t *tx = dmu_tx_create(zsb->z_os);
 
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
                zfs_sa_upgrade_txholds(tx, zp);
@@ -4235,7 +4041,7 @@ zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
                        dmu_tx_abort(tx);
                } else {
                        mutex_enter(&zp->z_lock);
-                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs),
+                       (void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zsb),
                            (void *)&zp->z_atime, sizeof (zp->z_atime), tx);
                        zp->z_atime_dirty = 0;
                        mutex_exit(&zp->z_lock);
@@ -4244,13 +4050,14 @@ zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
        }
 
        zfs_zinactive(zp);
-       rw_exit(&zfsvfs->z_teardown_inactive_lock);
+       rw_exit(&zsb->z_teardown_inactive_lock);
 }
+EXPORT_SYMBOL(zfs_inactive);
 
 /*
  * Bounds-check the seek operation.
  *
- *     IN:     vp      - vnode seeking within
+ *     IN:     ip      - inode seeking within
  *             ooff    - old file offset
  *             noffp   - pointer to new file offset
  *             ct      - caller context
@@ -4259,138 +4066,69 @@ zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
  *             EINVAL if new offset invalid
  */
 /* ARGSUSED */
-static int
-zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp,
-    caller_context_t *ct)
+int
+zfs_seek(struct inode *ip, offset_t ooff, offset_t *noffp)
 {
-       if (vp->v_type == VDIR)
+       if (S_ISDIR(ip->i_mode))
                return (0);
        return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
 }
+EXPORT_SYMBOL(zfs_seek);
 
 /*
- * Pre-filter the generic locking function to trap attempts to place
- * a mandatory lock on a memory mapped file.
+ * Fill pages with data from the disk.
  */
 static int
-zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
-    flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct)
+zfs_fillpage(struct inode *ip, struct page *pl[], int nr_pages)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
-
-       ZFS_ENTER(zfsvfs);
-       ZFS_VERIFY_ZP(zp);
-
-       /*
-        * We are following the UFS semantics with respect to mapcnt
-        * here: If we see that the file is mapped already, then we will
-        * return an error, but we don't worry about races between this
-        * function and zfs_map().
-        */
-       if (zp->z_mapcnt > 0 && MANDMODE(zp->z_mode)) {
-               ZFS_EXIT(zfsvfs);
-               return (EAGAIN);
-       }
-       ZFS_EXIT(zfsvfs);
-       return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
-}
-
-/*
- * If we can't find a page in the cache, we will create a new page
- * and fill it with file data.  For efficiency, we may try to fill
- * multiple pages at once (klustering) to fill up the supplied page
- * list.  Note that the pages to be filled are held with an exclusive
- * lock to prevent access by other threads while they are being filled.
- */
-static int
-zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg,
-    caddr_t addr, page_t *pl[], size_t plsz, enum seg_rw rw)
-{
-       znode_t *zp = VTOZ(vp);
-       page_t *pp, *cur_pp;
-       objset_t *os = zp->z_zfsvfs->z_os;
-       u_offset_t io_off, total;
-       size_t io_len;
-       int err;
-
-       if (plsz == PAGESIZE || zp->z_blksz <= PAGESIZE) {
-               /*
-                * We only have a single page, don't bother klustering
-                */
-               io_off = off;
-               io_len = PAGESIZE;
-               pp = page_create_va(vp, io_off, io_len,
-                   PG_EXCL | PG_WAIT, seg, addr);
-       } else {
-               /*
-                * Try to find enough pages to fill the page list
-                */
-               pp = pvn_read_kluster(vp, off, seg, addr, &io_off,
-                   &io_len, off, plsz, 0);
-       }
-       if (pp == NULL) {
-               /*
-                * The page already exists, nothing to do here.
-                */
-               *pl = NULL;
-               return (0);
-       }
+       znode_t     *zp = ITOZ(ip);
+       zfs_sb_t    *zsb = ITOZSB(ip);
+       objset_t    *os;
+       struct page *cur_pp;
+       u_offset_t  io_off, total;
+       size_t      io_len;
+       loff_t      i_size;
+       unsigned    page_idx;
+       int         err;
+
+       os     = zsb->z_os;
+       io_len = nr_pages << PAGE_CACHE_SHIFT;
+       i_size = i_size_read(ip);
+       io_off = page_offset(pl[0]);
+
+       if (io_off + io_len > i_size)
+               io_len = i_size - io_off;
 
        /*
-        * Fill the pages in the kluster.
+        * Iterate over list of pages and read each page individually.
         */
-       cur_pp = pp;
+       page_idx = 0;
+       cur_pp   = pl[0];
        for (total = io_off + io_len; io_off < total; io_off += PAGESIZE) {
                caddr_t va;
 
-               ASSERT3U(io_off, ==, cur_pp->p_offset);
-               va = zfs_map_page(cur_pp, S_WRITE);
+               va = kmap(cur_pp);
                err = dmu_read(os, zp->z_id, io_off, PAGESIZE, va,
                    DMU_READ_PREFETCH);
-               zfs_unmap_page(cur_pp, va);
+               kunmap(cur_pp);
                if (err) {
-                       /* On error, toss the entire kluster */
-                       pvn_read_done(pp, B_ERROR);
                        /* convert checksum errors into IO errors */
                        if (err == ECKSUM)
                                err = EIO;
                        return (err);
                }
-               cur_pp = cur_pp->p_next;
+               cur_pp = pl[++page_idx];
        }
 
-       /*
-        * Fill in the page list array from the kluster starting
-        * from the desired offset `off'.
-        * NOTE: the page list will always be null terminated.
-        */
-       pvn_plist_init(pp, pl, plsz, off, io_len, rw);
-       ASSERT(pl == NULL || (*pl)->p_offset == off);
-
        return (0);
 }
 
 /*
- * Return pointers to the pages for the file region [off, off + len]
- * in the pl array.  If plsz is greater than len, this function may
- * also return page pointers from after the specified region
- * (i.e. the region [off, off + plsz]).  These additional pages are
- * only returned if they are already in the cache, or were created as
- * part of a klustered read.
+ * Uses zfs_fillpage to read data from the file and fill the pages.
  *
- *     IN:     vp      - vnode of file to get data from.
- *             off     - position in file to get data from.
- *             len     - amount of data to retrieve.
- *             plsz    - length of provided page list.
- *             seg     - segment to obtain pages for.
- *             addr    - virtual address of fault.
- *             rw      - mode of created pages.
- *             cr      - credentials of caller.
- *             ct      - caller context.
- *
- *     OUT:    protp   - protection mode of created pages.
- *             pl      - list of pages created.
+ *     IN:     ip       - inode of file to get data from.
+ *             pl       - list of pages to read
+ *             nr_pages - number of pages to read
  *
  *     RETURN: 0 if success
  *             error code if failure
@@ -4399,217 +4137,119 @@ zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg,
  *     vp - atime updated
  */
 /* ARGSUSED */
-static int
-zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
-       page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
-       enum seg_rw rw, cred_t *cr, caller_context_t *ct)
+int
+zfs_getpage(struct inode *ip, struct page *pl[], int nr_pages)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
-       page_t          **pl0 = pl;
-       int             err = 0;
+       znode_t  *zp  = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
+       int      err;
 
-       /* we do our own caching, faultahead is unnecessary */
        if (pl == NULL)
                return (0);
-       else if (len > plsz)
-               len = plsz;
-       else
-               len = P2ROUNDUP(len, PAGESIZE);
-       ASSERT(plsz >= len);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       if (protp)
-               *protp = PROT_ALL;
+       err = zfs_fillpage(ip, pl, nr_pages);
 
-       /*
-        * Loop through the requested range [off, off + len) looking
-        * for pages.  If we don't find a page, we will need to create
-        * a new page and fill it with data from the file.
-        */
-       while (len > 0) {
-               if (*pl = page_lookup(vp, off, SE_SHARED))
-                       *(pl+1) = NULL;
-               else if (err = zfs_fillpage(vp, off, seg, addr, pl, plsz, rw))
-                       goto out;
-               while (*pl) {
-                       ASSERT3U((*pl)->p_offset, ==, off);
-                       off += PAGESIZE;
-                       addr += PAGESIZE;
-                       if (len > 0) {
-                               ASSERT3U(len, >=, PAGESIZE);
-                               len -= PAGESIZE;
-                       }
-                       ASSERT3U(plsz, >=, PAGESIZE);
-                       plsz -= PAGESIZE;
-                       pl++;
-               }
-       }
+       if (!err)
+               ZFS_ACCESSTIME_STAMP(zsb, zp);
 
-       /*
-        * Fill out the page array with any pages already in the cache.
-        */
-       while (plsz > 0 &&
-           (*pl++ = page_lookup_nowait(vp, off, SE_SHARED))) {
-                       off += PAGESIZE;
-                       plsz -= PAGESIZE;
-       }
-out:
-       if (err) {
-               /*
-                * Release any pages we have previously locked.
-                */
-               while (pl > pl0)
-                       page_unlock(*--pl);
-       } else {
-               ZFS_ACCESSTIME_STAMP(zfsvfs, zp);
-       }
-
-       *pl = NULL;
-
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (err);
 }
+EXPORT_SYMBOL(zfs_getpage);
 
 /*
- * Request a memory map for a section of a file.  This code interacts
- * with common code and the VM system as follows:
- *
- *     common code calls mmap(), which ends up in smmap_common()
+ * Check ZFS specific permissions to memory map a section of a file.
  *
- *     this calls VOP_MAP(), which takes you into (say) zfs
+ *     IN:     ip      - inode of the file to mmap
+ *             off     - file offset
+ *             addrp   - start address in memory region
+ *             len     - length of memory region
+ *             vm_flags- address flags
  *
- *     zfs_map() calls as_map(), passing segvn_create() as the callback
- *
- *     segvn_create() creates the new segment and calls VOP_ADDMAP()
- *
- *     zfs_addmap() updates z_mapcnt
+ *     RETURN: 0 if success
+ *             error code if failure
  */
 /*ARGSUSED*/
-static int
-zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
-    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_map(struct inode *ip, offset_t off, caddr_t *addrp, size_t len,
+    unsigned long vm_flags)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
-       segvn_crargs_t  vn_a;
-       int             error;
+       znode_t  *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       if ((prot & PROT_WRITE) && (zp->z_pflags &
+       if ((vm_flags & VM_WRITE) && (zp->z_pflags &
            (ZFS_IMMUTABLE | ZFS_READONLY | ZFS_APPENDONLY))) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EPERM);
        }
 
-       if ((prot & (PROT_READ | PROT_EXEC)) &&
+       if ((vm_flags & (VM_READ | VM_EXEC)) &&
            (zp->z_pflags & ZFS_AV_QUARANTINED)) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EACCES);
        }
 
-       if (vp->v_flag & VNOMAP) {
-               ZFS_EXIT(zfsvfs);
-               return (ENOSYS);
-       }
-
        if (off < 0 || len > MAXOFFSET_T - off) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (ENXIO);
        }
 
-       if (vp->v_type != VREG) {
-               ZFS_EXIT(zfsvfs);
-               return (ENODEV);
-       }
-
-       /*
-        * If file is locked, disallow mapping.
-        */
-       if (MANDMODE(zp->z_mode) && vn_has_flocks(vp)) {
-               ZFS_EXIT(zfsvfs);
-               return (EAGAIN);
-       }
-
-       as_rangelock(as);
-       error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
-       if (error != 0) {
-               as_rangeunlock(as);
-               ZFS_EXIT(zfsvfs);
-               return (error);
-       }
-
-       vn_a.vp = vp;
-       vn_a.offset = (u_offset_t)off;
-       vn_a.type = flags & MAP_TYPE;
-       vn_a.prot = prot;
-       vn_a.maxprot = maxprot;
-       vn_a.cred = cr;
-       vn_a.amp = NULL;
-       vn_a.flags = flags & ~MAP_TYPE;
-       vn_a.szc = 0;
-       vn_a.lgrp_mem_policy_flags = 0;
-
-       error = as_map(as, *addrp, len, segvn_create, &vn_a);
-
-       as_rangeunlock(as);
-       ZFS_EXIT(zfsvfs);
-       return (error);
-}
-
-/* ARGSUSED */
-static int
-zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
-    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
-{
-       uint64_t pages = btopr(len);
-
-       atomic_add_64(&VTOZ(vp)->z_mapcnt, pages);
+       ZFS_EXIT(zsb);
        return (0);
 }
+EXPORT_SYMBOL(zfs_map);
 
 /*
- * The reason we push dirty pages as part of zfs_delmap() is so that we get a
- * more accurate mtime for the associated file.  Since we don't have a way of
- * detecting when the data was actually modified, we have to resort to
- * heuristics.  If an explicit msync() is done, then we mark the mtime when the
- * last page is pushed.  The problem occurs when the msync() call is omitted,
- * which by far the most common case:
- *
- *     open()
- *     mmap()
- *     <modify memory>
- *     munmap()
- *     close()
- *     <time lapse>
- *     putpage() via fsflush
- *
- * If we wait until fsflush to come along, we can have a modification time that
- * is some arbitrary point in the future.  In order to prevent this in the
- * common case, we flush pages whenever a (MAP_SHARED, PROT_WRITE) mapping is
- * torn down.
+ * convoff - converts the given data (start, whence) to the
+ * given whence.
  */
-/* ARGSUSED */
-static int
-zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
-    size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
+int
+convoff(struct inode *ip, flock64_t *lckdat, int  whence, offset_t offset)
 {
-       uint64_t pages = btopr(len);
+       vattr_t vap;
+       int error;
+
+       if ((lckdat->l_whence == 2) || (whence == 2)) {
+               if ((error = zfs_getattr(ip, &vap, 0, CRED()) != 0))
+                       return (error);
+       }
 
-       ASSERT3U(VTOZ(vp)->z_mapcnt, >=, pages);
-       atomic_add_64(&VTOZ(vp)->z_mapcnt, -pages);
+       switch (lckdat->l_whence) {
+       case 1:
+               lckdat->l_start += offset;
+               break;
+       case 2:
+               lckdat->l_start += vap.va_size;
+               /* FALLTHRU */
+       case 0:
+               break;
+       default:
+               return (EINVAL);
+       }
 
-       if ((flags & MAP_SHARED) && (prot & PROT_WRITE) &&
-           vn_has_cached_data(vp))
-               (void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr, ct);
+       if (lckdat->l_start < 0)
+               return (EINVAL);
 
+       switch (whence) {
+       case 1:
+               lckdat->l_start -= offset;
+               break;
+       case 2:
+               lckdat->l_start -= vap.va_size;
+               /* FALLTHRU */
+       case 0:
+               break;
+       default:
+               return (EINVAL);
+       }
+
+       lckdat->l_whence = (short)whence;
        return (0);
 }
 
@@ -4619,84 +4259,95 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
  * misnamed, as its functionality includes the ability to allocate as
  * well as free space.
  *
- *     IN:     vp      - vnode of file to free data in.
+ *     IN:     ip      - inode of file to free data in.
  *             cmd     - action to take (only F_FREESP supported).
  *             bfp     - section of file to free/alloc.
  *             flag    - current file open mode flags.
  *             offset  - current file offset.
  *             cr      - credentials of caller [UNUSED].
- *             ct      - caller context.
  *
  *     RETURN: 0 if success
  *             error code if failure
  *
  * Timestamps:
- *     vp - ctime|mtime updated
+ *     ip - ctime|mtime updated
  */
 /* ARGSUSED */
-static int
-zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag,
-    offset_t offset, cred_t *cr, caller_context_t *ct)
+int
+zfs_space(struct inode *ip, int cmd, flock64_t *bfp, int flag,
+    offset_t offset, cred_t *cr)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        uint64_t        off, len;
        int             error;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
        if (cmd != F_FREESP) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
-       if (error = convoff(vp, bfp, 0, offset)) {
-               ZFS_EXIT(zfsvfs);
+       if ((error = convoff(ip, bfp, 0, offset))) {
+               ZFS_EXIT(zsb);
                return (error);
        }
 
        if (bfp->l_len < 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
+       /*
+        * Permissions aren't checked on Solaris because on this OS
+        * zfs_space() can only be called with an opened file handle.
+        * On Linux we can get here through truncate_range() which
+        * operates directly on inodes, so we need to check access rights.
+        */
+       if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr))) {
+               ZFS_EXIT(zsb);
+               return (error);
+       }
+
        off = bfp->l_start;
        len = bfp->l_len; /* 0 means from off to end of file */
 
        error = zfs_freesp(zp, off, len, flag, TRUE);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_space);
 
 /*ARGSUSED*/
-static int
-zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
+int
+zfs_fid(struct inode *ip, fid_t *fidp)
 {
-       znode_t         *zp = VTOZ(vp);
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       znode_t         *zp = ITOZ(ip);
+       zfs_sb_t        *zsb = ITOZSB(ip);
        uint32_t        gen;
        uint64_t        gen64;
        uint64_t        object = zp->z_id;
        zfid_short_t    *zfid;
        int             size, i, error;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(zfsvfs),
+       if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(zsb),
            &gen64, sizeof (uint64_t))) != 0) {
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (error);
        }
 
        gen = (uint32_t)gen64;
 
-       size = (zfsvfs->z_parent != zfsvfs) ? LONG_FID_LEN : SHORT_FID_LEN;
+       size = (zsb->z_parent != zsb) ? LONG_FID_LEN : SHORT_FID_LEN;
        if (fidp->fid_len < size) {
                fidp->fid_len = size;
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (ENOSPC);
        }
 
@@ -4714,7 +4365,7 @@ zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
                zfid->zf_gen[i] = (uint8_t)(gen >> (8 * i));
 
        if (size == LONG_FID_LEN) {
-               uint64_t        objsetid = dmu_objset_id(zfsvfs->z_os);
+               uint64_t        objsetid = dmu_objset_id(zsb->z_os);
                zfid_long_t     *zlfid;
 
                zlfid = (zfid_long_t *)fidp;
@@ -4727,139 +4378,70 @@ zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
                        zlfid->zf_setgen[i] = 0;
        }
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (0);
 }
-
-static int
-zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
-    caller_context_t *ct)
-{
-       znode_t         *zp, *xzp;
-       zfsvfs_t        *zfsvfs;
-       zfs_dirlock_t   *dl;
-       int             error;
-
-       switch (cmd) {
-       case _PC_LINK_MAX:
-               *valp = ULONG_MAX;
-               return (0);
-
-       case _PC_FILESIZEBITS:
-               *valp = 64;
-               return (0);
-
-       case _PC_XATTR_EXISTS:
-               zp = VTOZ(vp);
-               zfsvfs = zp->z_zfsvfs;
-               ZFS_ENTER(zfsvfs);
-               ZFS_VERIFY_ZP(zp);
-               *valp = 0;
-               error = zfs_dirent_lock(&dl, zp, "", &xzp,
-                   ZXATTR | ZEXISTS | ZSHARED, NULL, NULL);
-               if (error == 0) {
-                       zfs_dirent_unlock(dl);
-                       if (!zfs_dirempty(xzp))
-                               *valp = 1;
-                       VN_RELE(ZTOV(xzp));
-               } else if (error == ENOENT) {
-                       /*
-                        * If there aren't extended attributes, it's the
-                        * same as having zero of them.
-                        */
-                       error = 0;
-               }
-               ZFS_EXIT(zfsvfs);
-               return (error);
-
-       case _PC_SATTR_ENABLED:
-       case _PC_SATTR_EXISTS:
-               *valp = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
-                   (vp->v_type == VREG || vp->v_type == VDIR);
-               return (0);
-
-       case _PC_ACCESS_FILTERING:
-               *valp = vfs_has_feature(vp->v_vfsp, VFSFT_ACCESS_FILTER) &&
-                   vp->v_type == VDIR;
-               return (0);
-
-       case _PC_ACL_ENABLED:
-               *valp = _ACL_ACE_ENABLED;
-               return (0);
-
-       case _PC_MIN_HOLE_SIZE:
-               *valp = (ulong_t)SPA_MINBLOCKSIZE;
-               return (0);
-
-       case _PC_TIMESTAMP_RESOLUTION:
-               /* nanosecond timestamp resolution */
-               *valp = 1L;
-               return (0);
-
-       default:
-               return (fs_pathconf(vp, cmd, valp, cr, ct));
-       }
-}
+EXPORT_SYMBOL(zfs_fid);
 
 /*ARGSUSED*/
-static int
-zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_getsecattr(struct inode *ip, vsecattr_t *vsecp, int flag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int error;
        boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
        error = zfs_getacl(zp, vsecp, skipaclchk, cr);
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
 
        return (error);
 }
+EXPORT_SYMBOL(zfs_getsecattr);
 
 /*ARGSUSED*/
-static int
-zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr,
-    caller_context_t *ct)
+int
+zfs_setsecattr(struct inode *ip, vsecattr_t *vsecp, int flag, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
        int error;
        boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
-       zilog_t *zilog = zfsvfs->z_log;
+       zilog_t *zilog = zsb->z_log;
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
        error = zfs_setacl(zp, vsecp, skipaclchk, cr);
 
-       if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
-               zil_commit(zilog, UINT64_MAX, 0);
+       if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
+               zil_commit(zilog, 0);
 
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (error);
 }
+EXPORT_SYMBOL(zfs_setsecattr);
 
+#ifdef HAVE_UIO_ZEROCOPY
 /*
  * Tunable, both must be a power of 2.
  *
  * zcr_blksz_min: the smallest read we may consider to loan out an arcbuf
  * zcr_blksz_max: if set to less than the file block size, allow loaning out of
- *                an arcbuf for a partial block read
+ *             an arcbuf for a partial block read
  */
 int zcr_blksz_min = (1 << 10); /* 1K */
 int zcr_blksz_max = (1 << 17); /* 128K */
 
 /*ARGSUSED*/
 static int
-zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
-    caller_context_t *ct)
+zfs_reqzcbuf(struct inode *ip, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr)
 {
-       znode_t *zp = VTOZ(vp);
-       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
-       int max_blksz = zfsvfs->z_max_blksz;
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
+       int max_blksz = zsb->z_max_blksz;
        uio_t *uio = &xuio->xu_uio;
        ssize_t size = uio->uio_resid;
        offset_t offset = uio->uio_loffset;
@@ -4872,7 +4454,7 @@ zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
        if (xuio->xu_type != UIOTYPE_ZEROCOPY)
                return (EINVAL);
 
-       ZFS_ENTER(zfsvfs);
+       ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
        switch (ioflag) {
        case UIO_WRITE:
@@ -4882,7 +4464,7 @@ zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
                 */
                blksz = max_blksz;
                if (size < blksz || zp->z_blksz != blksz) {
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (EINVAL);
                }
                /*
@@ -4905,9 +4487,6 @@ zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
                fullblk = size / blksz;
                (void) dmu_xuio_init(xuio,
                    (preamble != 0) + fullblk + (postamble != 0));
-               DTRACE_PROBE3(zfs_reqzcbuf_align, int, preamble,
-                   int, postamble, int,
-                   (preamble != 0) + fullblk + (postamble != 0));
 
                /*
                 * Have to fix iov base/len for partial buffers.  They
@@ -4950,7 +4529,7 @@ zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
                        blksz = zcr_blksz_max;
                /* avoid potential complexity of dealing with it */
                if (blksz > max_blksz) {
-                       ZFS_EXIT(zfsvfs);
+                       ZFS_EXIT(zsb);
                        return (EINVAL);
                }
 
@@ -4958,25 +4537,25 @@ zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr,
                if (size > maxsize)
                        size = maxsize;
 
-               if (size < blksz || vn_has_cached_data(vp)) {
-                       ZFS_EXIT(zfsvfs);
+               if (size < blksz) {
+                       ZFS_EXIT(zsb);
                        return (EINVAL);
                }
                break;
        default:
-               ZFS_EXIT(zfsvfs);
+               ZFS_EXIT(zsb);
                return (EINVAL);
        }
 
        uio->uio_extflg = UIO_XUIO;
        XUIO_XUZC_RW(xuio) = ioflag;
-       ZFS_EXIT(zfsvfs);
+       ZFS_EXIT(zsb);
        return (0);
 }
 
 /*ARGSUSED*/
 static int
-zfs_retzcbuf(vnode_t *vp, xuio_t *xuio, cred_t *cr, caller_context_t *ct)
+zfs_retzcbuf(struct inode *ip, xuio_t *xuio, cred_t *cr)
 {
        int i;
        arc_buf_t *abuf;
@@ -4999,173 +4578,9 @@ zfs_retzcbuf(vnode_t *vp, xuio_t *xuio, cred_t *cr, caller_context_t *ct)
        dmu_xuio_fini(xuio);
        return (0);
 }
+#endif /* HAVE_UIO_ZEROCOPY */
 
-/*
- * Predeclare these here so that the compiler assumes that
- * this is an "old style" function declaration that does
- * not include arguments => we won't get type mismatch errors
- * in the initializations that follow.
- */
-static int zfs_inval();
-static int zfs_isdir();
-
-static int
-zfs_inval()
-{
-       return (EINVAL);
-}
-
-static int
-zfs_isdir()
-{
-       return (EISDIR);
-}
-/*
- * Directory vnode operations template
- */
-vnodeops_t *zfs_dvnodeops;
-const fs_operation_def_t zfs_dvnodeops_template[] = {
-       VOPNAME_OPEN,           { .vop_open = zfs_open },
-       VOPNAME_CLOSE,          { .vop_close = zfs_close },
-       VOPNAME_READ,           { .error = zfs_isdir },
-       VOPNAME_WRITE,          { .error = zfs_isdir },
-       VOPNAME_IOCTL,          { .vop_ioctl = zfs_ioctl },
-       VOPNAME_GETATTR,        { .vop_getattr = zfs_getattr },
-       VOPNAME_SETATTR,        { .vop_setattr = zfs_setattr },
-       VOPNAME_ACCESS,         { .vop_access = zfs_access },
-       VOPNAME_LOOKUP,         { .vop_lookup = zfs_lookup },
-       VOPNAME_CREATE,         { .vop_create = zfs_create },
-       VOPNAME_REMOVE,         { .vop_remove = zfs_remove },
-       VOPNAME_LINK,           { .vop_link = zfs_link },
-       VOPNAME_RENAME,         { .vop_rename = zfs_rename },
-       VOPNAME_MKDIR,          { .vop_mkdir = zfs_mkdir },
-       VOPNAME_RMDIR,          { .vop_rmdir = zfs_rmdir },
-       VOPNAME_READDIR,        { .vop_readdir = zfs_readdir },
-       VOPNAME_SYMLINK,        { .vop_symlink = zfs_symlink },
-       VOPNAME_FSYNC,          { .vop_fsync = zfs_fsync },
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_FID,            { .vop_fid = zfs_fid },
-       VOPNAME_SEEK,           { .vop_seek = zfs_seek },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       VOPNAME_GETSECATTR,     { .vop_getsecattr = zfs_getsecattr },
-       VOPNAME_SETSECATTR,     { .vop_setsecattr = zfs_setsecattr },
-       VOPNAME_VNEVENT,        { .vop_vnevent = fs_vnevent_support },
-       NULL,                   NULL
-};
-
-/*
- * Regular file vnode operations template
- */
-vnodeops_t *zfs_fvnodeops;
-const fs_operation_def_t zfs_fvnodeops_template[] = {
-       VOPNAME_OPEN,           { .vop_open = zfs_open },
-       VOPNAME_CLOSE,          { .vop_close = zfs_close },
-       VOPNAME_READ,           { .vop_read = zfs_read },
-       VOPNAME_WRITE,          { .vop_write = zfs_write },
-       VOPNAME_IOCTL,          { .vop_ioctl = zfs_ioctl },
-       VOPNAME_GETATTR,        { .vop_getattr = zfs_getattr },
-       VOPNAME_SETATTR,        { .vop_setattr = zfs_setattr },
-       VOPNAME_ACCESS,         { .vop_access = zfs_access },
-       VOPNAME_LOOKUP,         { .vop_lookup = zfs_lookup },
-       VOPNAME_RENAME,         { .vop_rename = zfs_rename },
-       VOPNAME_FSYNC,          { .vop_fsync = zfs_fsync },
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_FID,            { .vop_fid = zfs_fid },
-       VOPNAME_SEEK,           { .vop_seek = zfs_seek },
-       VOPNAME_FRLOCK,         { .vop_frlock = zfs_frlock },
-       VOPNAME_SPACE,          { .vop_space = zfs_space },
-       VOPNAME_GETPAGE,        { .vop_getpage = zfs_getpage },
-       VOPNAME_PUTPAGE,        { .vop_putpage = zfs_putpage },
-       VOPNAME_MAP,            { .vop_map = zfs_map },
-       VOPNAME_ADDMAP,         { .vop_addmap = zfs_addmap },
-       VOPNAME_DELMAP,         { .vop_delmap = zfs_delmap },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       VOPNAME_GETSECATTR,     { .vop_getsecattr = zfs_getsecattr },
-       VOPNAME_SETSECATTR,     { .vop_setsecattr = zfs_setsecattr },
-       VOPNAME_VNEVENT,        { .vop_vnevent = fs_vnevent_support },
-       VOPNAME_REQZCBUF,       { .vop_reqzcbuf = zfs_reqzcbuf },
-       VOPNAME_RETZCBUF,       { .vop_retzcbuf = zfs_retzcbuf },
-       NULL,                   NULL
-};
-
-/*
- * Symbolic link vnode operations template
- */
-vnodeops_t *zfs_symvnodeops;
-const fs_operation_def_t zfs_symvnodeops_template[] = {
-       VOPNAME_GETATTR,        { .vop_getattr = zfs_getattr },
-       VOPNAME_SETATTR,        { .vop_setattr = zfs_setattr },
-       VOPNAME_ACCESS,         { .vop_access = zfs_access },
-       VOPNAME_RENAME,         { .vop_rename = zfs_rename },
-       VOPNAME_READLINK,       { .vop_readlink = zfs_readlink },
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_FID,            { .vop_fid = zfs_fid },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       VOPNAME_VNEVENT,        { .vop_vnevent = fs_vnevent_support },
-       NULL,                   NULL
-};
-
-/*
- * special share hidden files vnode operations template
- */
-vnodeops_t *zfs_sharevnodeops;
-const fs_operation_def_t zfs_sharevnodeops_template[] = {
-       VOPNAME_GETATTR,        { .vop_getattr = zfs_getattr },
-       VOPNAME_ACCESS,         { .vop_access = zfs_access },
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_FID,            { .vop_fid = zfs_fid },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       VOPNAME_GETSECATTR,     { .vop_getsecattr = zfs_getsecattr },
-       VOPNAME_SETSECATTR,     { .vop_setsecattr = zfs_setsecattr },
-       VOPNAME_VNEVENT,        { .vop_vnevent = fs_vnevent_support },
-       NULL,                   NULL
-};
-
-/*
- * Extended attribute directory vnode operations template
- *     This template is identical to the directory vnodes
- *     operation template except for restricted operations:
- *             VOP_MKDIR()
- *             VOP_SYMLINK()
- * Note that there are other restrictions embedded in:
- *     zfs_create()    - restrict type to VREG
- *     zfs_link()      - no links into/out of attribute space
- *     zfs_rename()    - no moves into/out of attribute space
- */
-vnodeops_t *zfs_xdvnodeops;
-const fs_operation_def_t zfs_xdvnodeops_template[] = {
-       VOPNAME_OPEN,           { .vop_open = zfs_open },
-       VOPNAME_CLOSE,          { .vop_close = zfs_close },
-       VOPNAME_IOCTL,          { .vop_ioctl = zfs_ioctl },
-       VOPNAME_GETATTR,        { .vop_getattr = zfs_getattr },
-       VOPNAME_SETATTR,        { .vop_setattr = zfs_setattr },
-       VOPNAME_ACCESS,         { .vop_access = zfs_access },
-       VOPNAME_LOOKUP,         { .vop_lookup = zfs_lookup },
-       VOPNAME_CREATE,         { .vop_create = zfs_create },
-       VOPNAME_REMOVE,         { .vop_remove = zfs_remove },
-       VOPNAME_LINK,           { .vop_link = zfs_link },
-       VOPNAME_RENAME,         { .vop_rename = zfs_rename },
-       VOPNAME_MKDIR,          { .error = zfs_inval },
-       VOPNAME_RMDIR,          { .vop_rmdir = zfs_rmdir },
-       VOPNAME_READDIR,        { .vop_readdir = zfs_readdir },
-       VOPNAME_SYMLINK,        { .error = zfs_inval },
-       VOPNAME_FSYNC,          { .vop_fsync = zfs_fsync },
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_FID,            { .vop_fid = zfs_fid },
-       VOPNAME_SEEK,           { .vop_seek = zfs_seek },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       VOPNAME_GETSECATTR,     { .vop_getsecattr = zfs_getsecattr },
-       VOPNAME_SETSECATTR,     { .vop_setsecattr = zfs_setsecattr },
-       VOPNAME_VNEVENT,        { .vop_vnevent = fs_vnevent_support },
-       NULL,                   NULL
-};
-
-/*
- * Error vnode operations template
- */
-vnodeops_t *zfs_evnodeops;
-const fs_operation_def_t zfs_evnodeops_template[] = {
-       VOPNAME_INACTIVE,       { .vop_inactive = zfs_inactive },
-       VOPNAME_PATHCONF,       { .vop_pathconf = zfs_pathconf },
-       NULL,                   NULL
-};
+#if defined(_KERNEL) && defined(HAVE_SPL)
+module_param(zfs_read_chunk_size, long, 0644);
+MODULE_PARM_DESC(zfs_read_chunk_size, "Bytes to read per chunk");
+#endif