Make ZFS filesystem id persistent across different machines
[zfs.git] / module / zfs / zfs_vfsops.c
index 1763d17..8fe457e 100644 (file)
@@ -56,6 +56,7 @@
 #include <sys/modctl.h>
 #include <sys/refstr.h>
 #include <sys/zfs_ioctl.h>
+#include <sys/zfs_ctldir.h>
 #include <sys/zfs_fuid.h>
 #include <sys/bootconf.h>
 #include <sys/sunddi.h>
 
 /*ARGSUSED*/
 int
-zfs_sync(zfs_sb_t *zsb, short flag, cred_t *cr)
+zfs_sync(struct super_block *sb, int wait, cred_t *cr)
 {
+       zfs_sb_t *zsb = sb->s_fs_info;
+
        /*
         * Data integrity is job one.  We don't want a compromised kernel
         * writing to the storage pool, so we never sync during panic.
@@ -78,6 +81,13 @@ zfs_sync(zfs_sb_t *zsb, short flag, cred_t *cr)
        if (unlikely(oops_in_progress))
                return (0);
 
+       /*
+        * Semantically, the only requirement is that the sync be initiated.
+        * The DMU syncs out txgs frequently, so there's nothing to do.
+        */
+       if (!wait)
+               return (0);
+
        if (zsb != NULL) {
                /*
                 * Sync a specific filesystem.
@@ -87,19 +97,14 @@ zfs_sync(zfs_sb_t *zsb, short flag, cred_t *cr)
                ZFS_ENTER(zsb);
                dp = dmu_objset_pool(zsb->z_os);
 
-#ifdef HAVE_SHUTDOWN
                /*
                 * If the system is shutting down, then skip any
                 * filesystems which may exist on a suspended pool.
-                *
-                * XXX: This can be implemented using the Linux reboot
-                *      notifiers: {un}register_reboot_notifier().
                 */
-               if (sys_shutdown && spa_suspended(dp->dp_spa)) {
+               if (spa_suspended(dp->dp_spa)) {
                        ZFS_EXIT(zsb);
                        return (0);
                }
-#endif /* HAVE_SHUTDOWN */
 
                if (zsb->z_log != NULL)
                        zil_commit(zsb->z_log, 0);
@@ -118,22 +123,17 @@ zfs_sync(zfs_sb_t *zsb, short flag, cred_t *cr)
 }
 EXPORT_SYMBOL(zfs_sync);
 
+boolean_t
+zfs_is_readonly(zfs_sb_t *zsb)
+{
+       return (!!(zsb->z_sb->s_flags & MS_RDONLY));
+}
+EXPORT_SYMBOL(zfs_is_readonly);
+
 static void
 atime_changed_cb(void *arg, uint64_t newval)
 {
-       zfs_sb_t *zsb = arg;
-       struct super_block *sb = zsb->z_sb;
-       struct vfsmount *vfs = zsb->z_vfs;
-
-       if (newval == TRUE) {
-               vfs->mnt_flags &= ~MNT_NOATIME;
-               sb->s_flags &= ~MS_NOATIME;
-               zsb->z_atime = TRUE;
-       } else {
-               vfs->mnt_flags |= MNT_NOATIME;
-               sb->s_flags |= MS_NOATIME;
-               zsb->z_atime = FALSE;
-       }
+       ((zfs_sb_t *)arg)->z_atime = newval;
 }
 
 static void
@@ -141,10 +141,15 @@ xattr_changed_cb(void *arg, uint64_t newval)
 {
        zfs_sb_t *zsb = arg;
 
-       if (newval == TRUE) {
-               zsb->z_flags |= ZSB_XATTR_USER;
+       if (newval == ZFS_XATTR_OFF) {
+               zsb->z_flags &= ~ZSB_XATTR;
        } else {
-               zsb->z_flags &= ~ZSB_XATTR_USER;
+               zsb->z_flags |= ZSB_XATTR;
+
+               if (newval == ZFS_XATTR_SA)
+                       zsb->z_xattr_sa = B_TRUE;
+               else
+                       zsb->z_xattr_sa = B_FALSE;
        }
 }
 
@@ -165,84 +170,44 @@ readonly_changed_cb(void *arg, uint64_t newval)
 {
        zfs_sb_t *zsb = arg;
        struct super_block *sb = zsb->z_sb;
-       struct vfsmount *vfs = zsb->z_vfs;
 
-       if (newval) {
-               vfs->mnt_flags |= MNT_READONLY;
+       if (sb == NULL)
+               return;
+
+       if (newval)
                sb->s_flags |= MS_RDONLY;
-       } else {
-               vfs->mnt_flags &= ~MNT_READONLY;
+       else
                sb->s_flags &= ~MS_RDONLY;
-       }
 }
 
 static void
 devices_changed_cb(void *arg, uint64_t newval)
 {
-       zfs_sb_t *zsb = arg;
-       struct super_block *sb = zsb->z_sb;
-       struct vfsmount *vfs = zsb->z_vfs;
-
-       if (newval == FALSE) {
-               vfs->mnt_flags |= MNT_NODEV;
-               sb->s_flags |= MS_NODEV;
-       } else {
-               vfs->mnt_flags &= ~MNT_NODEV;
-               sb->s_flags &= ~MS_NODEV;
-       }
 }
 
 static void
 setuid_changed_cb(void *arg, uint64_t newval)
 {
-       zfs_sb_t *zsb = arg;
-       struct super_block *sb = zsb->z_sb;
-       struct vfsmount *vfs = zsb->z_vfs;
-
-       if (newval == FALSE) {
-               vfs->mnt_flags |= MNT_NOSUID;
-               sb->s_flags |= MS_NOSUID;
-       } else {
-               vfs->mnt_flags &= ~MNT_NOSUID;
-               sb->s_flags &= ~MS_NOSUID;
-       }
 }
 
 static void
 exec_changed_cb(void *arg, uint64_t newval)
 {
-       zfs_sb_t *zsb = arg;
-       struct super_block *sb = zsb->z_sb;
-       struct vfsmount *vfs = zsb->z_vfs;
-
-       if (newval == FALSE) {
-               vfs->mnt_flags |= MNT_NOEXEC;
-               sb->s_flags |= MS_NOEXEC;
-       } else {
-               vfs->mnt_flags &= ~MNT_NOEXEC;
-               sb->s_flags &= ~MS_NOEXEC;
-       }
 }
 
-/*
- * The nbmand mount option can be changed at mount time.
- * We can't allow it to be toggled on live file systems or incorrect
- * behavior may be seen from cifs clients
- *
- * This property isn't registered via dsl_prop_register(), but this callback
- * will be called when a file system is first mounted
- */
 static void
 nbmand_changed_cb(void *arg, uint64_t newval)
 {
        zfs_sb_t *zsb = arg;
        struct super_block *sb = zsb->z_sb;
 
-       if (newval == TRUE) {
+       if (sb == NULL)
+               return;
+
+       if (newval == TRUE)
                sb->s_flags |= MS_MANDLOCK;
-       } else {
+       else
                sb->s_flags &= ~MS_MANDLOCK;
-       }
 }
 
 static void
@@ -266,58 +231,12 @@ acl_inherit_changed_cb(void *arg, uint64_t newval)
 int
 zfs_register_callbacks(zfs_sb_t *zsb)
 {
-       struct vfsmount *vfsp = zsb->z_vfs;
        struct dsl_dataset *ds = NULL;
        objset_t *os = zsb->z_os;
-       uint64_t nbmand;
-       boolean_t readonly = B_FALSE;
-       boolean_t setuid = B_TRUE;
-       boolean_t exec = B_TRUE;
-       boolean_t devices = B_TRUE;
-       boolean_t xattr = B_TRUE;
-       boolean_t atime = B_TRUE;
-       char osname[MAXNAMELEN];
        int error = 0;
 
-       /*
-        * While Linux allows multiple vfs mounts per super block we have
-        * limited it artificially to one in zfs_fill_super.  Thus it is
-        * safe for us to modify the vfs mount fails through the callbacks.
-        */
-       if ((vfsp->mnt_flags & MNT_READONLY) ||
-           !spa_writeable(dmu_objset_spa(os)))
-               readonly = B_TRUE;
-
-       if (vfsp->mnt_flags & MNT_NOSUID) {
-               devices = B_FALSE;
-               setuid = B_FALSE;
-       } else {
-               if (vfsp->mnt_flags & MNT_NODEV)
-                       devices = B_FALSE;
-       }
-
-       if (vfsp->mnt_flags & MNT_NOEXEC)
-               exec = B_FALSE;
-
-       if (vfsp->mnt_flags & MNT_NOATIME)
-               atime = B_FALSE;
-
-       /*
-        * nbmand is a special property which may only be changed at
-        * mount time.  Unfortunately, Linux does not have a VFS mount
-        * flag instead this is a super block flag.  So setting this
-        * option at mount time will have to wait until we can parse
-        * the mount option string.  For now we rely on the nbmand
-        * value stored with the object set.  Additional mount option
-        * string to be handled:
-        *
-        *   case: sensitive|insensitive|mixed
-        *   zerocopy: on|off
-        */
-
-       dmu_objset_name(os, osname);
-       if ((error = dsl_prop_get_integer(osname, "nbmand", &nbmand, NULL)))
-               return (error);
+       if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os)))
+               readonly_changed_cb(zsb, B_TRUE);
 
        /*
         * Register property callbacks.
@@ -347,20 +266,11 @@ zfs_register_callbacks(zfs_sb_t *zsb)
            "aclinherit", acl_inherit_changed_cb, zsb);
        error = error ? error : dsl_prop_register(ds,
            "vscan", vscan_changed_cb, zsb);
+       error = error ? error : dsl_prop_register(ds,
+           "nbmand", nbmand_changed_cb, zsb);
        if (error)
                goto unregister;
 
-       /*
-        * Invoke our callbacks to set required flags.
-        */
-       readonly_changed_cb(zsb, readonly);
-       setuid_changed_cb(zsb, setuid);
-       exec_changed_cb(zsb, exec);
-       devices_changed_cb(zsb, devices);
-       xattr_changed_cb(zsb, xattr);
-       atime_changed_cb(zsb, atime);
-       nbmand_changed_cb(zsb, nbmand);
-
        return (0);
 
 unregister:
@@ -380,6 +290,7 @@ unregister:
        (void) dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb,
            zsb);
        (void) dsl_prop_unregister(ds, "vscan", vscan_changed_cb, zsb);
