From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, david@kernel.org, chrisl@kernel.org,
kasong@tencent.com, nphamcs@gmail.com, baohua@kernel.org,
youngjun.park@lge.com, corbet@lwn.net, hannes@cmpxchg.org,
chengming.zhou@linux.dev, kunwu.chan@gmail.com,
klarasmodin@gmail.com, linux-kernel@vger.kernel.org,
Baoquan He <hebaoquan@kylinos.cn>
Subject: [PATCH v5 10/19] mm, swap: let the command line set the xswap device size
Date: Mon, 9 Nov 2026 13:54:46 +0800 [thread overview]
Message-ID: <20261109055455.536925-11-baoquan.he@linux.dev> (raw)
In-Reply-To: <20261109055455.536925-1-baoquan.he@linux.dev>
From: Baoquan He <hebaoquan@kylinos.cn>
By default, a new xswap device is 200% of RAM, which is twice RAM.
Add xswap.max= to set the size of devices created after it:
xswap.max=300% three times RAM
xswap.max=8G eight GiB
xswap.max=max the largest size the device can address
The default is fine for most users. But a machine with a lot of RAM
and a small working set can get a SwapTotal much larger than it needs.
The size is resolved at device creation, not at parameter parsing
because a percentage depends on RAM, and totalram_pages() is not ready
when parameters are parsed.
An explicit size that is too large or overflows is refused. The default
size is clamped to the limit instead. A size below one cluster is also
refused. "max" means the largest addressable size, still bounded by the
unsigned int cluster index.
Signed-off-by: Baoquan He <hebaoquan@kylinos.cn>
---
.../admin-guide/kernel-parameters.txt | 7 +
mm/swapfile.c | 130 ++++++++++++++++--
2 files changed, 127 insertions(+), 10 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0b3c140babd9..e92a1b9062a6 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -8738,3 +8738,10 @@ Kernel parameters
memory, and other data can't be written using
xmon commands.
off xmon is disabled.
+
+ xswap.max= [KNL,SWAP]
+ Maximum size of a new xswap device. The value is a
+ size in bytes with an optional KMGTPE suffix, a
+ percentage of RAM, or "max" for the largest size
+ the device can address.
+ Format: <size>[KMGTPE] | <percent>% | max
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 44b54118d10a..498fbf6df1cd 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -14,6 +14,7 @@
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/kernel_stat.h>
+#include <linux/overflow.h>
#include <linux/swap.h>
#include <linux/vmalloc.h>
#include <linux/kobject.h>
@@ -150,6 +151,100 @@ static struct attribute *xswap_attrs[] = {
NULL,
};
+/*
+ * Size of a new device, from xswap.max=. A percentage unless bytes is
+ * set; "max" asks for the largest size the device can address.
+ */
+static unsigned int xswap_size_pct = 200;
+static unsigned long xswap_size_bytes;
+static bool xswap_size_max;
+static bool xswap_size_set; /* xswap.max= was given */
+
+/*
+ * Parse "200%", "2T" or "max". @str is not modified.
+ */
+static int __init xswap_parse_size(const char *str, const char *what)
+{
+ const char *pct;
+ char buf[16];
+ unsigned int pct_val;
+ unsigned long bytes;
+ char *end;
+
+ pct = strchr(str, '%');
+ if (!strcmp(str, "max")) {
+ xswap_size_max = true;
+ xswap_size_pct = 0;
+ xswap_size_bytes = 0;
+ } else if (pct) {
+ size_t len = pct - str;
+
+ if (pct[1] || !len || len >= sizeof(buf))
+ goto bad;
+ memcpy(buf, str, len);
+ buf[len] = '\0';
+ if (kstrtouint(buf, 0, &pct_val) || !pct_val)
+ goto bad;
+ xswap_size_pct = pct_val;
+ xswap_size_bytes = 0;
+ xswap_size_max = false;
+ } else {
+ bytes = memparse(str, &end);
+ if (*end || bytes < PAGE_SIZE)
+ goto bad;
+ xswap_size_bytes = bytes;
+ xswap_size_pct = 0;
+ xswap_size_max = false;
+ }
+
+ xswap_size_set = true;
+ return 0;
+bad:
+ pr_err("xswap: bad %s=%s\n", what, str);
+ return -EINVAL;
+}
+
+static int __init xswap_max_setup(char *str)
+{
+ xswap_parse_size(str, "xswap.max");
+ return 1;
+}
+__setup("xswap.max=", xswap_max_setup);
+
+/*
+ * The most a device can address, whatever xswap.max= asked for. An xswap
+ * slot is still a swap entry, so the swap offset limit applies even though
+ * the data never goes through a physical address.
+ */
+static unsigned long xswap_size_limit(void)
+{
+ unsigned long limit = swapfile_maximum_size;
+
+ /* A cluster index is an unsigned int. */
+ if (limit / SWAPFILE_CLUSTER > UINT_MAX)
+ limit = (unsigned long)UINT_MAX * SWAPFILE_CLUSTER;
+
+ return limit;
+}
+
+static unsigned long xswap_size_pages(void)
+{
+ unsigned long long pages;
+
+ if (xswap_size_max)
+ return xswap_size_limit();
+
+ if (xswap_size_bytes)
+ return xswap_size_bytes >> PAGE_SHIFT;
+
+ /* Let xswap_create() refuse a size that does not fit in the type. */
+ if (check_mul_overflow((unsigned long long)totalram_pages(),
+ (unsigned long long)xswap_size_pct, &pages))
+ return ULONG_MAX;
+
+ return div_u64(pages, 100);
+}
+
static const struct attribute_group xswap_attr_group = {
.attrs = xswap_attrs,
};
@@ -4268,7 +4363,8 @@ static int setup_swap_clusters_info(struct swap_info_struct *si,
static int xswap_create(int prio)
{
struct swap_info_struct *si;
- unsigned long ram, maxpages;
+ unsigned long maxpages;
+ unsigned long limit;
int error;
if (prio != DEF_XSWAP_PRIO &&
@@ -4279,6 +4375,28 @@ static int xswap_create(int prio)
if (!zswap_is_enabled())
return -EOPNOTSUPP;
+ /*
+ * Refuse an explicitly requested size the device cannot address;
+ * clamp the default size, which nobody asked for.
+ */
+ limit = xswap_size_limit();
+ maxpages = xswap_size_pages();
+ if (xswap_size_set && maxpages > limit) {
+ pr_err("xswap: xswap.max is larger than the device can address\n");
+ return -EINVAL;
+ }
+ maxpages = min(maxpages, limit);
+ /*
+ * Below one cluster the device is not cluster-aligned, so its only
+ * cluster would hold slots past si->max. Nothing masks those, and a
+ * slot allocated there is past the end of the device: it can never be
+ * read back or given up again.
+ */
+ if (maxpages < SWAPFILE_CLUSTER) {
+ pr_err("xswap: xswap.max is smaller than one cluster\n");
+ return -EINVAL;
+ }
+
si = alloc_swap_info();
if (IS_ERR(si))
return PTR_ERR(si);
@@ -4286,16 +4404,8 @@ static int xswap_create(int prio)
INIT_WORK(&si->discard_work, swap_discard_work);
INIT_WORK(&si->reclaim_work, swap_reclaim_work);
- ram = totalram_pages();
- maxpages = min_t(unsigned long, ram * 2, swapfile_maximum_size);
- /* A cluster index is an unsigned int, so cap the cluster count. */
- if (maxpages / SWAPFILE_CLUSTER > UINT_MAX)
- maxpages = (unsigned long)UINT_MAX * SWAPFILE_CLUSTER;
/* Cluster-aligned, so no cluster holds a slot past si->max. */
- if (maxpages > SWAPFILE_CLUSTER)
- maxpages = rounddown(maxpages, SWAPFILE_CLUSTER);
- if (maxpages < 2)
- maxpages = 2;
+ maxpages = rounddown(maxpages, SWAPFILE_CLUSTER);
si->bdev = NULL;
si->swap_file = NULL;
--
2.54.0
next prev parent reply other threads:[~2026-11-09 5:56 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 5:54 [PATCH v5 00/19] mm, swap: extendable swap devices (xswap phase I) Baoquan He
2026-11-09 5:54 ` [PATCH v5 01/19] mm: xswap support for zswap Baoquan He
2026-11-10 5:57 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 02/19] mm, swap: refactor free_swap_cluster_info to take swap_info_struct Baoquan He
2026-11-09 5:54 ` [PATCH v5 03/19] mm, swap: add VM_SPARSE cluster-array helpers for xswap Baoquan He
2026-11-09 5:54 ` [PATCH v5 04/19] mm, swap: use a VM_SPARSE cluster array for xswap devices Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 05/19] mm, swap: widen swap_info_struct max/pages to unsigned long Baoquan He
2026-11-09 5:54 ` [PATCH v5 06/19] mm, swap: add a sysfs interface to create xswap devices Baoquan He
2026-11-09 5:54 ` [PATCH v5 07/19] mm, swap: refactor swapoff and add xswap_destroy Baoquan He
2026-11-09 5:54 ` [PATCH v5 08/19] mm, swap: require zswap for xswap devices Baoquan He
2026-11-09 5:54 ` [PATCH v5 09/19] mm, swap: use a stable id " Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` Baoquan He [this message]
2026-11-09 5:54 ` [PATCH v5 11/19] mm, swap: add CONFIG_XSWAP_DEFAULT_MAX Baoquan He
2026-11-09 5:54 ` [PATCH v5 12/19] mm, swap: let swapoff destroy xswap devices by name Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 13/19] mm, swap: add xswap grow trigger on cluster allocation Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 14/19] mm, swap: add debugfs counters for xswap Baoquan He
2026-11-09 5:54 ` [PATCH v5 15/19] mm, swap: make xswap cluster_info accesses RCU-safe Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 16/19] mm, swap: prepare xswap_unmap_clusters for range shrinking Baoquan He
2026-11-09 5:54 ` [PATCH v5 17/19] mm, swap: add xswap shrink trigger and defer it to a workqueue Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 18/19] mm, swap: do not give an xswap slot to a cgroup that cannot zswap Baoquan He
2026-11-10 5:58 ` sashiko-bot
2026-11-09 5:54 ` [PATCH v5 19/19] mm, swap: skip the overcommit probe when removing xswap Baoquan He
2026-11-09 6:15 ` [PATCH v5 00/19] mm, swap: extendable swap devices (xswap phase I) Baoquan He
2026-11-12 7:25 ` [syzbot ci] " syzbot ci
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=20261109055455.536925-11-baoquan.he@linux.dev \
--to=baoquan.he@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=chrisl@kernel.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=hebaoquan@kylinos.cn \
--cc=kasong@tencent.com \
--cc=klarasmodin@gmail.com \
--cc=kunwu.chan@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nphamcs@gmail.com \
--cc=youngjun.park@lge.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.