]>
Commit | Line | Data |
---|---|---|
1143c684 SL |
1 | From acf6321a8d3b601d600f35331b185a1868a09ef2 Mon Sep 17 00:00:00 2001 |
2 | From: Ross Lagerwall <ross.lagerwall@citrix.com> | |
3 | Date: Wed, 27 Mar 2019 17:09:17 +0000 | |
4 | Subject: gfs2: Fix lru_count going negative | |
5 | ||
6 | [ Upstream commit 7881ef3f33bb80f459ea6020d1e021fc524a6348 ] | |
7 | ||
8 | Under certain conditions, lru_count may drop below zero resulting in | |
9 | a large amount of log spam like this: | |
10 | ||
11 | vmscan: shrink_slab: gfs2_dump_glock+0x3b0/0x630 [gfs2] \ | |
12 | negative objects to delete nr=-1 | |
13 | ||
14 | This happens as follows: | |
15 | 1) A glock is moved from lru_list to the dispose list and lru_count is | |
16 | decremented. | |
17 | 2) The dispose function calls cond_resched() and drops the lru lock. | |
18 | 3) Another thread takes the lru lock and tries to add the same glock to | |
19 | lru_list, checking if the glock is on an lru list. | |
20 | 4) It is on a list (actually the dispose list) and so it avoids | |
21 | incrementing lru_count. | |
22 | 5) The glock is moved to lru_list. | |
23 | 5) The original thread doesn't dispose it because it has been re-added | |
24 | to the lru list but the lru_count has still decreased by one. | |
25 | ||
26 | Fix by checking if the LRU flag is set on the glock rather than checking | |
27 | if the glock is on some list and rearrange the code so that the LRU flag | |
28 | is added/removed precisely when the glock is added/removed from lru_list. | |
29 | ||
30 | Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> | |
31 | Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> | |
32 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
33 | --- | |
34 | fs/gfs2/glock.c | 22 +++++++++++++--------- | |
35 | 1 file changed, 13 insertions(+), 9 deletions(-) | |
36 | ||
37 | diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c | |
38 | index 09a0cf5f3dd86..1eb737c466ddc 100644 | |
39 | --- a/fs/gfs2/glock.c | |
40 | +++ b/fs/gfs2/glock.c | |
41 | @@ -136,22 +136,26 @@ static int demote_ok(const struct gfs2_glock *gl) | |
42 | ||
43 | void gfs2_glock_add_to_lru(struct gfs2_glock *gl) | |
44 | { | |
45 | + if (!(gl->gl_ops->go_flags & GLOF_LRU)) | |
46 | + return; | |
47 | + | |
48 | spin_lock(&lru_lock); | |
49 | ||
50 | - if (!list_empty(&gl->gl_lru)) | |
51 | - list_del_init(&gl->gl_lru); | |
52 | - else | |
53 | + list_del(&gl->gl_lru); | |
54 | + list_add_tail(&gl->gl_lru, &lru_list); | |
55 | + | |
56 | + if (!test_bit(GLF_LRU, &gl->gl_flags)) { | |
57 | + set_bit(GLF_LRU, &gl->gl_flags); | |
58 | atomic_inc(&lru_count); | |
59 | + } | |
60 | ||
61 | - list_add_tail(&gl->gl_lru, &lru_list); | |
62 | - set_bit(GLF_LRU, &gl->gl_flags); | |
63 | spin_unlock(&lru_lock); | |
64 | } | |
65 | ||
66 | static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) | |
67 | { | |
68 | spin_lock(&lru_lock); | |
69 | - if (!list_empty(&gl->gl_lru)) { | |
70 | + if (test_bit(GLF_LRU, &gl->gl_flags)) { | |
71 | list_del_init(&gl->gl_lru); | |
72 | atomic_dec(&lru_count); | |
73 | clear_bit(GLF_LRU, &gl->gl_flags); | |
74 | @@ -1040,8 +1044,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh) | |
75 | !test_bit(GLF_DEMOTE, &gl->gl_flags)) | |
76 | fast_path = 1; | |
77 | } | |
78 | - if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) && | |
79 | - (glops->go_flags & GLOF_LRU)) | |
80 | + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) | |
81 | gfs2_glock_add_to_lru(gl); | |
82 | ||
83 | trace_gfs2_glock_queue(gh, 0); | |
84 | @@ -1341,6 +1344,7 @@ __acquires(&lru_lock) | |
85 | if (!spin_trylock(&gl->gl_lockref.lock)) { | |
86 | add_back_to_lru: | |
87 | list_add(&gl->gl_lru, &lru_list); | |
88 | + set_bit(GLF_LRU, &gl->gl_flags); | |
89 | atomic_inc(&lru_count); | |
90 | continue; | |
91 | } | |
92 | @@ -1348,7 +1352,6 @@ __acquires(&lru_lock) | |
93 | spin_unlock(&gl->gl_lockref.lock); | |
94 | goto add_back_to_lru; | |
95 | } | |
96 | - clear_bit(GLF_LRU, &gl->gl_flags); | |
97 | gl->gl_lockref.count++; | |
98 | if (demote_ok(gl)) | |
99 | handle_callback(gl, LM_ST_UNLOCKED, 0, false); | |
100 | @@ -1384,6 +1387,7 @@ static long gfs2_scan_glock_lru(int nr) | |
101 | if (!test_bit(GLF_LOCK, &gl->gl_flags)) { | |
102 | list_move(&gl->gl_lru, &dispose); | |
103 | atomic_dec(&lru_count); | |
104 | + clear_bit(GLF_LRU, &gl->gl_flags); | |
105 | freed++; | |
106 | continue; | |
107 | } | |
108 | -- | |
109 | 2.20.1 | |
110 |