+       (void) dsl_prop_unregister(ds, "nbmand", nbmand_changed_cb, zsb);
 
        return (error);
 }
@@ -673,7 +584,7 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
        int i, error;
        uint64_t sa_obj;
 
-       zsb = kmem_zalloc(sizeof (zfs_sb_t), KM_SLEEP);
+       zsb = kmem_zalloc(sizeof (zfs_sb_t), KM_SLEEP | KM_NODEBUG);
 
        /*
         * We claim to always be readonly so we can open snapshots;
@@ -690,7 +601,7 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
         * Should probably make this a kmem cache, shuffle fields,
         * and just bzero up to z_hold_mtx[].
         */
-       zsb->z_vfs = NULL;
+       zsb->z_sb = NULL;
        zsb->z_parent = zsb;
        zsb->z_max_blksz = SPA_MAXBLOCKSIZE;
        zsb->z_show_ctldir = ZFS_SNAPDIR_VISIBLE;
@@ -736,7 +647,11 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
                error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1,
                    &sa_obj);
                if (error)
-                       return (error);
+                       goto out;
+
+               error = zfs_get_zplprop(os, ZFS_PROP_XATTR, &zval);
+               if ((error == 0) && (zval == ZFS_XATTR_SA))
+                       zsb->z_xattr_sa = B_TRUE;
        } else {
                /*
                 * Pre SA versions file systems should never touch
@@ -796,6 +711,10 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
        for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
                mutex_init(&zsb->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL);
 
+       avl_create(&zsb->z_ctldir_snaps, snapentry_compare,
+           sizeof (zfs_snapentry_t), offsetof(zfs_snapentry_t, se_node));
+       mutex_init(&zsb->z_ctldir_lock, NULL, MUTEX_DEFAULT, NULL);
+
        *zsbp = zsb;
        return (0);
 
@@ -805,8 +724,9 @@ out:
        kmem_free(zsb, sizeof (zfs_sb_t));
        return (error);
 }
+EXPORT_SYMBOL(zfs_sb_create);
 
-static int
+int
 zfs_sb_setup(zfs_sb_t *zsb, boolean_t mounting)
 {
        int error;
@@ -836,9 +756,9 @@ zfs_sb_setup(zfs_sb_t *zsb, boolean_t mounting)
                 * During replay we remove the read only flag to
                 * allow replays to succeed.
                 */
-               readonly = zsb->z_vfs->mnt_flags & MNT_READONLY;
+               readonly = zfs_is_readonly(zsb);
                if (readonly != 0)
-                       zsb->z_vfs->mnt_flags &= ~MNT_READONLY;
+                       readonly_changed_cb(zsb, B_FALSE);
                else
                        zfs_unlinked_drain(zsb);
 
@@ -879,11 +799,15 @@ zfs_sb_setup(zfs_sb_t *zsb, boolean_t mounting)
                                zsb->z_replay = B_FALSE;
                        }
                }
