Commit 16e5bb34 authored by Yuchen Tang's avatar Yuchen Tang
Browse files

etmem: add etmem swap feature

euleros inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8T1MB?from=project-issue


CVE: NA

-------------------------------------------------

This patch implements the etmem swap feature.

etmem swap, like etmem scan, also communicated with the user space program
thought a registered proc file system. It adds target pages to swap cache
to be further reclaimed by kswapd, and dwell in swap space.

Signed-off-by: default avataryanxiaodan <yanxiaodan@huawei.com>
Signed-off-by: default avatarlinmiaohe <linmiaohe@huawei.com>
Signed-off-by: default avatarlouhongxiang <louhongxiang@huawei.com>
Signed-off-by: default avatarliubo <liubo254@huawei.com>
Signed-off-by: default avatargeruijun <geruijun@huawei.com>
Signed-off-by: default avatarYuchen Tang <tangyuchen5@huawei.com>
parent 49945974
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -36,4 +36,5 @@ proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o
proc-$(CONFIG_BOOT_CONFIG)	+= bootconfig.o
proc-$(CONFIG_BOOT_CONFIG)	+= bootconfig.o
proc-$(CONFIG_MEMORY_RELIABLE)	+= mem_reliable.o
proc-$(CONFIG_MEMORY_RELIABLE)	+= mem_reliable.o
obj-$(CONFIG_ETMEM_SCAN)	+= etmem_scan.o
obj-$(CONFIG_ETMEM_SCAN)	+= etmem_scan.o
obj-$(CONFIG_ETMEM_SWAP)	+= etmem_swap.o
proc-${CONFIG_ETMEM}		+= etmem_proc.o
proc-${CONFIG_ETMEM}		+= etmem_proc.o
+2 −0
Original line number Original line Diff line number Diff line
@@ -3368,6 +3368,7 @@ static const struct pid_entry tgid_base_stuff[] = {
#endif
#endif
#ifdef CONFIG_ETMEM
#ifdef CONFIG_ETMEM
	REG("idle_pages", S_IRUSR|S_IWUSR, proc_mm_idle_operations),
	REG("idle_pages", S_IRUSR|S_IWUSR, proc_mm_idle_operations),
	REG("swap_pages", S_IWUSR, proc_mm_swap_operations),
#endif
#endif
#ifdef CONFIG_SECURITY
#ifdef CONFIG_SECURITY
	DIR("attr",       S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
	DIR("attr",       S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
@@ -3723,6 +3724,7 @@ static const struct pid_entry tid_base_stuff[] = {
#endif
#endif
#ifdef CONFIG_ETMEM
#ifdef CONFIG_ETMEM
	REG("idle_pages", S_IRUSR|S_IWUSR, proc_mm_idle_operations),
	REG("idle_pages", S_IRUSR|S_IWUSR, proc_mm_idle_operations),
	REG("swap_pages", S_IWUSR, proc_mm_swap_operations),
#endif
#endif
#ifdef CONFIG_SECURITY
#ifdef CONFIG_SECURITY
	DIR("attr",      S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
	DIR("attr",      S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
+94 −0
Original line number Original line Diff line number Diff line
@@ -120,3 +120,97 @@ const struct file_operations proc_mm_idle_operations = {
	.release	= mm_idle_release,
	.release	= mm_idle_release,
	.unlocked_ioctl = mm_idle_ioctl,
	.unlocked_ioctl = mm_idle_ioctl,
};
};

static DEFINE_SPINLOCK(swap_lock);

static int page_swap_lock(struct file *file, int is_lock, struct file_lock *flock)
{
	if (is_lock)
		spin_lock(&swap_lock);
	else
		spin_unlock(&swap_lock);

	return 0;
}
/*swap pages*/
struct file_operations proc_swap_pages_operations = {
	.flock = page_swap_lock,
};
EXPORT_SYMBOL_GPL(proc_swap_pages_operations);

static ssize_t mm_swap_write(struct file *file, const char __user *buf,
		size_t count, loff_t *ppos)
{
	if (proc_swap_pages_operations.write)
		return proc_swap_pages_operations.write(file, buf, count, ppos);

	return -1;
}

static int mm_swap_open(struct inode *inode, struct file *file)
{
	struct mm_struct *mm = NULL;
	struct module *module = NULL;
	int ret = -1;

	if (!file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN))
		return -EPERM;

	page_swap_lock(NULL, 1, NULL);
	module = proc_swap_pages_operations.owner;
	if (module != NULL && try_module_get(module))
		ret = 0;
	page_swap_lock(NULL, 0, NULL);
	if (ret != 0) {
		/* no swap ko installed, avoid to return valid file */
		return -ENODEV;
	}

	mm = proc_mem_open(inode, PTRACE_MODE_READ);
	if (IS_ERR(mm)) {
		module_put(module);
		return PTR_ERR(mm);
	}

	file->private_data = mm;

	if (proc_swap_pages_operations.open)
		ret = proc_swap_pages_operations.open(inode, file);

	if (ret != 0)
		module_put(module);

	return ret;
}

static int mm_swap_release(struct inode *inode, struct file *file)
{
	struct mm_struct *mm = file->private_data;
	int ret = 0;

	if (mm)
		mmdrop(mm);

	if (proc_swap_pages_operations.release)
		ret = proc_swap_pages_operations.release(inode, file);

	if (proc_swap_pages_operations.owner)
		module_put(proc_swap_pages_operations.owner);

	return ret;
}

static long mm_swap_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (proc_swap_pages_operations.unlocked_ioctl)
		return proc_swap_pages_operations.unlocked_ioctl(filp, cmd, arg);
	return 0;
}

const struct file_operations proc_mm_swap_operations = {
	.llseek     = mem_lseek,
	.write      = mm_swap_write,
	.open       = mm_swap_open,
	.release    = mm_swap_release,
	.unlocked_ioctl = mm_swap_ioctl,
};

fs/proc/etmem_swap.c

0 → 100644
+109 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <linux/sched/mm.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/mempolicy.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/etmem.h>

static ssize_t swap_pages_write(struct file *file, const char __user *buf,
				size_t count, loff_t *ppos)
{
	char *p, *data, *data_ptr_res;
	unsigned long vaddr;
	struct mm_struct *mm = file->private_data;
	struct page *page;
	LIST_HEAD(pagelist);
	int ret = 0;

	if (!mm || !mmget_not_zero(mm)) {
		ret = -ESRCH;
		goto out;
	}

	if (count < 0) {
		ret = -EOPNOTSUPP;
		goto out_mm;
	}

	data = memdup_user_nul(buf, count);
	if (IS_ERR(data)) {
		ret = PTR_ERR(data);
		goto out_mm;
	}

	data_ptr_res = data;
	while ((p = strsep(&data, "\n")) != NULL) {
		if (!*p)
			continue;

		ret = kstrtoul(p, 16, &vaddr);
		if (ret != 0)
			continue;

		/* If get page struct failed, ignore it, get next page */
		page = get_page_from_vaddr(mm, vaddr);
		if (!page)
			continue;

		add_page_for_swap(page, &pagelist);
	}

	if (!list_empty(&pagelist))
		reclaim_pages(&pagelist);

	ret = count;
	kfree(data_ptr_res);
out_mm:
	mmput(mm);
out:
	return ret;
}

static int swap_pages_open(struct inode *inode, struct file *file)
{
	if (!try_module_get(THIS_MODULE))
		return -EBUSY;

	return 0;
}

static int swap_pages_release(struct inode *inode, struct file *file)
{
	module_put(THIS_MODULE);
	return 0;
}

extern struct file_operations proc_swap_pages_operations;

static int swap_pages_entry(void)
{
	proc_swap_pages_operations.flock(NULL, 1, NULL);
	proc_swap_pages_operations.owner = THIS_MODULE;
	proc_swap_pages_operations.write = swap_pages_write;
	proc_swap_pages_operations.open = swap_pages_open;
	proc_swap_pages_operations.release = swap_pages_release;
	proc_swap_pages_operations.flock(NULL, 0, NULL);

	return 0;
}

static void swap_pages_exit(void)
{
	proc_swap_pages_operations.flock(NULL, 1, NULL);
	proc_swap_pages_operations.owner = NULL;
	proc_swap_pages_operations.write = NULL;
	proc_swap_pages_operations.open = NULL;
	proc_swap_pages_operations.release = NULL;
	proc_swap_pages_operations.flock(NULL, 0, NULL);
}

MODULE_LICENSE("GPL");
module_init(swap_pages_entry);
module_exit(swap_pages_exit);
+1 −0
Original line number Original line Diff line number Diff line
@@ -305,6 +305,7 @@ extern const struct file_operations proc_clear_refs_operations;
extern const struct file_operations proc_pagemap_operations;
extern const struct file_operations proc_pagemap_operations;
#ifdef CONFIG_ETMEM
#ifdef CONFIG_ETMEM
extern const struct file_operations proc_mm_idle_operations;
extern const struct file_operations proc_mm_idle_operations;
extern const struct file_operations proc_mm_swap_operations;
#endif
#endif


extern unsigned long task_vsize(struct mm_struct *);
extern unsigned long task_vsize(struct mm_struct *);
Loading