]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.4.83/quota-fix-race-between-dqput-and-dquot_scan_active.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.4.83 / quota-fix-race-between-dqput-and-dquot_scan_active.patch
1 From 1362f4ea20fa63688ba6026e586d9746ff13a846 Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Thu, 20 Feb 2014 17:02:27 +0100
4 Subject: quota: Fix race between dqput() and dquot_scan_active()
5
6 From: Jan Kara <jack@suse.cz>
7
8 commit 1362f4ea20fa63688ba6026e586d9746ff13a846 upstream.
9
10 Currently last dqput() can race with dquot_scan_active() causing it to
11 call callback for an already deactivated dquot. The race is as follows:
12
13 CPU1 CPU2
14 dqput()
15 spin_lock(&dq_list_lock);
16 if (atomic_read(&dquot->dq_count) > 1) {
17 - not taken
18 if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
19 spin_unlock(&dq_list_lock);
20 ->release_dquot(dquot);
21 if (atomic_read(&dquot->dq_count) > 1)
22 - not taken
23 dquot_scan_active()
24 spin_lock(&dq_list_lock);
25 if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
26 - not taken
27 atomic_inc(&dquot->dq_count);
28 spin_unlock(&dq_list_lock);
29 - proceeds to release dquot
30 ret = fn(dquot, priv);
31 - called for inactive dquot
32
33 Fix the problem by making sure possible ->release_dquot() is finished by
34 the time we call the callback and new calls to it will notice reference
35 dquot_scan_active() has taken and bail out.
36
37 Signed-off-by: Jan Kara <jack@suse.cz>
38 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
39
40 ---
41 fs/quota/dquot.c | 14 +++++++++++---
42 1 file changed, 11 insertions(+), 3 deletions(-)
43
44 --- a/fs/quota/dquot.c
45 +++ b/fs/quota/dquot.c
46 @@ -580,9 +580,17 @@ int dquot_scan_active(struct super_block
47 dqstats_inc(DQST_LOOKUPS);
48 dqput(old_dquot);
49 old_dquot = dquot;
50 - ret = fn(dquot, priv);
51 - if (ret < 0)
52 - goto out;
53 + /*
54 + * ->release_dquot() can be racing with us. Our reference
55 + * protects us from new calls to it so just wait for any
56 + * outstanding call and recheck the DQ_ACTIVE_B after that.
57 + */
58 + wait_on_dquot(dquot);
59 + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
60 + ret = fn(dquot, priv);
61 + if (ret < 0)
62 + goto out;
63 + }
64 spin_lock(&dq_list_lock);
65 /* We are safe to continue now because our dquot could not
66 * be moved out of the inuse list while we hold the reference */