-               zsb->z_vfs->mnt_flags |= readonly; /* restore readonly bit */
+
+               /* restore readonly bit */
+               if (readonly != 0)
+                       readonly_changed_cb(zsb, B_TRUE);
        }
 
        return (0);
 }
+EXPORT_SYMBOL(zfs_sb_setup);
 
 void
 zfs_sb_free(zfs_sb_t *zsb)
@@ -900,8 +824,11 @@ zfs_sb_free(zfs_sb_t *zsb)
        rw_destroy(&zsb->z_fuid_lock);
        for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
                mutex_destroy(&zsb->z_hold_mtx[i]);
+       mutex_destroy(&zsb->z_ctldir_lock);
+       avl_destroy(&zsb->z_ctldir_snaps);
        kmem_free(zsb, sizeof (zfs_sb_t));
 }
+EXPORT_SYMBOL(zfs_sb_free);
 
 static void
 zfs_set_fuid_feature(zfs_sb_t *zsb)
@@ -950,6 +877,9 @@ zfs_unregister_callbacks(zfs_sb_t *zsb)
 
                VERIFY(dsl_prop_unregister(ds, "vscan",
                    vscan_changed_cb, zsb) == 0);
+
+               VERIFY(dsl_prop_unregister(ds, "nbmand",
+                   nbmand_changed_cb, zsb) == 0);
        }
 }
 EXPORT_SYMBOL(zfs_unregister_callbacks);
