Whamcloud - gitweb
Branch b1_6
authoranserper <anserper>
Wed, 13 Feb 2008 23:50:29 +0000 (23:50 +0000)
committeranserper <anserper>
Wed, 13 Feb 2008 23:50:29 +0000 (23:50 +0000)
b=14591
i=tianzy
i=andrew.perepechko

lustre/kernel_patches/patches/quota-fix-oops-in-invalidate_dquots.patch [new file with mode: 0644]

diff --git a/lustre/kernel_patches/patches/quota-fix-oops-in-invalidate_dquots.patch b/lustre/kernel_patches/patches/quota-fix-oops-in-invalidate_dquots.patch
new file mode 100644 (file)
index 0000000..b8c6b0d
--- /dev/null
@@ -0,0 +1,127 @@
+From: Jan Kara <jack@suse.cz>
+Date: Thu, 23 Mar 2006 11:00:17 +0000 (-0800)
+Subject: [PATCH] Fix oops in invalidate_dquots()
+X-Git-Tag: v2.6.17-rc1~1059
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=6362e4d4eda61efb04ac1cdae32e48ac6d90b701
+
+[PATCH] Fix oops in invalidate_dquots()
+
+When quota is being turned off we assumed that all the references to dquots
+were already dropped.  That need not be true as inodes being deleted are
+not on superblock's inodes list and hence we need not reach it when
+removing quota references from inodes.  So invalidate_dquots() has to wait
+for all the users of dquots (as quota is already marked as turned off, no
+new references can be acquired and so this is bound to happen rather
+early).  When we do this, we can also remove the iprune_sem locking as it
+was protecting us against exactly the same problem when freeing inodes
+icache memory.
+
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Andrew Morton <akpm@osdl.org>
+Signed-off-by: Linus Torvalds <torvalds@osdl.org>
+---
+
+diff --git a/fs/dquot.c b/fs/dquot.c
+index 1966c89..9376a43 100644
+--- a/fs/dquot.c
++++ b/fs/dquot.c
+@@ -118,8 +118,7 @@
+  * spinlock to internal buffers before writing.
+  *
+  * Lock ordering (including related VFS locks) is the following:
+- *   i_mutex > dqonoff_sem > iprune_sem > journal_lock > dqptr_sem >
+- *   > dquot->dq_lock > dqio_sem
++ *  i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem
+  * i_mutex on quota files is special (it's below dqio_sem)
+  */
+@@ -407,23 +406,49 @@ out_dqlock:
+ /* Invalidate all dquots on the list. Note that this function is called after
+  * quota is disabled and pointers from inodes removed so there cannot be new
+- * quota users. Also because we hold dqonoff_sem there can be no quota users
+- * for this sb+type at all. */
++ * quota users. There can still be some users of quotas due to inodes being
++ * just deleted or pruned by prune_icache() (those are not attached to any
++ * list). We have to wait for such users.
++ */
+ static void invalidate_dquots(struct super_block *sb, int type)
+ {
+       struct dquot *dquot, *tmp;
++restart:
+       spin_lock(&dq_list_lock);
+       list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
+               if (dquot->dq_sb != sb)
+                       continue;
+               if (dquot->dq_type != type)
+                       continue;
+-#ifdef __DQUOT_PARANOIA
+-              if (atomic_read(&dquot->dq_count))
+-                      BUG();
+-#endif
+-              /* Quota now has no users and it has been written on last dqput() */
++              /* Wait for dquot users */
++              if (atomic_read(&dquot->dq_count)) {
++                      DEFINE_WAIT(wait);
++
++                      atomic_inc(&dquot->dq_count);
++                      prepare_to_wait(&dquot->dq_wait_unused, &wait,
++                                      TASK_UNINTERRUPTIBLE);
++                      spin_unlock(&dq_list_lock);
++                      /* Once dqput() wakes us up, we know it's time to free
++                       * the dquot.
++                       * IMPORTANT: we rely on the fact that there is always
++                       * at most one process waiting for dquot to free.
++                       * Otherwise dq_count would be > 1 and we would never
++                       * wake up.
++                       */
++                      if (atomic_read(&dquot->dq_count) > 1)
++                              schedule();
++                      finish_wait(&dquot->dq_wait_unused, &wait);
++                      dqput(dquot);
++                      /* At this moment dquot() need not exist (it could be
++                       * reclaimed by prune_dqcache(). Hence we must
++                       * restart. */
++                      goto restart;
++              }
++              /*
++               * Quota now has no users and it has been written on last
++               * dqput()
++               */
+               remove_dquot_hash(dquot);
+               remove_free_dquot(dquot);
+               remove_inuse(dquot);
+@@ -540,6 +565,10 @@ we_slept:
+       if (atomic_read(&dquot->dq_count) > 1) {
+               /* We have more than one user... nothing to do */
+               atomic_dec(&dquot->dq_count);
++              /* Releasing dquot during quotaoff phase? */
++              if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
++                  atomic_read(&dquot->dq_count) == 1)
++                      wake_up(&dquot->dq_wait_unused);
+               spin_unlock(&dq_list_lock);
+               return;
+       }
+@@ -581,6 +610,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
+       INIT_LIST_HEAD(&dquot->dq_inuse);
+       INIT_HLIST_NODE(&dquot->dq_hash);
+       INIT_LIST_HEAD(&dquot->dq_dirty);
++      init_waitqueue_head(&dquot->dq_wait_unused);
+       dquot->dq_sb = sb;
+       dquot->dq_type = type;
+       atomic_set(&dquot->dq_count, 1);
+@@ -732,13 +762,9 @@ static void drop_dquot_ref(struct super_block *sb, int type)
+ {
+       LIST_HEAD(tofree_head);
+-      /* We need to be guarded against prune_icache to reach all the
+-       * inodes - otherwise some can be on the local list of prune_icache */
+-      down(&iprune_sem);
+       down_write(&sb_dqopt(sb)->dqptr_sem);
+       remove_dquot_ref(sb, type, &tofree_head);
+       up_write(&sb_dqopt(sb)->dqptr_sem);
+-      up(&iprune_sem);
+       put_dquot_list(&tofree_head);
+ }