aboutsummaryrefslogtreecommitdiffstats
path: root/mm/zswap.c
diff options
context:
space:
mode:
authorWeijie Yang <weijie.yang@samsung.com>2013-11-12 18:08:26 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-12 22:09:10 -0500
commit67d13fe846c57a54d12578e7a4518f68c5c86ad7 (patch)
treeb1b00b15b87941b26f05bb181150c5d1fffbccc0 /mm/zswap.c
parent7a67d7abcc8da30a16ed64c3909d3fea004bde93 (diff)
mm/zswap: bugfix: memory leak when invalidate and reclaim occur concurrently
Consider the following scenario: thread 0: reclaim entry x (get refcount, but not call zswap_get_swap_cache_page) thread 1: call zswap_frontswap_invalidate_page to invalidate entry x. finished, entry x and its zbud is not freed as its refcount != 0 now, the swap_map[x] = 0 thread 0: now call zswap_get_swap_cache_page swapcache_prepare return -ENOENT because entry x is not used any more zswap_get_swap_cache_page return ZSWAP_SWAPCACHE_NOMEM zswap_writeback_entry do nothing except put refcount Now, the memory of zswap_entry x and its zpage leak. Modify: - check the refcount in fail path, free memory if it is not referenced. - use ZSWAP_SWAPCACHE_FAIL instead of ZSWAP_SWAPCACHE_NOMEM as the fail path can be not only caused by nomem but also by invalidate. Signed-off-by: Weijie Yang <weijie.yang@samsung.com> Reviewed-by: Bob Liu <bob.liu@oracle.com> Reviewed-by: Minchan Kim <minchan@kernel.org> Acked-by: Seth Jennings <sjenning@linux.vnet.ibm.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/zswap.c')
-rw-r--r--mm/zswap.c22
1 files changed, 14 insertions, 8 deletions
diff --git a/mm/zswap.c b/mm/zswap.c
index 001474c1a594..0ffcad03baea 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -387,7 +387,7 @@ static void zswap_free_entry(struct zswap_tree *tree, struct zswap_entry *entry)
387enum zswap_get_swap_ret { 387enum zswap_get_swap_ret {
388 ZSWAP_SWAPCACHE_NEW, 388 ZSWAP_SWAPCACHE_NEW,
389 ZSWAP_SWAPCACHE_EXIST, 389 ZSWAP_SWAPCACHE_EXIST,
390 ZSWAP_SWAPCACHE_NOMEM 390 ZSWAP_SWAPCACHE_FAIL,
391}; 391};
392 392
393/* 393/*
@@ -401,9 +401,10 @@ enum zswap_get_swap_ret {
401 * added to the swap cache, and returned in retpage. 401 * added to the swap cache, and returned in retpage.
402 * 402 *
403 * If success, the swap cache page is returned in retpage 403 * If success, the swap cache page is returned in retpage
404 * Returns 0 if page was already in the swap cache, page is not locked 404 * Returns ZSWAP_SWAPCACHE_EXIST if page was already in the swap cache
405 * Returns 1 if the new page needs to be populated, page is locked 405 * Returns ZSWAP_SWAPCACHE_NEW if the new page needs to be populated,
406 * Returns <0 on error 406 * the new page is added to swapcache and locked
407 * Returns ZSWAP_SWAPCACHE_FAIL on error
407 */ 408 */
408static int zswap_get_swap_cache_page(swp_entry_t entry, 409static int zswap_get_swap_cache_page(swp_entry_t entry,
409 struct page **retpage) 410 struct page **retpage)
@@ -475,7 +476,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
475 if (new_page) 476 if (new_page)
476 page_cache_release(new_page); 477 page_cache_release(new_page);
477 if (!found_page) 478 if (!found_page)
478 return ZSWAP_SWAPCACHE_NOMEM; 479 return ZSWAP_SWAPCACHE_FAIL;
479 *retpage = found_page; 480 *retpage = found_page;
480 return ZSWAP_SWAPCACHE_EXIST; 481 return ZSWAP_SWAPCACHE_EXIST;
481} 482}
@@ -529,11 +530,11 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
529 530
530 /* try to allocate swap cache page */ 531 /* try to allocate swap cache page */
531 switch (zswap_get_swap_cache_page(swpentry, &page)) { 532 switch (zswap_get_swap_cache_page(swpentry, &page)) {
532 case ZSWAP_SWAPCACHE_NOMEM: /* no memory */ 533 case ZSWAP_SWAPCACHE_FAIL: /* no memory or invalidate happened */
533 ret = -ENOMEM; 534 ret = -ENOMEM;
534 goto fail; 535 goto fail;
535 536
536 case ZSWAP_SWAPCACHE_EXIST: /* page is unlocked */ 537 case ZSWAP_SWAPCACHE_EXIST:
537 /* page is already in the swap cache, ignore for now */ 538 /* page is already in the swap cache, ignore for now */
538 page_cache_release(page); 539 page_cache_release(page);
539 ret = -EEXIST; 540 ret = -EEXIST;
@@ -594,7 +595,12 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
594 595
595fail: 596fail:
596 spin_lock(&tree->lock); 597 spin_lock(&tree->lock);
597 zswap_entry_put(entry); 598 refcount = zswap_entry_put(entry);
599 if (refcount <= 0) {
600 /* invalidate happened, consider writeback as success */
601 zswap_free_entry(tree, entry);
602 ret = 0;
603 }
598 spin_unlock(&tree->lock); 604 spin_unlock(&tree->lock);
599 return ret; 605 return ret;
600} 606}