diff options
author | npiggin@suse.de <npiggin@suse.de> | 2010-06-23 23:02:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-06-29 13:38:22 -0400 |
commit | 57439f878afafefad8836ebf5c49da2a0a746105 (patch) | |
tree | ec11dd35704aa37d77a5fd8404fda57f297a3834 /fs | |
parent | 5904b3b81d25166e5e39b9727645bb47937618e3 (diff) |
fs: fix superblock iteration race
list_for_each_entry_safe is not suitable to protect against concurrent
modification of the list. 6754af6 introduced a race in sb walking.
list_for_each_entry can use the trick of pinning the current entry in
the list before we drop and retake the lock because it subsequently
follows cur->next. However list_for_each_entry_safe saves n=cur->next
for following before entering the loop body, so when the lock is
dropped, n may be deleted.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Frank Mayhar <fmayhar@google.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 2 | ||||
-rw-r--r-- | fs/super.c | 6 |
2 files changed, 8 insertions, 0 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index d96047b4a633..c8c78ba07827 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -590,6 +590,8 @@ static void prune_dcache(int count) | |||
590 | up_read(&sb->s_umount); | 590 | up_read(&sb->s_umount); |
591 | } | 591 | } |
592 | spin_lock(&sb_lock); | 592 | spin_lock(&sb_lock); |
593 | /* lock was dropped, must reset next */ | ||
594 | list_safe_reset_next(sb, n, s_list); | ||
593 | count -= pruned; | 595 | count -= pruned; |
594 | __put_super(sb); | 596 | __put_super(sb); |
595 | /* more work left to do? */ | 597 | /* more work left to do? */ |
diff --git a/fs/super.c b/fs/super.c index 5c35bc7a499e..938119ab8dcb 100644 --- a/fs/super.c +++ b/fs/super.c | |||
@@ -374,6 +374,8 @@ void sync_supers(void) | |||
374 | up_read(&sb->s_umount); | 374 | up_read(&sb->s_umount); |
375 | 375 | ||
376 | spin_lock(&sb_lock); | 376 | spin_lock(&sb_lock); |
377 | /* lock was dropped, must reset next */ | ||
378 | list_safe_reset_next(sb, n, s_list); | ||
377 | __put_super(sb); | 379 | __put_super(sb); |
378 | } | 380 | } |
379 | } | 381 | } |
@@ -405,6 +407,8 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) | |||
405 | up_read(&sb->s_umount); | 407 | up_read(&sb->s_umount); |
406 | 408 | ||
407 | spin_lock(&sb_lock); | 409 | spin_lock(&sb_lock); |
410 | /* lock was dropped, must reset next */ | ||
411 | list_safe_reset_next(sb, n, s_list); | ||
408 | __put_super(sb); | 412 | __put_super(sb); |
409 | } | 413 | } |
410 | spin_unlock(&sb_lock); | 414 | spin_unlock(&sb_lock); |
@@ -585,6 +589,8 @@ static void do_emergency_remount(struct work_struct *work) | |||
585 | } | 589 | } |
586 | up_write(&sb->s_umount); | 590 | up_write(&sb->s_umount); |
587 | spin_lock(&sb_lock); | 591 | spin_lock(&sb_lock); |
592 | /* lock was dropped, must reset next */ | ||
593 | list_safe_reset_next(sb, n, s_list); | ||
588 | __put_super(sb); | 594 | __put_super(sb); |
589 | } | 595 | } |
590 | spin_unlock(&sb_lock); | 596 | spin_unlock(&sb_lock); |