rcu-tasks: Add an RCU-tasks rude variant
This commit adds a "rude" variant of RCU-tasks that has as quiescent states schedule(), cond_resched_tasks_rcu_qs(), userspace execution, and (in theory, anyway) cond_resched(). In other words, RCU-tasks rude readers are regions of code with preemption disabled, but excluding code early in the CPU-online sequence and late in the CPU-offline sequence. Updates make use of IPIs and force an IPI and a context switch on each online CPU. This variant is useful in some situations in tracing. Suggested-by: Steven Rostedt <rostedt@goodmis.org> [ paulmck: Apply EXPORT_SYMBOL_GPL() feedback from Qiujun Huang. ] Signed-off-by: Paul E. McKenney <paulmck@kernel.org> [ paulmck: Apply review feedback from Steve Rostedt. ]
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
/* Exported common interfaces */
|
/* Exported common interfaces */
|
||||||
void call_rcu(struct rcu_head *head, rcu_callback_t func);
|
void call_rcu(struct rcu_head *head, rcu_callback_t func);
|
||||||
void rcu_barrier_tasks(void);
|
void rcu_barrier_tasks(void);
|
||||||
|
void rcu_barrier_tasks_rude(void);
|
||||||
void synchronize_rcu(void);
|
void synchronize_rcu(void);
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT_RCU
|
#ifdef CONFIG_PREEMPT_RCU
|
||||||
@@ -138,6 +139,8 @@ static inline void rcu_init_nohz(void) { }
|
|||||||
#define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t)
|
#define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t)
|
||||||
void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func);
|
void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func);
|
||||||
void synchronize_rcu_tasks(void);
|
void synchronize_rcu_tasks(void);
|
||||||
|
void call_rcu_tasks_rude(struct rcu_head *head, rcu_callback_t func);
|
||||||
|
void synchronize_rcu_tasks_rude(void);
|
||||||
void exit_tasks_rcu_start(void);
|
void exit_tasks_rcu_start(void);
|
||||||
void exit_tasks_rcu_finish(void);
|
void exit_tasks_rcu_finish(void);
|
||||||
#else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
|
#else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
|
||||||
|
@@ -71,7 +71,7 @@ config TREE_SRCU
|
|||||||
This option selects the full-fledged version of SRCU.
|
This option selects the full-fledged version of SRCU.
|
||||||
|
|
||||||
config TASKS_RCU_GENERIC
|
config TASKS_RCU_GENERIC
|
||||||
def_bool TASKS_RCU
|
def_bool TASKS_RCU || TASKS_RUDE_RCU
|
||||||
select SRCU
|
select SRCU
|
||||||
help
|
help
|
||||||
This option enables generic infrastructure code supporting
|
This option enables generic infrastructure code supporting
|
||||||
@@ -84,6 +84,15 @@ config TASKS_RCU
|
|||||||
only voluntary context switch (not preemption!), idle, and
|
only voluntary context switch (not preemption!), idle, and
|
||||||
user-mode execution as quiescent states. Not for manual selection.
|
user-mode execution as quiescent states. Not for manual selection.
|
||||||
|
|
||||||
|
config TASKS_RUDE_RCU
|
||||||
|
def_bool 0
|
||||||
|
help
|
||||||
|
This option enables a task-based RCU implementation that uses
|
||||||
|
only context switch (including preemption) and user-mode
|
||||||
|
execution as quiescent states. It forces IPIs and context
|
||||||
|
switches on all online CPUs, including idle ones, so use
|
||||||
|
with caution.
|
||||||
|
|
||||||
config RCU_STALL_COMMON
|
config RCU_STALL_COMMON
|
||||||
def_bool TREE_RCU
|
def_bool TREE_RCU
|
||||||
help
|
help
|
||||||
|
@@ -180,6 +180,9 @@ static void __init rcu_tasks_bootup_oddness(void)
|
|||||||
else
|
else
|
||||||
pr_info("\tTasks RCU enabled.\n");
|
pr_info("\tTasks RCU enabled.\n");
|
||||||
#endif /* #ifdef CONFIG_TASKS_RCU */
|
#endif /* #ifdef CONFIG_TASKS_RCU */
|
||||||
|
#ifdef CONFIG_TASKS_RUDE_RCU
|
||||||
|
pr_info("\tRude variant of Tasks RCU enabled.\n");
|
||||||
|
#endif /* #ifdef CONFIG_TASKS_RUDE_RCU */
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* #ifndef CONFIG_TINY_RCU */
|
#endif /* #ifndef CONFIG_TINY_RCU */
|
||||||
@@ -410,3 +413,98 @@ static int __init rcu_spawn_tasks_kthread(void)
|
|||||||
core_initcall(rcu_spawn_tasks_kthread);
|
core_initcall(rcu_spawn_tasks_kthread);
|
||||||
|
|
||||||
#endif /* #ifdef CONFIG_TASKS_RCU */
|
#endif /* #ifdef CONFIG_TASKS_RCU */
|
||||||
|
|
||||||
|
#ifdef CONFIG_TASKS_RUDE_RCU
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// "Rude" variant of Tasks RCU, inspired by Steve Rostedt's trick of
|
||||||
|
// passing an empty function to schedule_on_each_cpu(). This approach
|
||||||
|
// provides an asynchronous call_rcu_tasks_rude() API and batching
|
||||||
|
// of concurrent calls to the synchronous synchronize_rcu_rude() API.
|
||||||
|
// This sends IPIs far and wide and induces otherwise unnecessary context
|
||||||
|
// switches on all online CPUs, whether idle or not.
|
||||||
|
|
||||||
|
// Empty function to allow workqueues to force a context switch.
|
||||||
|
static void rcu_tasks_be_rude(struct work_struct *work)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for one rude RCU-tasks grace period.
|
||||||
|
static void rcu_tasks_rude_wait_gp(struct rcu_tasks *rtp)
|
||||||
|
{
|
||||||
|
schedule_on_each_cpu(rcu_tasks_be_rude);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_rcu_tasks_rude(struct rcu_head *rhp, rcu_callback_t func);
|
||||||
|
DEFINE_RCU_TASKS(rcu_tasks_rude, rcu_tasks_rude_wait_gp, call_rcu_tasks_rude);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* call_rcu_tasks_rude() - Queue a callback rude task-based grace period
|
||||||
|
* @rhp: structure to be used for queueing the RCU updates.
|
||||||
|
* @func: actual callback function to be invoked after the grace period
|
||||||
|
*
|
||||||
|
* The callback function will be invoked some time after a full grace
|
||||||
|
* period elapses, in other words after all currently executing RCU
|
||||||
|
* read-side critical sections have completed. call_rcu_tasks_rude()
|
||||||
|
* assumes that the read-side critical sections end at context switch,
|
||||||
|
* cond_resched_rcu_qs(), or transition to usermode execution. As such,
|
||||||
|
* there are no read-side primitives analogous to rcu_read_lock() and
|
||||||
|
* rcu_read_unlock() because this primitive is intended to determine
|
||||||
|
* that all tasks have passed through a safe state, not so much for
|
||||||
|
* data-strcuture synchronization.
|
||||||
|
*
|
||||||
|
* See the description of call_rcu() for more detailed information on
|
||||||
|
* memory ordering guarantees.
|
||||||
|
*/
|
||||||
|
void call_rcu_tasks_rude(struct rcu_head *rhp, rcu_callback_t func)
|
||||||
|
{
|
||||||
|
call_rcu_tasks_generic(rhp, func, &rcu_tasks_rude);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(call_rcu_tasks_rude);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synchronize_rcu_tasks_rude - wait for a rude rcu-tasks grace period
|
||||||
|
*
|
||||||
|
* Control will return to the caller some time after a rude rcu-tasks
|
||||||
|
* grace period has elapsed, in other words after all currently
|
||||||
|
* executing rcu-tasks read-side critical sections have elapsed. These
|
||||||
|
* read-side critical sections are delimited by calls to schedule(),
|
||||||
|
* cond_resched_tasks_rcu_qs(), userspace execution, and (in theory,
|
||||||
|
* anyway) cond_resched().
|
||||||
|
*
|
||||||
|
* This is a very specialized primitive, intended only for a few uses in
|
||||||
|
* tracing and other situations requiring manipulation of function preambles
|
||||||
|
* and profiling hooks. The synchronize_rcu_tasks_rude() function is not
|
||||||
|
* (yet) intended for heavy use from multiple CPUs.
|
||||||
|
*
|
||||||
|
* See the description of synchronize_rcu() for more detailed information
|
||||||
|
* on memory ordering guarantees.
|
||||||
|
*/
|
||||||
|
void synchronize_rcu_tasks_rude(void)
|
||||||
|
{
|
||||||
|
synchronize_rcu_tasks_generic(&rcu_tasks_rude);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synchronize_rcu_tasks_rude);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rcu_barrier_tasks_rude - Wait for in-flight call_rcu_tasks_rude() callbacks.
|
||||||
|
*
|
||||||
|
* Although the current implementation is guaranteed to wait, it is not
|
||||||
|
* obligated to, for example, if there are no pending callbacks.
|
||||||
|
*/
|
||||||
|
void rcu_barrier_tasks_rude(void)
|
||||||
|
{
|
||||||
|
/* There is only one callback queue, so this is easy. ;-) */
|
||||||
|
synchronize_rcu_tasks_rude();
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rcu_barrier_tasks_rude);
|
||||||
|
|
||||||
|
static int __init rcu_spawn_tasks_rude_kthread(void)
|
||||||
|
{
|
||||||
|
rcu_spawn_tasks_kthread_generic(&rcu_tasks_rude);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
core_initcall(rcu_spawn_tasks_rude_kthread);
|
||||||
|
|
||||||
|
#endif /* #ifdef CONFIG_TASKS_RUDE_RCU */
|
||||||
|
Reference in New Issue
Block a user