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:
parent
5dc70a633d
commit
d5a175186d
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue