[RHEL7,COMMIT] mm/tswap: fix lockup in tswap_evict_page()

Submitted by Konstantin Khorenko on July 4, 2018, 9:05 a.m.


Message ID 201807040905.w6495xjD024329@finist_ce7.work
State New
Series "mm/tswap: fix lockup in tswap_evict_page()"
Headers show

Commit Message

Konstantin Khorenko July 4, 2018, 9:05 a.m.
The commit is pushed to "branch-rh7-3.10.0-862.3.2.vz7.61.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-862.3.2.vz7.61.10
commit 4dac0975970e682d09f65464f9b18be61d88e890
Author: Andrey Ryabinin <aryabinin@virtuozzo.com>
Date:   Wed Jul 4 12:05:59 2018 +0300

    mm/tswap: fix lockup in tswap_evict_page()
    Surprisingly after the upstream commit 7c00bafee87c
    ("mm/swap: free swap slots in batch"), backported
    by redhat, SWAP_HAS_CACHE in swap_info->swap_map[offset]
    doesn't mean that swap entry has a page in swap cache anymore.
    Now we may set SWAP_HAS_CACHE even if we know that page is not cached.
    Commit 7c00bafee87c changed swapcache_free() so it will set SWAP_HAS_CACHE:
    -       p->swap_map[offset] = usage;
    +       p->swap_map[offset] = usage ? : SWAP_HAS_CACHE;
    And it will be cleared only by swapcache_free_entries(), but swapcache_free_entries()
    call may be delayed indefinitely, until the 'swp_slots' per-cpu cache
    is full.
    It's not that obvious, how to fix this mess. For now, I think, the safest
    course of action would be a workaround in the tswap, similar to what we
    do in __read_swap_cache_async() - bail out on zero swap_count. Apperently
    it works for __read_swap_cache_async(), so it should work for tswap.
    Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
    Reviewed-by: Cyrill Gorcunov <gorcunov@openvz.org>
    Reviewed-by: Kirill Tkhai <ktkhai@virtuozzo.com>
 mm/tswap.c | 5 +++++
 1 file changed, 5 insertions(+)

Patch hide | download patch | download mbox

diff --git a/mm/tswap.c b/mm/tswap.c
index b7a990e8cd8d..7b02c1142db2 100644
--- a/mm/tswap.c
+++ b/mm/tswap.c
@@ -183,6 +183,11 @@  static int tswap_evict_page(struct page *page)
 		goto out;
+	if (!__swp_swapcount(entry) && swap_slot_cache_enabled) {
+		err = -ENOENT;
+		goto out;
+	}
 	err = swapcache_prepare(entry);
 	if (err == -EEXIST) {