@@ -982,6 +912,7 @@ zfs_check_global_label(const char *dsname, const char *hexsl)
        }
        return (EACCES);
 }
+EXPORT_SYMBOL(zfs_check_global_label);
 #endif /* HAVE_MLSLABEL */
 
 int
@@ -989,6 +920,7 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
 {
        zfs_sb_t *zsb = dentry->d_sb->s_fs_info;
        uint64_t refdbytes, availbytes, usedobjs, availobjs;
+       uint64_t fsid;
        uint32_t bshift;
 
        ZFS_ENTER(zsb);
@@ -996,12 +928,17 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
        dmu_objset_space(zsb->z_os,
            &refdbytes, &availbytes, &usedobjs, &availobjs);
 
+       fsid = dmu_objset_fsid_guid(zsb->z_os);
        /*
-        * The underlying storage pool actually uses multiple block sizes.
-        * We report the fragsize as the smallest block size we support,
-        * and we report our blocksize as the filesystem's maximum blocksize.
+        * The underlying storage pool actually uses multiple block
+        * size.  Under Solaris frsize (fragment size) is reported as
+        * the smallest block size we support, and bsize (block size)
+        * as the filesystem's maximum block size.  Unfortunately,
+        * under Linux the fragment size and block size are often used
+        * interchangeably.  Thus we are forced to report both of them
+        * as the filesystem's maximum block size.
         */
-       statp->f_frsize = 1UL << SPA_MINBLOCKSHIFT;
+       statp->f_frsize = zsb->z_max_blksz;
        statp->f_bsize = zsb->z_max_blksz;
        bshift = fls(statp->f_bsize) - 1;
 
@@ -1023,10 +960,10 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
         * For f_ffree, report the smaller of the number of object available
         * and the number of blocks (each object will take at least a block).
         */
-       statp->f_ffree = MIN(availobjs, statp->f_bfree);
+       statp->f_ffree = MIN(availobjs, availbytes >> DNODE_SHIFT);
        statp->f_files = statp->f_ffree + usedobjs;
-       statp->f_fsid.val[0] = 0; /* XXX: Map up some unique ID */
-       statp->f_fsid.val[1] = 0;
+       statp->f_fsid.val[0] = (uint32_t)fsid;
+       statp->f_fsid.val[1] = (uint32_t)(fsid >> 32);
        statp->f_type = ZFS_SUPER_MAGIC;
        statp->f_namelen = ZFS_MAXNAMELEN;
 
@@ -1058,6 +995,26 @@ zfs_root(zfs_sb_t *zsb, struct inode **ipp)
 }
 EXPORT_SYMBOL(zfs_root);
 
