1 From: Jan Kara <jack@suse.cz>
2 Date: Thu, 23 Mar 2006 11:00:17 +0000 (-0800)
3 Subject: [PATCH] Fix oops in invalidate_dquots()
4 X-Git-Tag: v2.6.17-rc1~1059
5 X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=6362e4d4eda61efb04ac1cdae32e48ac6d90b701
7 [PATCH] Fix oops in invalidate_dquots()
9 When quota is being turned off we assumed that all the references to dquots
10 were already dropped. That need not be true as inodes being deleted are
11 not on superblock's inodes list and hence we need not reach it when
12 removing quota references from inodes. So invalidate_dquots() has to wait
13 for all the users of dquots (as quota is already marked as turned off, no
14 new references can be acquired and so this is bound to happen rather
15 early). When we do this, we can also remove the iprune_sem locking as it
16 was protecting us against exactly the same problem when freeing inodes
19 Signed-off-by: Jan Kara <jack@suse.cz>
20 Signed-off-by: Andrew Morton <akpm@osdl.org>
21 Signed-off-by: Linus Torvalds <torvalds@osdl.org>
24 diff --git a/fs/dquot.c b/fs/dquot.c
25 index 1966c89..9376a43 100644
29 * spinlock to internal buffers before writing.
31 * Lock ordering (including related VFS locks) is the following:
32 - * i_mutex > dqonoff_sem > iprune_sem > journal_lock > dqptr_sem >
33 - * > dquot->dq_lock > dqio_sem
34 + * i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem
35 * i_mutex on quota files is special (it's below dqio_sem)
38 @@ -407,23 +406,49 @@ out_dqlock:
40 /* Invalidate all dquots on the list. Note that this function is called after
41 * quota is disabled and pointers from inodes removed so there cannot be new
42 - * quota users. Also because we hold dqonoff_sem there can be no quota users
43 - * for this sb+type at all. */
44 + * quota users. There can still be some users of quotas due to inodes being
45 + * just deleted or pruned by prune_icache() (those are not attached to any
46 + * list). We have to wait for such users.
48 static void invalidate_dquots(struct super_block *sb, int type)
50 struct dquot *dquot, *tmp;
53 spin_lock(&dq_list_lock);
54 list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
55 if (dquot->dq_sb != sb)
57 if (dquot->dq_type != type)
59 -#ifdef __DQUOT_PARANOIA
60 - if (atomic_read(&dquot->dq_count))
63 - /* Quota now has no users and it has been written on last dqput() */
64 + /* Wait for dquot users */
65 + if (atomic_read(&dquot->dq_count)) {
68 + atomic_inc(&dquot->dq_count);
69 + prepare_to_wait(&dquot->dq_wait_unused, &wait,
70 + TASK_UNINTERRUPTIBLE);
71 + spin_unlock(&dq_list_lock);
72 + /* Once dqput() wakes us up, we know it's time to free
74 + * IMPORTANT: we rely on the fact that there is always
75 + * at most one process waiting for dquot to free.
76 + * Otherwise dq_count would be > 1 and we would never
79 + if (atomic_read(&dquot->dq_count) > 1)
81 + finish_wait(&dquot->dq_wait_unused, &wait);
83 + /* At this moment dquot() need not exist (it could be
84 + * reclaimed by prune_dqcache(). Hence we must
89 + * Quota now has no users and it has been written on last
92 remove_dquot_hash(dquot);
93 remove_free_dquot(dquot);
95 @@ -540,6 +565,10 @@ we_slept:
96 if (atomic_read(&dquot->dq_count) > 1) {
97 /* We have more than one user... nothing to do */
98 atomic_dec(&dquot->dq_count);
99 + /* Releasing dquot during quotaoff phase? */
100 + if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
101 + atomic_read(&dquot->dq_count) == 1)
102 + wake_up(&dquot->dq_wait_unused);
103 spin_unlock(&dq_list_lock);
106 @@ -581,6 +610,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
107 INIT_LIST_HEAD(&dquot->dq_inuse);
108 INIT_HLIST_NODE(&dquot->dq_hash);
109 INIT_LIST_HEAD(&dquot->dq_dirty);
110 + init_waitqueue_head(&dquot->dq_wait_unused);
112 dquot->dq_type = type;
113 atomic_set(&dquot->dq_count, 1);
114 @@ -732,13 +762,9 @@ static void drop_dquot_ref(struct super_block *sb, int type)
116 LIST_HEAD(tofree_head);
118 - /* We need to be guarded against prune_icache to reach all the
119 - * inodes - otherwise some can be on the local list of prune_icache */
121 down_write(&sb_dqopt(sb)->dqptr_sem);
122 remove_dquot_ref(sb, type, &tofree_head);
123 up_write(&sb_dqopt(sb)->dqptr_sem);
125 put_dquot_list(&tofree_head);