From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793270754; b=MI2Ie6GN4kHUj29efZXkcf88IDK7DF6g9R+TIkInt8gVVxEaIcxxqoSCJ1AfAcc4yaPD M1PBnv8BWPgaNZgxCkrN/sxLqFEs6XbswQMn0hJIGkj4e3VbXjLEb1wAQfyKg7DsIuoJQ 5ktYIRJEq02JUgKaFx2IAcQlfE5B+9CnnZxzpuIZZu+TzkhKyWwNKr7U799lDZD3AYCCQ RSh5QVLBOEqUYv+4MW8AYrsQPsjlDDSm/Sxs3rT8dUN2zYPY/Fhsp5DhIagS7nOVsHdft 4QeOC3/UOtjga3Wk4aHVx3OoItd0yont/j4j81t3aqcSxK4ddiyPRNjXg1ZcjQUBdsg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793270754; h=Received:DKIM-Signature:From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:References:In-Reply-To:To:Cc: X-Mailer:X-Developer-Signature:X-Developer-Key; bh=EOpicr4/nVjUTFq+nOkoNBNjJy8NdOt38t8q45tot68=; b=RVvKid4bsMY3Zs0JELlcITZ59q0lsWENBLtPCvDGSYwEj8BXLKu9MODr4VYIzuG9/KvO qD/wBU1zlkN2UgPgt/XIGcl/PBdJhTFBXT9iSGl0Rk8Eh9Zz+hEXh9zqF5Bp6mPLTonrq 18xEDgChxYhexIP8kYv4Jhe4BU/bEhxySGv8Z54jbBLSShXDwvqmh/9N7RRgkoGOc6feW yDrxIYBqh19IbY3qBZBijeQlFdTMdU7BUq0QPpqByhZQs5jeVz1aY4B6lIaLRNamkqwkC AfAsSmglZJbpZxfX5qxFbgaHdJaVl91ltEqISWk7ysBF0RmLPQWxKak8IrzZGqsflBw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Hza8FH4y; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6EA7606A31A; Thu, 29 Oct 2026 10:45:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Hza8FH4y Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7B29B1F01828; Thu, 29 Oct 2026 10:45:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793270753; bh=EOpicr4/nVjUTFq+nOkoNBNjJy8NdOt38t8q45tot68=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Hza8FH4ycQzhLwxvTbwUuEXu5jbFA7/R/t9H0LYZ2vUY+SI3PYzU/CxvePMOA9uOH gAR9ulTJxITjo0Fy5FxcM0bGyJhE5BAHu92aOhRRc+MXctqaMdwvvaiSqBA5mQxrCX LMNAEHJs/TCxtz9saDDG0S5I3i/Y/LYSQJ8IrbYDc1R70PF6GnHDQ5nOD42ET+WdpQ fF45dr/ZJ4Fe5d0Ow4A8nZ/TCz+fVS5g5Qvo7lcAxrurkyA58/Y4h6clUb9ZhI5HIZ UL9T88lUN9jbf/3XsV48+zptozyTg/DP/NSvUNzbNeOse0I26Zk22E1je2DVB4qbBo W5ayb6gNLa/cg== From: "David Hildenbrand (Arm)" Date: Thu, 29 Oct 2026 11:43:35 +0100 Subject: [PATCH v4 14/15] mm/shmem: fix accounting of swapin-error markers X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261029-fixes_shmem-v4-14-392fc715d8f6@kernel.org> References: <20261029-fixes_shmem-v4-0-392fc715d8f6@kernel.org> In-Reply-To: <20261029-fixes_shmem-v4-0-392fc715d8f6@kernel.org> To: Andrew Morton , Gregory Price , Zi Yan , Joshua Hahn , Rakie Kim , Ying Huang , Johannes Weiner , Vladimir Davydov , Michal Hocko , Hugh Dickins , Baolin Wang , Lukas Czerner , Carlos Maiolino , Christian Brauner , Jan Kara , Daniel Verkamp , Jeff Xu , Muchun Song , Oscar Salvador , David Stevens , "Matthew Wilcox (Oracle)" , William Kucharski , Chris Li , Kairui Song , Nhat Pham , Miaohe Lin , Naoya Horiguchi Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, "David Hildenbrand (Arm)" , stable@vger.kernel.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4524; i=david@kernel.org; h=from:subject:message-id; bh=wKlzTfzpZMnScXA1kPIqFAuac+52qIb/BdE1mQIaJKQ=; b=owGbwMvMwCF2/OVpCdfQ3ZWMp9WSGLIeK4f/Vmc9s+2FzUwhPRZTuULuxIAQre6vNZ33l56dv J5l4QexjlIWBjEOBlkxRZag4PsGL1+uq61242OGmcPKBDKEgYtTACbyXJqR4XvIt/+ZATE/o/uW /RVv68q787LjXP/PY2dzj/K2ix1c2M3I0ODUIm08qyrlI5eqRXaO1Fyjsofvby9R9sjyfDEh7cg JXgA= X-Developer-Key: i=david@kernel.org; a=openpgp; fpr=1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A A failed swap read replaces the swap entry with a swapin-error marker in the page-cache and subtracts its pages from "alloced" and "swapped"; however, it leaves their block charge in place. Removing the marker during truncate then subtracts from "swapped" again. This can underflow the counters or make swapped reach zero while other pages remain swapped out, causing swapoff to loop without finding them. When installing the marker, subtract only from "swapped" and let shmem_recalc_inode() reduce "alloced" and release the block charge. Make shmem_free_swap() report successful removal separately from the number of swap pages freed, so removing a swapin-error marker does not subtract from "swapped" again. It's a bit confusing, but replacing a swapped out page by a swapin-error marker in shmem_set_folio_swapin_error() behaves as follows: Initially: alloced=1, swapped=1, nrpages=0 Calling shmem_recalc_inode(inode, 0, -1) -> swapped = 0 -> freed = 1 - 0 - 0 = 1 -> alloced -= freed -> alloced=0, swapped=0, block charge released Fixes: 6cec2b95dadf ("mm/shmem: fix infinite loop when swap in shmem error at swapoff time") Cc: stable@vger.kernel.org Tested-by: Gregory Price (Meta) Reviewed-by: Baolin Wang Signed-off-by: David Hildenbrand (Arm) --- mm/shmem.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index eb2e6e50630c..897f19ed0fe3 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1177,12 +1177,11 @@ static void shmem_delete_from_page_cache(struct folio *folio, void *radswap) } /* - * Remove swap entry from page cache, free the swap and its page cache. Returns - * the number of pages being freed. 0 means entry not found in XArray (0 pages - * being freed). + * Remove a swap entry or swapin-error marker wholly within [index, end]. + * Return whether it was removed, counting freed swap pages in nr_swaps_freed. */ -static long shmem_free_swap(struct address_space *mapping, - pgoff_t index, pgoff_t end, void *radswap) +static bool shmem_free_swap(struct address_space *mapping, + pgoff_t index, pgoff_t end, void *radswap, long *nr_swaps_freed) { XA_STATE(xas, &mapping->i_pages, index); const softleaf_t swp = radix_to_swp_entry(radswap); @@ -1203,8 +1202,10 @@ static long shmem_free_swap(struct address_space *mapping, xas_unlock_irq(&xas); /* A swapin-error marker holds no swap slot, so just drop it. */ - if (nr_pages && softleaf_is_swap(swp)) + if (nr_pages && softleaf_is_swap(swp)) { swap_put_entries_direct(swp, nr_pages); + *nr_swaps_freed += nr_pages; + } return nr_pages; } @@ -1358,8 +1359,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend, if (xa_is_value(folio)) { if (unfalloc) continue; - nr_swaps_freed += shmem_free_swap(mapping, indices[i], - end - 1, folio); + shmem_free_swap(mapping, indices[i], end - 1, + folio, &nr_swaps_freed); continue; } @@ -1422,13 +1423,11 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend, if (xa_is_value(folio)) { int order; - long swaps_freed; if (unfalloc) continue; - swaps_freed = shmem_free_swap(mapping, indices[i], - end - 1, folio); - if (!swaps_freed) { + if (!shmem_free_swap(mapping, indices[i], end - 1, + folio, &nr_swaps_freed)) { pgoff_t base = indices[i]; order = shmem_confirm_swap(mapping, indices[i], @@ -1449,7 +1448,6 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend, index = base; break; } - nr_swaps_freed += swaps_freed; continue; } @@ -2409,11 +2407,10 @@ static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index, folio_put_swap(folio, NULL); swap_cache_del_folio(folio); /* - * Don't treat swapin error folio as alloced. Otherwise inode->i_blocks - * won't be 0 when inode is released and thus trigger WARN_ON(i_blocks) - * in shmem_evict_inode(). + * Let shmem_recalc_inode() reduce alloced and release the block charge + * for the freed swap pages. */ - shmem_recalc_inode(inode, -nr_pages, -nr_pages); + shmem_recalc_inode(inode, 0, -nr_pages); } static int shmem_split_large_entry(struct inode *inode, pgoff_t index, -- 2.43.0