+#ifdef HAVE_SHRINK
+int
+zfs_sb_prune(struct super_block *sb, unsigned long nr_to_scan, int *objects)
+{
+       zfs_sb_t *zsb = sb->s_fs_info;
+       struct shrinker *shrinker = &sb->s_shrink;
+       struct shrink_control sc = {
+               .nr_to_scan = nr_to_scan,
+               .gfp_mask = GFP_KERNEL,
+       };
+
+       ZFS_ENTER(zsb);
+       *objects = (*shrinker->shrink)(shrinker, &sc);
+       ZFS_EXIT(zsb);
+
+       return (0);
+}
+EXPORT_SYMBOL(zfs_sb_prune);
+#endif /* HAVE_SHRINK */
+
 /*
  * Teardown the zfs_sb_t::z_os.
  *
@@ -1065,23 +1022,29 @@ EXPORT_SYMBOL(zfs_root);
  * and 'z_teardown_inactive_lock' held.
  */
 int
-zfsvfs_teardown(zfs_sb_t *zsb, boolean_t unmounting)
+zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting)
 {
        znode_t *zp;
 
        rrw_enter(&zsb->z_teardown_lock, RW_WRITER, FTAG);
 
-#ifdef HAVE_DNLC
        if (!unmounting) {
                /*
-                * We purge the parent filesystem's vfsp as the parent
-                * filesystem and all of its snapshots have their vnode's
-                * v_vfsp set to the parent's filesystem's vfsp.  Note,
-                * 'z_parent' is self referential for non-snapshots.
+                * We purge the parent filesystem's super block as the
+                * parent filesystem and all of its snapshots have their
+                * inode's super block set to the parent's filesystem's
+                * super block.  Note,  'z_parent' is self referential
+                * for non-snapshots.
                 */
-               (void) dnlc_purge_vfsp(zsb->z_parent->z_vfs, 0);
+               shrink_dcache_sb(zsb->z_parent->z_sb);
+               (void) spl_invalidate_inodes(zsb->z_parent->z_sb, 0);
        }
-#endif /* HAVE_DNLC */
+
+       /*
+        * Drain the iput_taskq to ensure all active references to the
+        * zfs_sb_t have been handled only then can it be safely destroyed.
+        */
+       taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool(zsb->z_os)));
 
        /*
         * Close the zil. NB: Can't close the zil while zfs_inactive
@@ -1150,12 +1113,17 @@ zfsvfs_teardown(zfs_sb_t *zsb, boolean_t unmounting)
         * Evict cached data
         */
        if (dmu_objset_is_dirty_anywhere(zsb->z_os))
-               if (!(zsb->z_vfs->mnt_flags & MNT_READONLY))
+               if (!zfs_is_readonly(zsb))
                        txg_wait_synced(dmu_objset_pool(zsb->z_os), 0);
        (void) dmu_objset_evict_dbufs(zsb->z_os);
 
        return (0);
 }
+EXPORT_SYMBOL(zfs_sb_teardown);
+
+#if defined(HAVE_BDI) && !defined(HAVE_BDI_SETUP_AND_REGISTER)
+atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
+#endif /* HAVE_BDI && !HAVE_BDI_SETUP_AND_REGISTER */
 
 int
 zfs_domount(struct super_block *sb, void *data, int silent)
@@ -1167,17 +1135,6 @@ zfs_domount(struct super_block *sb, void *data, int silent)
        uint64_t recordsize;
        int error;
 
