From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 24C4B35DA78 for ; Tue, 18 Aug 2026 10:08:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787047712; cv=none; b=cj4YUuDVYnduexD2/Pq4OqXg+7wAJ8kZkzsn6aTPDpshAsVQsqa/Jy40gOtN5PdQP5z/BW2qFg2hxNAuPAtI65KiAgw6edvsUw+Q212zLUxaeRITvWAAK3QeK3J5bxm7eVFlRgAepNS2OuoGYGBhKy7QLpMQlBICOdS7oivVTyA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787047712; c=relaxed/simple; bh=YAGrMLLCUasxDttA7eyHnCoI15R8kt2N/zq1pMcZSZM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MM09V5vOFJZSaQd+an8NvhHGmNeG9z4BPeyCJ1CdUCIweFemHPIgCEBhzjF4K1GWMEMak2PWL4SrDae1TKBQFL0dzxU3JtmuXSP8YWldOAPulr+ptl25GipwZuFs1uT6o1Mo4CmronVPUVuqkMvNxEeDyZrqF9uF8bGjED2+0s4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=tE21RPI2; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="tE21RPI2" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=G9KuxdbMXarhzOqWryoLJREjzAVULu+8jNKt/2uhZrc=; b=tE21RPI2vktLk0uTk2t0g+KUU2 6XNkmTbilLx3CArimV0Kn3yV6z5SPduqZqUqQitICJAivYHuLBHzeR15ghsNe+7YFnNz4XdH7abte QkopcDpnICt/YU1rlo7lQqrdSXalKIGxk4spjGLeePTda/LKdOolR1k4pQ39eAnr9m/ZB4Tk4gazp Bq6KUZwghm5IOIhGhbjf8Xntk8dL8ASKTrAlFrNdvyJrhtxpxe2BNOz1zcAanPUUhT16WCeF7aHVn 7Qe7fBmuEJdgOeGRHCGUk5s+fUkseXBPgEBuYctxuYevOu6mPXPY5qt4fm5kKTO3m084xa+l/AJQl NnK5kq1w==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1wwGk1-008wGT-08; Tue, 18 Aug 2026 10:08:21 +0000 From: Breno Leitao Date: Tue, 18 Aug 2026 03:06:23 -0700 Subject: [PATCH v3 1/2] mm, swap: distinguish a malformed swap entry from a dying device 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: 7bit Message-Id: <20260818-swap-v3-1-d3fa52598a59@debian.org> References: <20260818-swap-v3-0-d3fa52598a59@debian.org> In-Reply-To: <20260818-swap-v3-0-d3fa52598a59@debian.org> To: Andrew Morton , Chris Li , Kairui Song , Kemeng Shi , Nhat Pham , Baoquan He , Barry Song , Youngjun Park , David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Jann Horn , Pedro Falcato , Hugh Dickins , Baolin Wang , Peter Xu , Johannes Weiner , Yosry Ahmed , Chengming Zhou Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, kernel-team@meta.com, Breno Leitao X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=6257; i=leitao@debian.org; h=from:subject:message-id; bh=YAGrMLLCUasxDttA7eyHnCoI15R8kt2N/zq1pMcZSZM=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqhC8HOtBiM0iguv0eeUSXDTd55DYPFmgOsnGpS xdL87D89yqJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaoQvBwAKCRA1o5Of/Hh3 beE+D/41At++CwJ+00eHB2rsH+6LnmclU55BnvqWAfWdwE8FNybwh21lVFRdDHgFwM7qJ+vNGma WMGtADyxG+UC7pmCnA7Lcn+6jVPloKif95TBWbomexE1KBGTL2vkGjCxAXB0DYP4l/Bvf9W7O96 zH7O+juAPpoise6f0EVIzeFnmrVbX52Povx/G9Lneml+V17fwaFWDYrgc1aujiY+9AZiY1GPDoC U8b9bhQVzUnx8/cGs94iODqqCW7Tn+9SOqj8GTJoUY3fRD3SDyD07uMZFF//f+KmJVlJ4o4ZcsD 2M7BBPJwxpf4pff8ZEMC4SACf5VSip/DV7vJmbsUy3V4T2cspBFSt0ca6H5VyQyTLf4gJDd4z6k SJLxSCIbcJ23yr5kTgB+86v7GAAlCIrOfed/yuQkr0SxdpmQozGOaa5+5ChtaLfE1hOwH9xUL3a R7OrsAmHymD027cM8QQGzWoBR9i1xrA7qbgoKVQYCJ98m5M53VkxziDKGV69QNfvU0klQ4C6F4W 2JIhKWGnGDnGayIIcUa4HoTcxbkOArLeKTClci6esFVoI1gq3ySpM5uXGIBtR3PQScQcPBaFzTd Ooovj9k1uxj19+axmXXoNrEHVDf1VXdZJPDBijGUj9OelkBTxV8DGkuTyQRA+n+z2n6xYd6F9Km Z9kSbPaSAiOzWAA== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao get_swap_device() returns NULL for two different things: an entry whose type names no swap device or whose offset is past the end of one, and a device that swapoff is taking away. The first never becomes valid, the second does, and callers cannot tell them apart. Return ERR_PTR(-EIO) for the two malformed cases and keep NULL for swapoff. copy_nonpresent_pte() already reports -EIO for an entry whose type names no device. Callers bail out on failure either way, so switch them to IS_ERR_OR_NULL(), and let the two paths that drop the reference skip an error pointer. No functional change. Reviewed-by: Barry Song Acked-by: Kairui Song Signed-off-by: Breno Leitao --- mm/memory.c | 6 +++--- mm/mincore.c | 2 +- mm/shmem.c | 2 +- mm/swap_state.c | 4 ++-- mm/swapfile.c | 14 +++++++++----- mm/userfaultfd.c | 4 ++-- mm/zswap.c | 2 +- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index d9cf941967cf0..03d8cf111d0be 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4954,9 +4954,9 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) goto out; } - /* Prevent swapoff from happening to us. */ + /* Prevent swapoff from happening to us, and reject a bad entry. */ si = get_swap_device(entry); - if (unlikely(!si)) + if (IS_ERR_OR_NULL(si)) goto out; folio = swap_cache_get_folio(entry); @@ -5266,7 +5266,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) if (vmf->pte) pte_unmap_unlock(vmf->pte, vmf->ptl); out: - if (si) + if (!IS_ERR_OR_NULL(si)) put_swap_device(si); return ret; out_nomap: diff --git a/mm/mincore.c b/mm/mincore.c index ff4ac82817683..c086836bc4bcc 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -71,7 +71,7 @@ static unsigned char mincore_swap(swp_entry_t entry, bool shmem) */ if (shmem) { si = get_swap_device(entry); - if (!si) + if (IS_ERR_OR_NULL(si)) return 0; } folio = swap_cache_get_folio(entry); diff --git a/mm/shmem.c b/mm/shmem.c index 65572cbf1bd3c..d0a9f52bfed71 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2276,7 +2276,7 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index, si = get_swap_device(index_entry); order = shmem_confirm_swap(mapping, index, index_entry); - if (unlikely(!si)) { + if (IS_ERR_OR_NULL(si)) { if (order < 0) return -EEXIST; else diff --git a/mm/swap_state.c b/mm/swap_state.c index 4b7a3303c463b..f2e86d6626ecc 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -715,7 +715,7 @@ struct folio *read_swap_cache_async(struct swap_io_ctx *ctx, swp_entry_t entry, struct folio *folio; si = get_swap_device(entry); - if (!si) + if (IS_ERR_OR_NULL(si)) return NULL; mpol = get_vma_policy(vma, addr, 0, &ilx); @@ -951,7 +951,7 @@ static struct folio *swap_vma_readahead(swp_entry_t targ_entry, gfp_t gfp_mask, */ if (swp_type(entry) != swp_type(targ_entry)) { si = get_swap_device(entry); - if (!si) + if (IS_ERR_OR_NULL(si)) continue; } folio = swap_cache_read_folio(&ctx, entry, gfp_mask, mpol, ilx, diff --git a/mm/swapfile.c b/mm/swapfile.c index 4d4e3e3059f6b..3b1883930e943 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1504,7 +1504,7 @@ int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp) unsigned long offset = swp_offset(entry); si = get_swap_device(entry); - if (!si) + if (IS_ERR_OR_NULL(si)) return 0; ci = __swap_offset_to_cluster(si, offset); @@ -1859,7 +1859,10 @@ void folio_put_swap(struct folio *folio, struct page *page) * Check whether swap entry is valid in the swap device. If so, * return pointer to swap_info_struct, and keep the swap entry valid * via preventing the swap device from being swapoff, until - * put_swap_device() is called. Otherwise return NULL. + * put_swap_device() is called. Return NULL for an empty entry or a + * device that is going away, and ERR_PTR(-EIO) if the entry's type + * names no swap device or its offset is past the end of one. These EIOs + * are preceded by pr_err(). * * Notice that swapoff or swapoff+swapon can still happen before the * percpu_ref_tryget_live() in get_swap_device() or after the @@ -1900,12 +1903,13 @@ struct swap_info_struct *get_swap_device(swp_entry_t entry) return si; bad_nofile: pr_err("%s: %s%08lx\n", __func__, Bad_file, entry.val); + return ERR_PTR(-EIO); out: return NULL; put_out: pr_err("%s: %s%08lx\n", __func__, Bad_offset, entry.val); percpu_ref_put(&si->users); - return NULL; + return ERR_PTR(-EIO); } /* @@ -2001,7 +2005,7 @@ int swp_swapcount(swp_entry_t entry) int count; si = get_swap_device(entry); - if (!si) + if (IS_ERR_OR_NULL(si)) return 0; ci = swap_cluster_lock(si, swp_offset(entry)); @@ -2127,7 +2131,7 @@ void swap_put_entries_direct(swp_entry_t entry, int nr) struct swap_info_struct *si; si = get_swap_device(entry); - if (WARN_ON_ONCE(!si)) + if (WARN_ON_ONCE(IS_ERR_OR_NULL(si))) return; if (WARN_ON_ONCE(end_offset > si->max)) goto out; diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 24a4d92ffa3c2..cba5e20a641ed 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -1700,7 +1700,7 @@ static long move_pages_ptes(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd } si = get_swap_device(entry); - if (unlikely(!si)) { + if (IS_ERR_OR_NULL(si)) { ret = -EAGAIN; goto out; } @@ -1757,7 +1757,7 @@ static long move_pages_ptes(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd if (dst_pte) pte_unmap(dst_pte); mmu_notifier_invalidate_range_end(&range); - if (si) + if (!IS_ERR_OR_NULL(si)) put_swap_device(si); return ret; diff --git a/mm/zswap.c b/mm/zswap.c index f7c9c89f6449c..bc9b931d6f447 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -997,7 +997,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry, /* try to allocate swap cache folio */ si = get_swap_device(swpentry); - if (!si) + if (IS_ERR_OR_NULL(si)) return -EEXIST; mpol = get_task_policy(current); -- 2.53.0-Meta