From nobody Mon Feb 9 03:30:29 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F71C3148A0 for ; Sun, 11 Jan 2026 17:55:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768154112; cv=none; b=pdcKNCiWZ2ylR7/4ge7ValOuxNDDpV7EhbNeEAkaeYNoLPitNFt+fyA0QKGjfSzNgXBoqkXtq6KOKVFKEp9GVJfzP9UAdV54NGsdk+dKJaLce4G9OpZ6IK4pggz6TLHTWl7P/jrN8KbCyEaH7ONkGhRWFJk5ApirN2qFqETBpiE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768154112; c=relaxed/simple; bh=cWCSdYt4b65eh+IzBE3IUy25Pu2/+qQ1wHSD1i1Reuc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=pwFAIFlXDWWDAJKVUf+yMq/wz1ZlRsK3aINs8TR9DLYWFsroOXBWSlkEFTzpCR2CXumbTNKGnCMNrIQT1SEvZt4hZIL6ICDmLugcL3likKX0mVtC7Wwh+m48mwMhVqAThraSSFyOM1vwlattNphUcjLp4FNHLt4dy/fobM3Pssk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=InB+zk36; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="InB+zk36" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-29f102b013fso55230715ad.2 for ; Sun, 11 Jan 2026 09:55:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768154110; x=1768758910; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:from:to:cc:subject:date:message-id:reply-to; bh=mn2Hr5PSunu94OsN9KDSKjc7Q62FgkjIKfj/HXbAqXQ=; b=InB+zk36SvVaREIXOVa0ExPpRFv0ZpmEP+Bj5usQou7+ojyVSAf+4IJ51jJ6b4Hig7 YCSbBrLzL0K2L7uc2CZSntmBS0+olbVepaq3vwigM7mQ9YrSDwJy6RkSAMdosbcZkOv9 /fPgfs+7OBcY6Iaog6JYPrwAj6fI87Q8QOVje6VknK/hrKQIQuVHmNbsa5AjPIWhWYma S0r7Ill4mcEiJIqRdG1wyED7Dx9e4SOupFxBQXwDPuBepQoHlRlxBBfW49R9xA2XY3r2 NqONr9sIQ2KpDA8mpdvgBNILY60cBt1LDt/y5Cl7uCIS/Cevr6s/7jyZfDGp5stuOZ5V 1d4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768154110; x=1768758910; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=mn2Hr5PSunu94OsN9KDSKjc7Q62FgkjIKfj/HXbAqXQ=; b=BuCbl85Ii11g1CtzI7ecQGhif5x5jh3z81x3AUW9JXJ+ZyInDqEj8nlrJl266l+gq4 sU+3L9eHWMeF0DaelzQbcfFyhqzUK71J6lxmIeVSPI7x3d6I/n1H11qviHMSPJssotfR EYVmtaAWp6a5LVrIcLcIS0kT7uiPs0tmvdTZjSxWqDJwsGbnRb8/Mxu0VXzMm8AlxEv0 q4+jkhH5/o2F+lCHYqGXVhoi5ulPkGIlX4/xgm0Kf9uvfm/EaBjVVQJCLnMGhiRr2qR2 LiHd/FUhWiVITWqjclDmlK9PLLW+yu03WYJ1AN9VyqbKqOOh1lc2WIs3M0ng+onQrfAm KDHg== X-Forwarded-Encrypted: i=1; AJvYcCVz59J5ZrH+crTzCOdK9hL2kRxDysQA7U3U5kCXo0cYoTMKTdW+sL9k30Af9DaJ1NpCZgz2EbmPLJxAUtY=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7zeD3ZEaUNqx8uheVlV7bhD4Jb/nr9K4JrDkQsn0MOWgHgw8q PeykjzdeUuv0LU14BVxvpu00B6nw5E0dkrnEjBLkIjT9xCsdDj0gJiPJ7K711WMDbKY= X-Gm-Gg: AY/fxX4zYM1dMh0QUW634CrQDX6RPh4h0gOWbCLsVECDmP9YDH8SH9UHHqkQgsX2+TU u3qknqcvPpm1eqRJ2nBfVPZV4uAWEn2JsrhHX0pC6OQawzt9/GPZiK8u6ymjvOJXA2r8eH8kPSj uLnX7n79C/2UZXynZFWOBOOEo4Lyp1tly6bliRq9BA7Q5YWAQtOn4uKKkNavbBn8wuWpACgD9yp irfMAaXMLve4XWeU7GTexLDpeMfnRGm9dwBcD9a7KlmTSpKADzOdZBql+6MjdO0tKR2qA0VK8iV XF1pkODTBPYiS+vxC0R4dhfBlt37ARY2GenDZ1GPsvSRCUdJeT33hSn61V4puiS/naC+XUUydnJ QJhZGVM192u6rvgE8P4zCmqUMiNpcsCiF7GlkRKIxt4Xggy7fVDktSdjw3MCB2AtgKs0fCg8PPO r5EAbUpnEllSG/H6iFy1WZDm/+xzN9HkzA136QuYzpwEr4rWIz X-Google-Smtp-Source: AGHT+IGRjrIuMsl7ABTtavQCHRXkK5SahpOXls7YS4dgUaOY68TrcZCDlcWyyr8kisqTY0HybKOKCA== X-Received: by 2002:a17:903:184:b0:29d:65ed:f481 with SMTP id d9443c01a7336-2a3ee3411bcmr170291295ad.0.1768154109952; Sun, 11 Jan 2026 09:55:09 -0800 (PST) Received: from [127.0.0.1] ([101.32.222.185]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a3e3c3ae21sm150500795ad.7.2026.01.11.09.55.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 11 Jan 2026 09:55:09 -0800 (PST) From: Kairui Song Date: Mon, 12 Jan 2026 01:53:36 +0800 Subject: [PATCH] mm/shmem, swap: fix race of truncate and swap entry split Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260112-shmem-swap-fix-v1-1-0f347f4f6952@tencent.com> X-B4-Tracking: v=1; b=H4sIAAAAAAAC/x2MQQqAMAzAviI9W1iHivgV8TBddT1MZQUVhn93e EwgyaCchBWGKkPiS1SOvQDVFSzB7Ruj+MJgje0MEaGGyBH1dieu8mDvDVvjqJlbDyU6Exf9D8f pfT+UL/4gYAAAAA== X-Change-ID: 20260111-shmem-swap-fix-8d0e20a14b5d To: linux-mm@kvack.org Cc: Hugh Dickins , Baolin Wang , Andrew Morton , Kemeng Shi , Nhat Pham , Chris Li , Kemeng Shi , Nhat Pham , Baoquan He , Barry Song , linux-kernel@vger.kernel.org, Kairui Song , stable@vger.kernel.org X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1768154106; l=3256; i=kasong@tencent.com; s=kasong-sign-tencent; h=from:subject:message-id; bh=y8LXMuvlkDlyCTV58KsaetOE+va9ri7lv7P7G8ssw0g=; b=RVEa+tGwO0D/Cduu3BeHX+GntDWMUg1Lbu+Rm6zDB+3EfubeQAdCLcFf/7clzYQEZ1TYn9esm O65mahA2w1DASc67hy1Xh1z739U0JTywGL2xQIYerk3c9KQ0UdMBG2A X-Developer-Key: i=kasong@tencent.com; a=ed25519; pk=kCdoBuwrYph+KrkJnrr7Sm1pwwhGDdZKcKrqiK8Y1mI= From: Kairui Song The helper for shmem swap freeing is not handling the order of swap entries correctly. It uses xa_cmpxchg_irq to erase the swap entry, but it gets the entry order before that using xa_get_order without lock protection. As a result the order could be a stalled value if the entry is split after the xa_get_order and before the xa_cmpxchg_irq. In fact that are more way for other races to occur during the time window. To fix that, open code the Xarray cmpxchg and put the order retrivial and value checking in the same critical section. Also ensure the order won't exceed the truncate border. I observed random swapoff hangs and swap entry leaks when stress testing ZSWAP with shmem. After applying this patch, the problem is resolve= d. Fixes: 809bc86517cc ("mm: shmem: support large folio swap out") Cc: stable@vger.kernel.org Signed-off-by: Kairui Song --- mm/shmem.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 0b4c8c70d017..e160da0cd30f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -961,18 +961,28 @@ static void shmem_delete_from_page_cache(struct folio= *folio, void *radswap) * the number of pages being freed. 0 means entry not found in XArray (0 p= ages * being freed). */ -static long shmem_free_swap(struct address_space *mapping, - pgoff_t index, void *radswap) +static long shmem_free_swap(struct address_space *mapping, pgoff_t index, + unsigned int max_nr, void *radswap) { - int order =3D xa_get_order(&mapping->i_pages, index); - void *old; + XA_STATE(xas, &mapping->i_pages, index); + unsigned int nr_pages =3D 0; + void *entry; =20 - old =3D xa_cmpxchg_irq(&mapping->i_pages, index, radswap, NULL, 0); - if (old !=3D radswap) - return 0; - swap_put_entries_direct(radix_to_swp_entry(radswap), 1 << order); + xas_lock_irq(&xas); + entry =3D xas_load(&xas); + if (entry =3D=3D radswap) { + nr_pages =3D 1 << xas_get_order(&xas); + if (index =3D=3D round_down(xas.xa_index, nr_pages) && nr_pages < max_nr) + xas_store(&xas, NULL); + else + nr_pages =3D 0; + } + xas_unlock_irq(&xas); + + if (nr_pages) + swap_put_entries_direct(radix_to_swp_entry(radswap), nr_pages); =20 - return 1 << order; + return nr_pages; } =20 /* @@ -1124,8 +1134,8 @@ static void shmem_undo_range(struct inode *inode, lof= f_t lstart, uoff_t lend, if (xa_is_value(folio)) { if (unfalloc) continue; - nr_swaps_freed +=3D shmem_free_swap(mapping, - indices[i], folio); + nr_swaps_freed +=3D shmem_free_swap(mapping, indices[i], + end - indices[i], folio); continue; } =20 @@ -1195,7 +1205,8 @@ static void shmem_undo_range(struct inode *inode, lof= f_t lstart, uoff_t lend, =20 if (unfalloc) continue; - swaps_freed =3D shmem_free_swap(mapping, indices[i], folio); + swaps_freed =3D shmem_free_swap(mapping, indices[i], + end - indices[i], folio); if (!swaps_freed) { /* Swap was replaced by page: retry */ index =3D indices[i]; --- base-commit: ab3d40bdac831c67e130fda12f3011505556500f change-id: 20260111-shmem-swap-fix-8d0e20a14b5d Best regards, --=20 Kairui Song