Whamcloud - gitweb
b=20595
[fs/lustre-release.git] / lustre / kernel_patches / patches / quota-fix-oops-in-invalidate_dquots.patch
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
6
7 [PATCH] Fix oops in invalidate_dquots()
8
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
17 icache memory.
18
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>
22 ---
23
24 diff --git a/fs/dquot.c b/fs/dquot.c
25 index 1966c89..9376a43 100644
26 --- a/fs/dquot.c
27 +++ b/fs/dquot.c
28 @@ -118,8 +118,7 @@
29   * spinlock to internal buffers before writing.
30   *
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)
36   */
37  
38 @@ -407,23 +406,49 @@ out_dqlock:
39  
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.
47 + */
48  static void invalidate_dquots(struct super_block *sb, int type)
49  {
50         struct dquot *dquot, *tmp;
51  
52 +restart:
53         spin_lock(&dq_list_lock);
54         list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
55                 if (dquot->dq_sb != sb)
56                         continue;
57                 if (dquot->dq_type != type)
58                         continue;
59 -#ifdef __DQUOT_PARANOIA
60 -               if (atomic_read(&dquot->dq_count))
61 -                       BUG();
62 -#endif
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)) {
66 +                       DEFINE_WAIT(wait);
67 +
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
73 +                        * the dquot.
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
77 +                        * wake up.
78 +                        */
79 +                       if (atomic_read(&dquot->dq_count) > 1)
80 +                               schedule();
81 +                       finish_wait(&dquot->dq_wait_unused, &wait);
82 +                       dqput(dquot);
83 +                       /* At this moment dquot() need not exist (it could be
84 +                        * reclaimed by prune_dqcache(). Hence we must
85 +                        * restart. */
86 +                       goto restart;
87 +               }
88 +               /*
89 +                * Quota now has no users and it has been written on last
90 +                * dqput()
91 +                */
92                 remove_dquot_hash(dquot);
93                 remove_free_dquot(dquot);
94                 remove_inuse(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);
104                 return;
105         }
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);
111         dquot->dq_sb = sb;
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)
115  {
116         LIST_HEAD(tofree_head);
117  
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 */
120 -       down(&iprune_sem);
121         down_write(&sb_dqopt(sb)->dqptr_sem);
122         remove_dquot_ref(sb, type, &tofree_head);
123         up_write(&sb_dqopt(sb)->dqptr_sem);
124 -       up(&iprune_sem);
125         put_dquot_list(&tofree_head);
126  }
127