srcu: Allocate per-CPU data for DEFINE_SRCU() in modules
Adding DEFINE_SRCU() or DEFINE_STATIC_SRCU() to a loadable module requires that the size of the reserved region be increased, which is not something we want to be doing all that often. One approach would be to require that loadable modules define an srcu_struct and invoke init_srcu_struct() from their module_init function and cleanup_srcu_struct() from their module_exit function. However, this is more than a bit user unfriendly. This commit therefore creates an ___srcu_struct_ptrs linker section, and pointers to srcu_struct structures created by DEFINE_SRCU() and DEFINE_STATIC_SRCU() within a module are placed into that module's ___srcu_struct_ptrs section. The required init_srcu_struct() and cleanup_srcu_struct() functions are then automatically invoked as needed when that module is loaded and unloaded, thus allowing modules to continue to use DEFINE_SRCU() and DEFINE_STATIC_SRCU() while avoiding the need to increase the size of the reserved region. Many of the algorithms and some of the code was cheerfully cherry-picked from other code making use of linker sections, perhaps most notably from tracepoints. All bugs are nevertheless the sole property of the author. Suggested-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> [ paulmck: Use __section() and use "default" in srcu_module_notify()'s "switch" statement as suggested by Joel Fernandes. ] Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com> Tested-by: Joel Fernandes (Google) <joel@joelfernandes.org>
This commit is contained in:
parent
a188339ca5
commit
fe15b50cde
|
@ -337,6 +337,10 @@
|
|||
KEEP(*(__tracepoints_ptrs)) /* Tracepoints: pointer array */ \
|
||||
__stop___tracepoints_ptrs = .; \
|
||||
*(__tracepoints_strings)/* Tracepoints: strings */ \
|
||||
. = ALIGN(8); \
|
||||
__start___srcu_struct = .; \
|
||||
*(___srcu_struct_ptrs) \
|
||||
__end___srcu_struct = .; \
|
||||
} \
|
||||
\
|
||||
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/error-injection.h>
|
||||
#include <linux/tracepoint-defs.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <linux/percpu.h>
|
||||
#include <asm/module.h>
|
||||
|
@ -450,6 +451,10 @@ struct module {
|
|||
unsigned int num_tracepoints;
|
||||
tracepoint_ptr_t *tracepoints_ptrs;
|
||||
#endif
|
||||
#ifdef CONFIG_TREE_SRCU
|
||||
unsigned int num_srcu_structs;
|
||||
struct srcu_struct **srcu_struct_ptrs;
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
unsigned int num_bpf_raw_events;
|
||||
struct bpf_raw_event_map *bpf_raw_events;
|
||||
|
|
|
@ -120,9 +120,17 @@ struct srcu_struct {
|
|||
*
|
||||
* See include/linux/percpu-defs.h for the rules on per-CPU variables.
|
||||
*/
|
||||
#define __DEFINE_SRCU(name, is_static) \
|
||||
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data);\
|
||||
is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_data)
|
||||
#ifdef MODULE
|
||||
# define __DEFINE_SRCU(name, is_static) \
|
||||
is_static struct srcu_struct name; \
|
||||
struct srcu_struct *__srcu_struct_##name \
|
||||
__section("___srcu_struct_ptrs") = &name
|
||||
#else
|
||||
# define __DEFINE_SRCU(name, is_static) \
|
||||
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data); \
|
||||
is_static struct srcu_struct name = \
|
||||
__SRCU_STRUCT_INIT(name, name##_srcu_data)
|
||||
#endif
|
||||
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
|
||||
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)
|
||||
|
||||
|
|
|
@ -3095,6 +3095,11 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
|||
sizeof(*mod->tracepoints_ptrs),
|
||||
&mod->num_tracepoints);
|
||||
#endif
|
||||
#ifdef CONFIG_TREE_SRCU
|
||||
mod->srcu_struct_ptrs = section_objs(info, "___srcu_struct_ptrs",
|
||||
sizeof(*mod->srcu_struct_ptrs),
|
||||
&mod->num_srcu_structs);
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
mod->bpf_raw_events = section_objs(info, "__bpf_raw_tp_map",
|
||||
sizeof(*mod->bpf_raw_events),
|
||||
|
|
|
@ -1310,3 +1310,68 @@ void __init srcu_init(void)
|
|||
queue_work(rcu_gp_wq, &ssp->work.work);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
/* Initialize any global-scope srcu_struct structures used by this module. */
|
||||
static int srcu_module_coming(struct module *mod)
|
||||
{
|
||||
int i;
|
||||
struct srcu_struct **sspp = mod->srcu_struct_ptrs;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < mod->num_srcu_structs; i++) {
|
||||
ret = init_srcu_struct(*(sspp++));
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up any global-scope srcu_struct structures used by this module. */
|
||||
static void srcu_module_going(struct module *mod)
|
||||
{
|
||||
int i;
|
||||
struct srcu_struct **sspp = mod->srcu_struct_ptrs;
|
||||
|
||||
for (i = 0; i < mod->num_srcu_structs; i++)
|
||||
cleanup_srcu_struct(*(sspp++));
|
||||
}
|
||||
|
||||
/* Handle one module, either coming or going. */
|
||||
static int srcu_module_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
int ret = 0;
|
||||
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
ret = srcu_module_coming(mod);
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
srcu_module_going(mod);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block srcu_module_nb = {
|
||||
.notifier_call = srcu_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static __init int init_srcu_module_notifier(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_module_notifier(&srcu_module_nb);
|
||||
if (ret)
|
||||
pr_warn("Failed to register srcu module notifier\n");
|
||||
return ret;
|
||||
}
|
||||
late_initcall(init_srcu_module_notifier);
|
||||
|
||||
#endif /* #ifdef CONFIG_MODULES */
|
||||
|
|
Loading…
Reference in New Issue