From 88d2373e57dbc9c937cba44d616fde154ee001c1 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 4 Mar 2011 12:47:30 +1100 Subject: [PATCH] Avoid a race in lafs_get_cleanable. If we find a cleanable segment that is actually clean, then we drop the lock and try to add it. If something else removed it at just this time we end up with a refcount issue as we are meant to take references to youth_db and usage0_db when adding things to the table, and we don't have them any more. So simply allow the 'add_clean' to fail as that is safe. Signed-off-by: NeilBrown --- segments.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/segments.c b/segments.c index 124c212..ac83ca9 100644 --- a/segments.c +++ b/segments.c @@ -1366,11 +1366,12 @@ static int add_free(struct fs *fs, unsigned int dev, u32 seg, u16 *youthp) return 1; } -static int add_clean(struct fs *fs, unsigned int dev, u32 seg) +static int add_clean(struct fs *fs, unsigned int dev, u32 seg, bool if_present) { /* This dev/seg is now clean. Make sure it is on the list. * Chances are that this is already present in the table as - * a recently cleaned segment. + * a recently cleaned segment. If 'if_present', then insist + * on this. * Return TRUE if segment was added to the table (as this * implies a reference count). */ @@ -1402,6 +1403,10 @@ static int add_clean(struct fs *fs, unsigned int dev, u32 seg) spin_unlock(&fs->lock); return 0; } + if (if_present) { + spin_unlock(&fs->lock); + return 0; + } if (fs->segtrack->free.cnt + fs->segtrack->clean.cnt >= fs->segtrack->total / 2) { @@ -1621,10 +1626,8 @@ retry: if (ss->usage == 0) { int rv; spin_unlock(&fs->lock); - rv = add_clean(fs, ss->dev, ss->segment); - /* FIXME we dropped the lock, so maybe this could bug?? */ - /* I should make sure I hold the block references */ - BUG_ON(rv); + rv = add_clean(fs, *dev, *seg, true); + BUG_ON(rv); /* passing 'true' makes this impossible */ goto retry; } segdelete(fs->segtrack, ss); @@ -1659,7 +1662,7 @@ static int add_cleanable(struct fs *fs, unsigned int dev, u32 seg, fs->total_free += segsize - usage /* - 1 */; if (usage == 0) { - int rv = add_clean(fs, dev, seg); + int rv = add_clean(fs, dev, seg, false); lafs_check_seg_cnt(fs->segtrack); return rv; } -- 2.39.5