From: Xueyuan Chen <xueyuan.chen21@gmail.com>
To: akpm@linux-foundation.org, linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org, cgroups@vger.kernel.org,
zhaonanzhe@xiaomi.com, baohua@kernel.org, hannes@cmpxchg.org,
ryncsn@gmail.com, youngjun.park@lge.com,
baolin.wang@linux.alibaba.com, hughd@google.com,
chrisl@kernel.org, shikemeng@huaweicloud.com, nphamcs@gmail.com,
baoquan.he@linux.dev, mhocko@kernel.org,
roman.gushchin@linux.dev, shakeel.butt@linux.dev,
muchun.song@linux.dev, david@kernel.org, ljs@kernel.org,
xueyuan.chen21@gmail.com
Subject: [PATCH v7 2/4] mm: distinguish large folio swap allocation failures
Date: Sun, 30 Aug 2026 12:29:18 +0800 [thread overview]
Message-ID: <20260830042920.2280454-3-xueyuan.chen21@gmail.com> (raw)
In-Reply-To: <20260830042920.2280454-1-xueyuan.chen21@gmail.com>
folio_alloc_swap() reports most failures with generic negative error
codes. Reclaim callers consequently cannot tell whether splitting a large
folio could make progress, or whether no swap space is available for even
a single page.
Classify failures using both the global free swap count and the remaining
capacity in the folio's memcg swap hierarchy. Return -ENOSPC when global
swap space is exhausted, -ENOMEM when splitting cannot overcome the
failure, and -E2BIG for a large folio when allocating or charging a
smaller folio might still succeed.
Use this classification for all folio_alloc_swap() failure paths,
including capability rejection, swap slot allocation failure, and memcg
swap charge failure. Callers are updated separately to split large folios
only on -E2BIG.
Suggested-by: Kairui Song <ryncsn@gmail.com>
Suggested-by: Barry Song <baohua@kernel.org>
Suggested-by: Youngjun Park <youngjun.park@lge.com>
Signed-off-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/linux/swap.h | 6 ++++++
mm/memcontrol.c | 23 +++++++++++++++++++++++
mm/swapfile.c | 26 +++++++++++++++++++-------
3 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 0544b2ec4c56..b23108d992aa 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -525,6 +525,7 @@ static inline void mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_p
__mem_cgroup_uncharge_swap(id, nr_pages);
}
+long mem_cgroup_get_folio_swap_margin(struct folio *folio);
extern long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg);
extern bool mem_cgroup_swap_full(struct folio *folio);
#else
@@ -538,6 +539,11 @@ static inline void mem_cgroup_uncharge_swap(unsigned short id,
{
}
+static inline long mem_cgroup_get_folio_swap_margin(struct folio *folio)
+{
+ return PAGE_COUNTER_MAX;
+}
+
static inline long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg)
{
return get_nr_swap_pages();
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 109c08be91cf..4b42f3fc6075 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -5676,6 +5676,29 @@ long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg)
return nr_swap_pages;
}
+/**
+ * mem_cgroup_get_folio_swap_margin - get a folio's memcg swap margin
+ * @folio: folio whose memcg margin is queried
+ *
+ * Return: Remaining chargeable pages in the folio's memcg hierarchy.
+ */
+long mem_cgroup_get_folio_swap_margin(struct folio *folio)
+{
+ struct mem_cgroup *memcg;
+ long margin;
+
+ if (mem_cgroup_disabled() || do_memsw_account() ||
+ !folio_memcg_charged(folio))
+ return PAGE_COUNTER_MAX;
+
+ rcu_read_lock();
+ memcg = folio_memcg(folio);
+ margin = page_counter_margin(&memcg->swap);
+ rcu_read_unlock();
+
+ return margin;
+}
+
bool mem_cgroup_swap_full(struct folio *folio)
{
struct mem_cgroup *memcg;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 70b90fa9c2a0..651682b1fe63 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1735,7 +1735,9 @@ static int swap_dup_entries_cluster(struct swap_info_struct *si,
* swap cache.
*
* Context: Caller needs to hold the folio lock.
- * Return: Whether the folio was added to the swap cache.
+ * Return: %0 on success, %-E2BIG if splitting the folio might allow swapout,
+ * %-ENOSPC if no global swap space is available, or %-ENOMEM if splitting
+ * would not help.
*/
int folio_alloc_swap(struct folio *folio)
{
@@ -1747,11 +1749,11 @@ int folio_alloc_swap(struct folio *folio)
if (order) {
/*
- * Reject large allocation when THP_SWAP is disabled,
- * the caller should split the folio and try again.
+ * Reject large allocation when THP_SWAP is disabled. Check below
+ * whether splitting and retrying can make progress.
*/
if (!IS_ENABLED(CONFIG_THP_SWAP))
- return -EAGAIN;
+ goto failed;
/*
* Allocation size should never exceed cluster size
@@ -1759,7 +1761,7 @@ int folio_alloc_swap(struct folio *folio)
*/
if (size > SWAPFILE_CLUSTER) {
VM_WARN_ON_ONCE(1);
- return -EINVAL;
+ goto failed;
}
}
@@ -1775,13 +1777,23 @@ int folio_alloc_swap(struct folio *folio)
}
/* Need to call this even if allocation failed, for MEMCG_SWAP_FAIL. */
- if (unlikely(mem_cgroup_try_charge_swap(folio)))
+ if (unlikely(mem_cgroup_try_charge_swap(folio))) {
swap_cache_del_folio(folio);
+ goto failed;
+ }
if (unlikely(!folio_test_swapcache(folio)))
- return -ENOMEM;
+ goto failed;
return 0;
+
+failed:
+ if (get_nr_swap_pages() <= 0)
+ return -ENOSPC;
+ if (mem_cgroup_get_folio_swap_margin(folio) <= 0)
+ return -ENOMEM;
+
+ return order ? -E2BIG : -ENOMEM;
}
/**
--
2.47.3
next prev parent reply other threads:[~2026-08-30 4:29 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-30 4:29 [PATCH v7 0/4] mm: avoid large folio splits when swap is unavailable Xueyuan Chen
2026-08-30 4:29 ` [PATCH v7 1/4] mm: add page_counter_margin() Xueyuan Chen
2026-08-30 4:42 ` Barry Song
2026-08-30 4:29 ` Xueyuan Chen [this message]
2026-08-30 4:29 ` [PATCH v7 3/4] mm/vmscan: avoid pointless large folio splits without swap Xueyuan Chen
2026-08-30 4:29 ` [PATCH v7 4/4] mm/shmem: split large folios only on -E2BIG Xueyuan Chen
2026-09-01 3:39 ` [PATCH v7 0/4] mm: avoid large folio splits when swap is unavailable Andrew Morton
2026-09-01 8:01 ` Xueyuan Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260830042920.2280454-3-xueyuan.chen21@gmail.com \
--to=xueyuan.chen21@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=baoquan.he@linux.dev \
--cc=cgroups@vger.kernel.org \
--cc=chrisl@kernel.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=hughd@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=nphamcs@gmail.com \
--cc=roman.gushchin@linux.dev \
--cc=ryncsn@gmail.com \
--cc=shakeel.butt@linux.dev \
--cc=shikemeng@huaweicloud.com \
--cc=youngjun.park@lge.com \
--cc=zhaonanzhe@xiaomi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.