From 59ad8483a286278def59ca24a9ab1d874d24ad24 Mon Sep 17 00:00:00 2001 From: anserper Date: Wed, 13 Feb 2008 23:50:29 +0000 Subject: [PATCH] Branch b1_6 b=14591 i=tianzy i=andrew.perepechko --- .../quota-fix-oops-in-invalidate_dquots.patch | 127 +++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 lustre/kernel_patches/patches/quota-fix-oops-in-invalidate_dquots.patch 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 index 0000000..b8c6b0d --- /dev/null +++ b/lustre/kernel_patches/patches/quota-fix-oops-in-invalidate_dquots.patch @@ -0,0 +1,127 @@ +From: Jan Kara +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 +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + +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); + } + -- 1.8.3.1