Skip to content

Commit 18a9370

Browse files
yosrym93akpm00
authored andcommitted
mm: zswap: fix double invalidate with exclusive loads
If exclusive loads are enabled for zswap, we invalidate the entry before returning from zswap_frontswap_load(), after dropping the local reference. However, the tree lock is dropped during decompression after the local reference is acquired, so the entry could be invalidated before we drop the local ref. If this happens, the entry is freed once we drop the local ref, and zswap_invalidate_entry() tries to invalidate an already freed entry. Fix this by: (a) Making sure zswap_invalidate_entry() is always called with a local ref held, to avoid being called on a freed entry. (b) Making sure zswap_invalidate_entry() only drops the ref if the entry was actually on the rbtree. Otherwise, another invalidation could have already happened, and the initial ref is already dropped. With these changes, there is no need to check that there is no need to make sure the entry still exists in the tree in zswap_reclaim_entry() before invalidating it, as zswap_reclaim_entry() will make this check internally. Link: https://lkml.kernel.org/r/[email protected] Fixes: b9c91c4 ("mm: zswap: support exclusive loads") Signed-off-by: Yosry Ahmed <[email protected]> Reported-by: Hyeonggon Yoo <[email protected]> Cc: Dan Streetman <[email protected]> Cc: Domenico Cerasuolo <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Konrad Rzeszutek Wilk <[email protected]> Cc: Nhat Pham <[email protected]> Cc: Seth Jennings <[email protected]> Cc: Vitaly Wool <[email protected]> Cc: Yu Zhao <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 994ec4e commit 18a9370

File tree

1 file changed

+12
-9
lines changed

1 file changed

+12
-9
lines changed

mm/zswap.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -355,12 +355,14 @@ static int zswap_rb_insert(struct rb_root *root, struct zswap_entry *entry,
355355
return 0;
356356
}
357357

358-
static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
358+
static bool zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
359359
{
360360
if (!RB_EMPTY_NODE(&entry->rbnode)) {
361361
rb_erase(&entry->rbnode, root);
362362
RB_CLEAR_NODE(&entry->rbnode);
363+
return true;
363364
}
365+
return false;
364366
}
365367

366368
/*
@@ -599,14 +601,16 @@ static struct zswap_pool *zswap_pool_find_get(char *type, char *compressor)
599601
return NULL;
600602
}
601603

604+
/*
605+
* If the entry is still valid in the tree, drop the initial ref and remove it
606+
* from the tree. This function must be called with an additional ref held,
607+
* otherwise it may race with another invalidation freeing the entry.
608+
*/
602609
static void zswap_invalidate_entry(struct zswap_tree *tree,
603610
struct zswap_entry *entry)
604611
{
605-
/* remove from rbtree */
606-
zswap_rb_erase(&tree->rbroot, entry);
607-
608-
/* drop the initial reference from entry creation */
609-
zswap_entry_put(tree, entry);
612+
if (zswap_rb_erase(&tree->rbroot, entry))
613+
zswap_entry_put(tree, entry);
610614
}
611615

612616
static int zswap_reclaim_entry(struct zswap_pool *pool)
@@ -659,8 +663,7 @@ static int zswap_reclaim_entry(struct zswap_pool *pool)
659663
* swapcache. Drop the entry from zswap - unless invalidate already
660664
* took it out while we had the tree->lock released for IO.
661665
*/
662-
if (entry == zswap_rb_search(&tree->rbroot, swpoffset))
663-
zswap_invalidate_entry(tree, entry);
666+
zswap_invalidate_entry(tree, entry);
664667

665668
put_unlock:
666669
/* Drop local reference */
@@ -1466,7 +1469,6 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
14661469
count_objcg_event(entry->objcg, ZSWPIN);
14671470
freeentry:
14681471
spin_lock(&tree->lock);
1469-
zswap_entry_put(tree, entry);
14701472
if (!ret && zswap_exclusive_loads_enabled) {
14711473
zswap_invalidate_entry(tree, entry);
14721474
*exclusive = true;
@@ -1475,6 +1477,7 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
14751477
list_move(&entry->lru, &entry->pool->lru);
14761478
spin_unlock(&entry->pool->lru_lock);
14771479
}
1480+
zswap_entry_put(tree, entry);
14781481
spin_unlock(&tree->lock);
14791482

14801483
return ret;

0 commit comments

Comments
 (0)