From: Baoquan He An xswap device has no backing file, so /proc/swaps shows a synthetic name, xswap, that file_open_name() cannot open. Teach swapoff to fall back to xswap_destroy() when the name is exactly "xswap" and the lookup failed with -ENOENT. A real file or directory keeps precedence; other open errors are returned unchanged, so a file that exists but cannot be opened is not mistaken for an xswap device. The ABI documentation is updated, and the destroy attribute remains the unambiguous teardown interface. A well-formed name for an id that does not exist asks xswap_destroy() for a device that is not there. It returns -ENODEV; map that to -ENOENT so swapoff reports a missing name like it does for a missing file. Signed-off-by: Baoquan He --- .../ABI/testing/sysfs-kernel-mm-xswap | 11 +++- mm/swapfile.c | 56 ++++++++++++++++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-xswap b/Documentation/ABI/testing/sysfs-kernel-mm-xswap index 38eead0b1b31..f7e7f3e4d216 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-xswap +++ b/Documentation/ABI/testing/sysfs-kernel-mm-xswap @@ -9,9 +9,14 @@ Description: File-less swap devices backed by zswap. for the lifetime of the device. The name shown in /proc/swaps, xswap, is synthetic and - is not a path. It cannot be passed to swapoff(2), so - swapoff -a reports an error for these entries. Use the - destroy attribute instead. + is not a path. swapoff(2) accepts it, so both "swapoff + xswap" and swapoff -a tear the device down. A real + file or directory with that name takes precedence: if it + opens, it is looked up as a swap file and the synthetic + device is not used. Only a name that cannot be opened + because it does not exist, including a dangling symlink, + is treated as an xswap device. Use the destroy attribute + for an unambiguous teardown. An xswap device requires zswap. Disabling zswap at runtime (enabled=0, or max_pool_percent=0) while xswap devices diff --git a/mm/swapfile.c b/mm/swapfile.c index cb53f9415de8..7b1d16226b7b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,7 @@ static int xswap_mapped_end(pte_t *pte, unsigned long addr, void *data); static int xswap_create(int prio); static int xswap_destroy(u64 id); +static int xswap_destroy_by_name(const char *name); /* Stable device id, assigned at create and never reused. */ static atomic64_t xswap_id_counter = ATOMIC64_INIT(0); @@ -275,6 +277,11 @@ static void xswap_sysfs_init(void) static inline void xswap_sysfs_init(void) { } + +static inline int xswap_destroy_by_name(const char *name) +{ + return -ENOENT; +} #endif /* CONFIG_XSWAP */ static void swap_range_alloc(struct swap_info_struct *si, @@ -3513,8 +3520,21 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) CLASS(filename, pathname)(specialfile); victim = file_open_name(pathname, O_RDWR|O_LARGEFILE, 0); - if (IS_ERR(victim)) - return PTR_ERR(victim); + if (IS_ERR(victim)) { + /* + * An xswap device has no file, so /proc/swaps shows a name + * that cannot be opened. Accept it here so swapoff -a works. + * Only a missing name is taken for a device; a real file or + * directory that failed for another reason must not match. + */ + if (PTR_ERR(victim) != -ENOENT) + return PTR_ERR(victim); + + err = xswap_destroy_by_name(pathname->name); + if (err == -ENOENT) + return PTR_ERR(victim); + return err; + } mapping = victim->f_mapping; spin_lock(&swap_lock); @@ -4504,6 +4524,38 @@ static int xswap_destroy(u64 id) return __swapoff(p); } + +static int xswap_destroy_by_name(const char *name) +{ + const char *p; + char buf[24]; + u64 id; + int err; + + /* Strictly "xswap": no sign, no trailing newline. */ + if (strncmp(name, "xswap", 5)) + return -ENOENT; + + p = name + 5; + if (!isdigit((unsigned char)*p)) + return -ENOENT; + while (isdigit((unsigned char)*p)) + p++; + if (*p) + return -ENOENT; + + if (strscpy(buf, name + 5, sizeof(buf)) < 0) + return -ENOENT; + + err = kstrtou64(buf, 10, &id); + if (err) + return -ENOENT; + + err = xswap_destroy(id); + if (err == -ENODEV) + return -ENOENT; + return err; +} #endif /* CONFIG_XSWAP */ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) -- 2.54.0