217 lines
4.7 KiB
C
217 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/seq_file.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/sched/mm.h>
|
|
#include <linux/mmu_notifier.h>
|
|
#include <linux/page_idle.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/pkeys.h>
|
|
#include <linux/module.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/etmem.h>
|
|
|
|
#include "internal.h"
|
|
|
|
static DEFINE_SPINLOCK(scan_lock);
|
|
|
|
static int page_scan_lock(struct file *file, int is_lock, struct file_lock *flock)
|
|
{
|
|
if (is_lock)
|
|
spin_lock(&scan_lock);
|
|
else
|
|
spin_unlock(&scan_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* will be filled when kvm_ept_idle module loads */
|
|
struct file_operations proc_page_scan_operations = {
|
|
.flock = page_scan_lock,
|
|
};
|
|
EXPORT_SYMBOL_GPL(proc_page_scan_operations);
|
|
|
|
static ssize_t mm_idle_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct mm_struct *mm = file->private_data;
|
|
int ret = 0;
|
|
|
|
if (!mm || !mmget_not_zero(mm)) {
|
|
ret = -ESRCH;
|
|
return ret;
|
|
}
|
|
if (proc_page_scan_operations.read)
|
|
ret = proc_page_scan_operations.read(file, buf, count, ppos);
|
|
|
|
mmput(mm);
|
|
return ret;
|
|
}
|
|
|
|
static int mm_idle_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_scan_lock(NULL, 1, NULL);
|
|
module = proc_page_scan_operations.owner;
|
|
if (module != NULL && try_module_get(module))
|
|
ret = 0;
|
|
page_scan_lock(NULL, 0, NULL);
|
|
if (ret != 0) {
|
|
/* no scan 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_page_scan_operations.open)
|
|
ret = proc_page_scan_operations.open(inode, file);
|
|
|
|
if (ret != 0)
|
|
module_put(module);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mm_idle_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct mm_struct *mm = file->private_data;
|
|
int ret = 0;
|
|
|
|
if (mm) {
|
|
if (!mm_kvm(mm))
|
|
flush_tlb_mm(mm);
|
|
mmdrop(mm);
|
|
}
|
|
|
|
if (proc_page_scan_operations.release)
|
|
ret = proc_page_scan_operations.release(inode, file);
|
|
|
|
if (proc_page_scan_operations.owner)
|
|
module_put(proc_page_scan_operations.owner);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long mm_idle_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
if (proc_page_scan_operations.unlocked_ioctl)
|
|
return proc_page_scan_operations.unlocked_ioctl(filp, cmd, arg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct file_operations proc_mm_idle_operations = {
|
|
.llseek = mem_lseek, /* borrow this */
|
|
.read = mm_idle_read,
|
|
.open = mm_idle_open,
|
|
.release = mm_idle_release,
|
|
.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,
|
|
};
|