summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugh Dickins <hughd@google.com>2011-05-28 13:14:09 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-06-03 09:32:37 +0900
commite753e72e4d6698a629634e66580638e7bd6835ba (patch)
tree33bd9c2f2cf294886df60a964866cb072eebc32e
parentca328545a1eca31a6c15487e526ddfaf870bc749 (diff)
downloadlinux-stable-e753e72e4d6698a629634e66580638e7bd6835ba.tar.gz
linux-stable-e753e72e4d6698a629634e66580638e7bd6835ba.tar.bz2
linux-stable-e753e72e4d6698a629634e66580638e7bd6835ba.zip
tmpfs: fix race between truncate and writepage
commit 826267cf1e6c6899eda1325a19f1b1d15c558b20 upstream. While running fsx on tmpfs with a memhog then swapoff, swapoff was hanging (interruptibly), repeatedly failing to locate the owner of a 0xff entry in the swap_map. Although shmem_writepage() does abandon when it sees incoming page index is beyond eof, there was still a window in which shmem_truncate_range() could come in between writepage's dropping lock and updating swap_map, find the half-completed swap_map entry, and in trying to free it, leave it in a state that swap_shmem_alloc() could not correct. Arguably a bug in __swap_duplicate()'s and swap_entry_free()'s handling of the different cases, but easiest to fix by moving swap_shmem_alloc() under cover of the lock. More interesting than the bug: it's been there since 2.6.33, why could I not see it with earlier kernels? The mmotm of two weeks ago seems to have some magic for generating races, this is just one of three I found. With yesterday's git I first saw this in mainline, bisected in search of that magic, but the easy reproducibility evaporated. Oh well, fix the bug. Signed-off-by: Hugh Dickins <hughd@google.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--mm/shmem.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index ba4ad28b7db6..ba12be45cabb 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1101,8 +1101,8 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
delete_from_page_cache(page);
shmem_swp_set(info, entry, swap.val);
shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
swap_shmem_alloc(swap);
+ spin_unlock(&info->lock);
BUG_ON(page_mapped(page));
swap_writepage(page, wbc);
return 0;