rue: Add support for rue modularization

Add framework support to enable rue to be installed as
a separate module.

In order to safely insmod/rmmod, we use per-cpu counter to
track how many rue related functions are on the fly, and
it's only safe to insmod/rmmod when there's no tasks using
any of these functions registered by rue module.

Signed-off-by: Ze Gao <zegao@tencent.com>
This commit is contained in:
Ze Gao 2023-08-23 10:45:27 +08:00 committed by Haisu Wang
parent 5dc70a633d
commit d5a175186d
3 changed files with 153 additions and 1 deletions

65
include/linux/rue.h Normal file
View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_RUE_H
#define __LINUX_RUE_H
#include <linux/types.h>
#include <linux/percpu.h>
#include <linux/mutex.h>
struct rue_ops {};
extern bool rue_installed;
extern struct rue_ops *rue_mod_ops;
DECLARE_PER_CPU(long, nr_rue_calls);
extern struct mutex rue_mutex;
int register_rue_ops(struct rue_ops *ops);
int try_unregister_rue_ops(void);
#define RUE_FUNC(subsys, ops, func) RUE_##subsys##_FUNC(ops, func)
#define RUE_CALL_TYPE(subsys, func, retype, ...) \
({ \
retype ret = {0}; \
struct rue_ops *__ops; \
typeof(RUE_FUNC(subsys, __ops, func)) __f; \
preempt_disable(); \
__ops = READ_ONCE(rue_mod_ops); \
if (__ops) { \
__f = RUE_FUNC(subsys, __ops, func); \
BUG_ON(!__f); \
this_cpu_inc(nr_rue_calls); \
preempt_enable(); \
ret = __f(__VA_ARGS__); \
this_cpu_dec(nr_rue_calls); \
} else { \
preempt_enable(); \
} \
ret; \
})
#define RUE_CALL_VOID(subsys, func, ...) \
({ \
struct rue_ops *__ops; \
typeof(RUE_FUNC(subsys, __ops, func)) __f; \
preempt_disable(); \
__ops = READ_ONCE(rue_mod_ops); \
if (__ops) { \
__f = RUE_FUNC(subsys, __ops, func); \
BUG_ON(!__f); \
this_cpu_inc(nr_rue_calls); \
preempt_enable(); \
__f(__VA_ARGS__); \
this_cpu_dec(nr_rue_calls); \
} else { \
preempt_enable(); \
} \
})
#define RUE_CALL_PTR(subsys, func, ...) \
RUE_CALL_TYPE(subsys, func, void *, __VA_ARGS__)
#define RUE_CALL_INT(subsys, func, ...) \
RUE_CALL_TYPE(subsys, func, int, __VA_ARGS__)
#endif /* __LINUX_RUE_H */

View File

@ -10,7 +10,7 @@ obj-y = fork.o exec_domain.o panic.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o \
notifier.o ksysfs.o cred.o reboot.o \
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o rue.o
obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
obj-$(CONFIG_MULTIUSER) += groups.o

87
kernel/rue.c Normal file
View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include <linux/delay.h>
#include <linux/rue.h>
bool rue_installed;
DEFINE_PER_CPU(long, nr_rue_calls);
struct rue_ops *rue_mod_ops;
DEFINE_MUTEX(rue_mutex);
static bool rue_used(void)
{
int cpu;
long total = 0;
for_each_possible_cpu(cpu)
total += per_cpu(nr_rue_calls, cpu);
pr_info("RUE: cpu %d sees the sum of nr_rue_calls %ld\n",
smp_processor_id(), total);
return !!total;
}
static int check_patch_state(struct rue_ops *ops)
{
int ret = 0;
if (ops) {
/* check if patching */
} else {
/* check if unpatching */
}
return ret;
}
int register_rue_ops(struct rue_ops *ops)
{
int ret = 0;
cpus_read_lock();
mutex_lock(&rue_mutex);
if (rue_used()) {
ret = -EBUSY;
pr_warn("RUE: system corrupted, "
"failed to register rue_ops");
goto out;
}
ret = check_patch_state(ops);
if (ret)
goto out;
WRITE_ONCE(rue_mod_ops, ops);
out:
WRITE_ONCE(rue_installed, !ret);
mutex_unlock(&rue_mutex);
cpus_read_unlock();
return ret;
}
EXPORT_SYMBOL(register_rue_ops);
int try_unregister_rue_ops(void)
{
int ret = 0;
cpus_read_lock();
mutex_lock(&rue_mutex);
ret = check_patch_state(NULL);
if (ret)
goto out;
WRITE_ONCE(rue_mod_ops, NULL);
synchronize_rcu();
while (rue_used()) {
if (!cond_resched())
cpu_relax();
}
out:
WRITE_ONCE(rue_installed, !!ret);
mutex_unlock(&rue_mutex);
cpus_read_unlock();
return ret;
}
EXPORT_SYMBOL(try_unregister_rue_ops);