Fix arc_adapt() spinning in iterate_supers_type()
[zfs.git] / module / zfs / zpl_super.c
index 0e6e936..eee4a50 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/zfs_vfsops.h>
 #include <sys/zfs_vnops.h>
 #include <sys/zfs_znode.h>
+#include <sys/zfs_ctldir.h>
 #include <sys/zpl.h>
 
 
@@ -48,6 +49,25 @@ zpl_inode_destroy(struct inode *ip)
 }
 
 /*
+ * Called from __mark_inode_dirty() to reflect that something in the
+ * inode has changed.  We use it to ensure the znode system attributes
+ * are always strictly update to date with respect to the inode.
+ */
+#ifdef HAVE_DIRTY_INODE_WITH_FLAGS
+static void
+zpl_dirty_inode(struct inode *ip, int flags)
+{
+       zfs_dirty_inode(ip, flags);
+}
+#else
+static void
+zpl_dirty_inode(struct inode *ip)
+{
+       zfs_dirty_inode(ip, 0);
+}
+#endif /* HAVE_DIRTY_INODE_WITH_FLAGS */
+
+/*
  * When ->drop_inode() is called its return value indicates if the
  * inode should be evicted from the inode cache.  If the inode is
  * unhashed and has no links the default policy is to evict it
@@ -63,10 +83,15 @@ zpl_inode_destroy(struct inode *ip)
  * This elaborate mechanism was replaced by ->evict_inode() which
  * does the job of both ->delete_inode() and ->clear_inode().  It
  * will be called exactly once, and when it returns the inode must
- * be in a state where it can simply be freed.  The ->evict_inode()
- * callback must minimally truncate the inode pages, and call
- * end_writeback() to complete all outstanding writeback for the
- * inode.  After this is complete evict inode can cleanup any
+ * be in a state where it can simply be freed.i
+ *
+ * The ->evict_inode() callback must minimally truncate the inode pages,
+ * and call clear_inode().  For 2.6.35 and later kernels this will
+ * simply update the inode state, with the sync occurring before the
+ * truncate in evict().  For earlier kernels clear_inode() maps to
+ * end_writeback() which is responsible for completing all outstanding
+ * write back.  In either case, once this is done it is safe to cleanup
+ * any remaining inode specific data via zfs_inactive().
  * remaining filesystem specific data.
  */
 #ifdef HAVE_EVICT_INODE
@@ -74,7 +99,7 @@ static void
 zpl_evict_inode(struct inode *ip)
 {
        truncate_setsize(ip, 0);
-       end_writeback(ip);
+       clear_inode(ip);
        zfs_inactive(ip);
 }
 
@@ -139,6 +164,20 @@ zpl_remount_fs(struct super_block *sb, int *flags, char *data)
        return (error);
 }
 
+static void
+zpl_umount_begin(struct super_block *sb)
+{
+       zfs_sb_t *zsb = sb->s_fs_info;
+       int count;
+
+       /*
+        * Best effort to unmount snapshots in .zfs/snapshot/.  Normally this
+        * isn't required because snapshots have the MNT_SHRINKABLE flag set.
+        */
+       if (zsb->z_ctldir)
+               (void) zfsctl_unmount_snapshots(zsb, MNT_FORCE, &count);
+}
+
 /*
  * The Linux VFS automatically handles the following flags:
  * MNT_NOSUID, MNT_NODEV, MNT_NOEXEC, MNT_NOATIME, MNT_READONLY
@@ -199,14 +238,12 @@ zpl_get_sb(struct file_system_type *fs_type, int flags,
 static void
 zpl_kill_sb(struct super_block *sb)
 {
-#ifdef HAVE_SNAPSHOT
-       zfs_sb_t *zsb = sb->s_fs_info;
-
-       if (zsb && dmu_objset_is_snapshot(zsb->z_os))
-               zfs_snap_destroy(zsb);
-#endif /* HAVE_SNAPSHOT */
-
+       zfs_preumount(sb);
        kill_anon_super(sb);
+
+#ifdef HAVE_S_INSTANCES_LIST_HEAD
+       sb->s_instances.next = &(zpl_fs_type.fs_supers);
+#endif /* HAVE_S_INSTANCES_LIST_HEAD */
 }
 
 #ifdef HAVE_SHRINK
@@ -292,7 +329,7 @@ zpl_free_cached_objects(struct super_block *sb, int nr_to_scan)
 const struct super_operations zpl_super_operations = {
        .alloc_inode            = zpl_inode_alloc,
        .destroy_inode          = zpl_inode_destroy,
-       .dirty_inode            = NULL,
+       .dirty_inode            = zpl_dirty_inode,
        .write_inode            = NULL,
        .drop_inode             = NULL,
 #ifdef HAVE_EVICT_INODE
@@ -302,10 +339,10 @@ const struct super_operations zpl_super_operations = {
        .delete_inode           = zpl_inode_delete,
 #endif /* HAVE_EVICT_INODE */
        .put_super              = zpl_put_super,
-       .write_super            = NULL,
        .sync_fs                = zpl_sync_fs,
        .statfs                 = zpl_statfs,
        .remount_fs             = zpl_remount_fs,
+       .umount_begin           = zpl_umount_begin,
        .show_options           = zpl_show_options,
        .show_stats             = NULL,
 #ifdef HAVE_NR_CACHED_OBJECTS