diff options
author | Carlos Maiolino <cmaiolino@redhat.com> | 2012-08-10 14:01:51 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2012-08-29 16:01:11 -0400 |
commit | 6fb8a90aa3f2319a25f3396b1e9273300f8903b8 (patch) | |
tree | 3a74bce9dbe7a6ef8b0c952335c1963a649eefa2 /fs/xfs/xfs_buf.c | |
parent | a672e1be30d5bc848cd0067c55ed29b2015b7c17 (diff) |
xfs: fix race while discarding buffers [V4]
While xfs_buftarg_shrink() is freeing buffers from the dispose list (filled with
buffers from lru list), there is a possibility to have xfs_buf_stale() racing
with it, and removing buffers from dispose list before xfs_buftarg_shrink() does
it.
This happens because xfs_buftarg_shrink() handle the dispose list without
locking and the test condition in xfs_buf_stale() checks for the buffer being in
*any* list:
if (!list_empty(&bp->b_lru))
If the buffer happens to be on dispose list, this causes the buffer counter of
lru list (btp->bt_lru_nr) to be decremented twice (once in xfs_buftarg_shrink()
and another in xfs_buf_stale()) causing a wrong account usage of the lru list.
This may cause xfs_buftarg_shrink() to return a wrong value to the memory
shrinker shrink_slab(), and such account error may also cause an underflowed
value to be returned; since the counter is lower than the current number of
items in the lru list, a decrement may happen when the counter is 0, causing
an underflow on the counter.
The fix uses a new flag field (and a new buffer flag) to serialize buffer
handling during the shrink process. The new flag field has been designed to use
btp->bt_lru_lock/unlock instead of xfs_buf_lock/unlock mechanism.
dchinner, sandeen, aquini and aris also deserve credits for this.
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_buf.c')
-rw-r--r-- | fs/xfs/xfs_buf.c | 5 |
1 files changed, 4 insertions, 1 deletions
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index d7a9dd735e1e..933b7930b863 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c | |||
@@ -96,6 +96,7 @@ xfs_buf_lru_add( | |||
96 | atomic_inc(&bp->b_hold); | 96 | atomic_inc(&bp->b_hold); |
97 | list_add_tail(&bp->b_lru, &btp->bt_lru); | 97 | list_add_tail(&bp->b_lru, &btp->bt_lru); |
98 | btp->bt_lru_nr++; | 98 | btp->bt_lru_nr++; |
99 | bp->b_lru_flags &= ~_XBF_LRU_DISPOSE; | ||
99 | } | 100 | } |
100 | spin_unlock(&btp->bt_lru_lock); | 101 | spin_unlock(&btp->bt_lru_lock); |
101 | } | 102 | } |
@@ -154,7 +155,8 @@ xfs_buf_stale( | |||
154 | struct xfs_buftarg *btp = bp->b_target; | 155 | struct xfs_buftarg *btp = bp->b_target; |
155 | 156 | ||
156 | spin_lock(&btp->bt_lru_lock); | 157 | spin_lock(&btp->bt_lru_lock); |
157 | if (!list_empty(&bp->b_lru)) { | 158 | if (!list_empty(&bp->b_lru) && |
159 | !(bp->b_lru_flags & _XBF_LRU_DISPOSE)) { | ||
158 | list_del_init(&bp->b_lru); | 160 | list_del_init(&bp->b_lru); |
159 | btp->bt_lru_nr--; | 161 | btp->bt_lru_nr--; |
160 | atomic_dec(&bp->b_hold); | 162 | atomic_dec(&bp->b_hold); |
@@ -1501,6 +1503,7 @@ xfs_buftarg_shrink( | |||
1501 | */ | 1503 | */ |
1502 | list_move(&bp->b_lru, &dispose); | 1504 | list_move(&bp->b_lru, &dispose); |
1503 | btp->bt_lru_nr--; | 1505 | btp->bt_lru_nr--; |
1506 | bp->b_lru_flags |= _XBF_LRU_DISPOSE; | ||
1504 | } | 1507 | } |
1505 | spin_unlock(&btp->bt_lru_lock); | 1508 | spin_unlock(&btp->bt_lru_lock); |
1506 | 1509 | ||