-       /*
-        * Linux allows multiple vfs mounts per super block.  However, the
-        * zfs_sb_t only contains a pointer for a single vfs mount.  This
-        * back reference in the long term could be extended to a list of
-        * vfs mounts if a hook were added to the kernel to notify us when
-        * a vfsmount is destroyed.  Until then we must limit the number
-        * of mounts per super block to one.
-        */
-       if (atomic_read(&sb->s_active) > 1)
-               return (EBUSY);
-
        error = zfs_sb_create(osname, &zsb);
        if (error)
                return (error);
@@ -1187,7 +1144,6 @@ zfs_domount(struct super_block *sb, void *data, int silent)
                goto out;
 
        zsb->z_sb = sb;
-       zsb->z_vfs = zmd->z_vfs;
        sb->s_fs_info = zsb;
        sb->s_magic = ZFS_SUPER_MAGIC;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -1195,12 +1151,27 @@ zfs_domount(struct super_block *sb, void *data, int silent)
        sb->s_blocksize = recordsize;
        sb->s_blocksize_bits = ilog2(recordsize);
 
+#ifdef HAVE_BDI
+       /*
+        * 2.6.32 API change,
+        * Added backing_device_info (BDI) per super block interfaces.  A BDI
+        * must be configured when using a non-device backed filesystem for
+        * proper writeback.  This is not required for older pdflush kernels.
+        *
+        * NOTE: Linux read-ahead is disabled in favor of zfs read-ahead.
+        */
+       zsb->z_bdi.ra_pages = 0;
+       sb->s_bdi = &zsb->z_bdi;
+
+       error = -bdi_setup_and_register(&zsb->z_bdi, "zfs", BDI_CAP_MAP_COPY);
+       if (error)
+               goto out;
+#endif /* HAVE_BDI */
+
        /* Set callback operations for the file system. */
        sb->s_op = &zpl_super_operations;
        sb->s_xattr = zpl_xattr_handlers;
-#ifdef HAVE_EXPORTS
-        sb->s_export_op = &zpl_export_operations;
-#endif /* HAVE_EXPORTS */
+       sb->s_export_op = &zpl_export_operations;
 
        /* Set features for file system. */
        zfs_set_fuid_feature(zsb);
@@ -1221,9 +1192,6 @@ zfs_domount(struct super_block *sb, void *data, int silent)
                mutex_exit(&zsb->z_os->os_user_ptr_lock);
        } else {
                error = zfs_sb_setup(zsb, B_TRUE);
-#ifdef HAVE_SNAPSHOT
-               (void) zfs_snap_create(zsb);
-#endif /* HAVE_SNAPSHOT */
        }
 
        /* Allocate a root inode for the filesystem. */
@@ -1234,12 +1202,15 @@ zfs_domount(struct super_block *sb, void *data, int silent)
        }
 
        /* Allocate a root dentry for the filesystem */
-       sb->s_root = d_alloc_root(root_inode);
+       sb->s_root = d_make_root(root_inode);
        if (sb->s_root == NULL) {
                (void) zfs_umount(sb);
                error = ENOMEM;
                goto out;
        }
+
+       if (!zsb->z_issnap)
+               zfsctl_create(zsb);
 out:
        if (error) {
                dmu_objset_disown(zsb->z_os, zsb);
@@ -1250,6 +1221,27 @@ out:
 }
 EXPORT_SYMBOL(zfs_domount);
 
+/*
+ * Called when an unmount is requested and certain sanity checks have
+ * already passed.  At this point no dentries or inodes have been reclaimed
+ * from their respective caches.  We drop the extra reference on the .zfs
+ * control directory to allow everything to be reclaimed.  All snapshots
+ * must already have been unmounted to reach this point.
+ */
+void
+zfs_preumount(struct super_block *sb)
+{
+       zfs_sb_t *zsb = sb->s_fs_info;
+
+       if (zsb != NULL && zsb->z_ctldir != NULL)
+               zfsctl_destroy(zsb);
+}
+EXPORT_SYMBOL(zfs_preumount);
+
+/*
+ * Called once all other unmount released tear down has occurred.
+ * It is our responsibility to release any remaining infrastructure.
+ */
 /*ARGSUSED*/
 int
 zfs_umount(struct super_block *sb)
