From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, chrisl@kernel.org, kasong@tencent.com,
shikemeng@huaweicloud.com, nphamcs@gmail.com, baohua@kernel.org,
youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org,
chengming.zhou@linux.dev, linux-kernel@vger.kernel.org,
Baoquan He <baoquan.he@linux.dev>
Subject: [RFC PATCH 10/10] mm/swapfile: add sysfs interface for ghost swap extension
Date: Tue, 7 Jul 2026 16:26:12 +0800 [thread overview]
Message-ID: <20260707082614.95030-11-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260707082614.95030-1-baoquan.he@linux.dev>
Add per-device attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — dentry basename
max (RW) — current max pages; write to trigger swap_ghost_extend_max()
The ghost_kobj field ties each ghost device to its sysfs directory,
created at swapon and removed at swapoff.
Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
include/linux/swap.h | 1 +
mm/swapfile.c | 111 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+)
diff --git a/include/linux/swap.h b/include/linux/swap.h
index b87688b7d4ba..d9ae2a9e33cc 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -283,6 +283,7 @@ struct swap_info_struct {
struct xarray redirect_xa; /* ghost offset → phys swp_entry_t */
struct vm_struct *cluster_vm; /* ghost: sparse vm_area for cluster_info */
char *ghost_name; /* ghost: dentry name for sysfs */
+ struct kobject *ghost_kobj; /* ghost: sysfs per-device directory */
#endif
};
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 8bf336678887..ede10539156a 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -31,6 +31,8 @@
#include <linux/security.h>
#include <linux/backing-dev.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
#include <linux/capability.h>
#include <linux/syscalls.h>
#include <linux/memcontrol.h>
@@ -60,6 +62,8 @@ static void move_cluster(struct swap_info_struct *si,
#define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024)
static int swap_ghost_extend_max(struct swap_info_struct *si,
unsigned int new_maxpages);
+static int ghost_sysfs_register(struct swap_info_struct *si);
+static void ghost_sysfs_unregister(struct swap_info_struct *si);
#endif
/*
@@ -3250,6 +3254,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
p->swap_file = NULL;
maxpages = p->max;
if (p->flags & SWP_GHOST) {
+ ghost_sysfs_unregister(p);
p->max = 0;
cluster_info = NULL;
} else {
@@ -4010,6 +4015,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
(si->flags & SWP_AREA_DISCARD) ? "s" : "",
(si->flags & SWP_PAGE_DISCARD) ? "c" : "");
+ if (si->flags & SWP_GHOST) {
+ error = ghost_sysfs_register(si);
+ if (error)
+ pr_warn("swapon: ghost sysfs register failed: %d\n",
+ error);
+ }
+
mutex_unlock(&swapon_mutex);
atomic_inc(&proc_poll_event);
wake_up_interruptible(&proc_poll_wait);
@@ -4357,6 +4369,105 @@ static int swap_ghost_extend_max(struct swap_info_struct *si,
spin_unlock(&swap_lock);
return 0;
}
+
+/* ─── Ghost swap sysfs interface ─── */
+
+static struct kobject *ghost_swap_kobj;
+
+static struct swap_info_struct *ghost_kobj_to_si(struct kobject *kobj)
+{
+ int type;
+
+ for (type = 0; type < MAX_SWAPFILES; type++) {
+ struct swap_info_struct *si = READ_ONCE(swap_info[type]);
+
+ if (si && si->ghost_kobj == kobj && (si->flags & SWP_USED))
+ return si;
+ }
+ return NULL;
+}
+
+#define GHOST_ATTR_RO(_n) \
+ static ssize_t _n##_show(struct kobject *k, struct kobj_attribute *a, char *b)
+#define GHOST_ATTR_WO(_n) \
+ static ssize_t _n##_store(struct kobject *k, struct kobj_attribute *a, \
+ const char *b, size_t n)
+
+GHOST_ATTR_RO(path)
+{
+ struct swap_info_struct *si = ghost_kobj_to_si(k);
+
+ if (!si || !si->ghost_name)
+ return -ENODEV;
+ return sysfs_emit(b, "%s\n", si->ghost_name);
+}
+
+GHOST_ATTR_RO(max)
+{
+ struct swap_info_struct *si = ghost_kobj_to_si(k);
+
+ if (!si)
+ return -ENODEV;
+ return sysfs_emit(b, "%u\n", si->max);
+}
+
+GHOST_ATTR_WO(max)
+{
+ struct swap_info_struct *si = ghost_kobj_to_si(k);
+ unsigned long val;
+ int ret;
+
+ if (!si)
+ return -ENODEV;
+ ret = kstrtoul(b, 10, &val);
+ if (ret)
+ return ret;
+ if (mutex_lock_interruptible(&swapon_mutex))
+ return -ERESTARTSYS;
+ ret = swap_ghost_extend_max(si, (unsigned int)val);
+ mutex_unlock(&swapon_mutex);
+ return ret ? ret : n;
+}
+
+static struct kobj_attribute ghost_attr_path = __ATTR_RO(path);
+static struct kobj_attribute ghost_attr_max = __ATTR_RW(max);
+static struct attribute *ghost_attrs[] = {
+ &ghost_attr_path.attr,
+ &ghost_attr_max.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ghost);
+
+static int ghost_sysfs_register(struct swap_info_struct *si)
+{
+ char name[32];
+ int ret;
+
+ if (!ghost_swap_kobj) {
+ ghost_swap_kobj = kobject_create_and_add("ghost_swap", mm_kobj);
+ if (!ghost_swap_kobj)
+ return -ENOMEM;
+ }
+ snprintf(name, sizeof(name), "ghost_%d", si->type);
+ si->ghost_kobj = kobject_create_and_add(name, ghost_swap_kobj);
+ if (!si->ghost_kobj)
+ return -ENOMEM;
+ ret = sysfs_create_groups(si->ghost_kobj, ghost_groups);
+ if (ret) {
+ kobject_put(si->ghost_kobj);
+ si->ghost_kobj = NULL;
+ }
+ return ret;
+}
+
+static void ghost_sysfs_unregister(struct swap_info_struct *si)
+{
+ if (!si->ghost_kobj)
+ return;
+ sysfs_remove_groups(si->ghost_kobj, ghost_groups);
+ kobject_put(si->ghost_kobj);
+ si->ghost_kobj = NULL;
+}
#endif /* CONFIG_ZSWAP */
static int __init swapfile_init(void)
--
2.54.0
next prev parent reply other threads:[~2026-07-07 8:27 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 8:26 [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07 8:26 ` [RFC PATCH 01/10] mm: ghost swapfile support for zswap Baoquan He
2026-07-07 8:26 ` [RFC PATCH 02/10] mm/swap_table: add Redirect entry encoding for ghost swap backend switching Baoquan He
2026-07-07 8:26 ` [RFC PATCH 03/10] mm/swap: add redirect_xa field and ghost redirect helper declarations Baoquan He
2026-07-07 8:26 ` [RFC PATCH 04/10] mm/swapfile: implement ghost redirect helpers and free-path cascade Baoquan He
2026-07-07 8:26 ` [RFC PATCH 05/10] mm/swap_state: restore Redirect entry when swap cache folio is removed Baoquan He
2026-07-07 8:26 ` [RFC PATCH 06/10] mm/zswap: implement ghost-to-physical writeback for backend switching Baoquan He
2026-07-07 8:26 ` [RFC PATCH 07/10] mm/page_io: forward ghost swap reads to physical device via Redirect Baoquan He
2026-07-07 8:26 ` [RFC PATCH 08/10] mm/swapfile: manage ghost cluster_info via lazy vmalloc Baoquan He
2026-07-07 8:26 ` [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth Baoquan He
2026-07-07 22:36 ` Nhat Pham
2026-07-09 14:52 ` Baoquan He
2026-07-10 1:11 ` Nhat Pham
2026-07-11 9:02 ` Chris Li
2026-07-07 8:26 ` Baoquan He [this message]
2026-07-07 8:56 ` [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07 21:23 ` Nhat Pham
2026-07-07 21:25 ` Nhat Pham
2026-07-09 11:38 ` Baoquan He
2026-07-13 0:02 ` Nhat Pham
2026-07-13 17:47 ` Chris Li
2026-07-15 11:21 ` Baoquan He
2026-07-15 14:19 ` Baoquan He
2026-07-15 16:17 ` Nhat Pham
2026-07-11 8:49 ` Chris Li
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=20260707082614.95030-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=hannes@cmpxchg.org \
--cc=kasong@tencent.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nphamcs@gmail.com \
--cc=shikemeng@huaweicloud.com \
--cc=yosry@kernel.org \
--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.