@@ -1257,9 +1249,13 @@ zfs_umount(struct super_block *sb)
        zfs_sb_t *zsb = sb->s_fs_info;
        objset_t *os;
 
-       VERIFY(zfsvfs_teardown(zsb, B_TRUE) == 0);
+       VERIFY(zfs_sb_teardown(zsb, B_TRUE) == 0);
        os = zsb->z_os;
 
+#ifdef HAVE_BDI
+       bdi_destroy(sb->s_bdi);
+#endif /* HAVE_BDI */
+
        /*
         * z_os will be NULL if there was an error in
         * attempting to reopen zsb.
@@ -1284,9 +1280,20 @@ zfs_umount(struct super_block *sb)
 EXPORT_SYMBOL(zfs_umount);
 
 int
-zfs_vget(struct vfsmount *vfsp, struct inode **ipp, fid_t *fidp)
+zfs_remount(struct super_block *sb, int *flags, char *data)
+{
+       /*
+        * All namespace flags (MNT_*) and super block flags (MS_*) will
+        * be handled by the Linux VFS.  Only handle custom options here.
+        */
+       return (0);
+}
+EXPORT_SYMBOL(zfs_remount);
+
+int
+zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
 {
-       zfs_sb_t        *zsb = VTOZSB(vfsp);
+       zfs_sb_t        *zsb = sb->s_fs_info;
        znode_t         *zp;
        uint64_t        object = 0;
        uint64_t        fid_gen = 0;
@@ -1311,11 +1318,10 @@ zfs_vget(struct vfsmount *vfsp, struct inode **ipp, fid_t *fidp)
 
                ZFS_EXIT(zsb);
 
-#ifdef HAVE_SNAPSHOT
-               err = zfsctl_lookup_objset(vfsp, objsetid, &zsb);
+               err = zfsctl_lookup_objset(sb, objsetid, &zsb);
                if (err)
                        return (EINVAL);
-#endif /* HAVE_SNAPSHOT */
+
                ZFS_ENTER(zsb);
        }
 
@@ -1332,22 +1338,20 @@ zfs_vget(struct vfsmount *vfsp, struct inode **ipp, fid_t *fidp)
                return (EINVAL);
        }
 
-#ifdef HAVE_SNAPSHOT
        /* A zero fid_gen means we are in the .zfs control directories */
        if (fid_gen == 0 &&
            (object == ZFSCTL_INO_ROOT || object == ZFSCTL_INO_SNAPDIR)) {
                *ipp = zsb->z_ctldir;
                ASSERT(*ipp != NULL);
                if (object == ZFSCTL_INO_SNAPDIR) {
-                       VERIFY(zfsctl_root_lookup(*ipp, "snapshot", ipp, NULL,
-                           0, NULL, NULL, NULL, NULL, NULL) == 0);
+                       VERIFY(zfsctl_root_lookup(*ipp, "snapshot", ipp,
+                           0, kcred, NULL, NULL) == 0);
                } else {
                        igrab(*ipp);
                }
                ZFS_EXIT(zsb);
                return (0);
        }
-#endif /* HAVE_SNAPSHOT */
 
        gen_mask = -1ULL >> (64 - 8 * i);
 
@@ -1388,7 +1392,7 @@ zfs_suspend_fs(zfs_sb_t *zsb)
 {
        int error;
 
-       if ((error = zfsvfs_teardown(zsb, B_FALSE)) != 0)
+       if ((error = zfs_sb_teardown(zsb, B_FALSE)) != 0)
                return (error);
        dmu_objset_disown(zsb->z_os, zsb);
 
@@ -1568,13 +1572,16 @@ zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
        }
        return (error);
 }
+EXPORT_SYMBOL(zfs_get_zplprop);
 
 void
 zfs_init(void)
 {
+       zfsctl_init();
        zfs_znode_init();
        dmu_objset_register_type(DMU_OST_ZFS, zfs_space_delta_cb);
        register_filesystem(&zpl_fs_type);
+       (void) arc_add_prune_callback(zpl_prune_sbs, NULL);
 }
 
 void
@@ -1582,4 +1589,5 @@ zfs_fini(void)
 {
        unregister_filesystem(&zpl_fs_type);
        zfs_znode_fini();
+       zfsctl_fini();
 }