rcu: Create a synchronize_rcu_mult()
There have been several requests for a primitive that waits for grace periods for several RCU flavors concurrently, so this commit creates it. This is a variadic macro, and you pass in the call_rcu() functions of the flavors of RCU that you wish to wait for. Note that you cannot pass in call_srcu() for two reasons: (1) This would result in a type mismatch and (2) You need to specify which srcu_struct you want to use. Handle this by creating a wrapper function for your SRCU domain, for example: void call_srcu_mine(struct rcu_head *head, rcu_callback_t func) { call_srcu(&ss_mine, head, func); } You can then do something like this: synchronize_rcu_mult(call_srcu_mine, call_rcu, call_rcu_sched); Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
This commit is contained in:
Родитель
bc17ea1092
Коммит
ec90a194ae
|
@ -226,6 +226,37 @@ struct rcu_synchronize {
|
||||||
};
|
};
|
||||||
void wakeme_after_rcu(struct rcu_head *head);
|
void wakeme_after_rcu(struct rcu_head *head);
|
||||||
|
|
||||||
|
void __wait_rcu_gp(bool checktiny, int n, call_rcu_func_t *crcu_array,
|
||||||
|
struct rcu_synchronize *rs_array);
|
||||||
|
|
||||||
|
#define _wait_rcu_gp(checktiny, ...) \
|
||||||
|
do { \
|
||||||
|
call_rcu_func_t __crcu_array[] = { __VA_ARGS__ }; \
|
||||||
|
const int __n = ARRAY_SIZE(__crcu_array); \
|
||||||
|
struct rcu_synchronize __rs_array[__n]; \
|
||||||
|
\
|
||||||
|
__wait_rcu_gp(checktiny, __n, __crcu_array, __rs_array); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define wait_rcu_gp(...) _wait_rcu_gp(false, __VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synchronize_rcu_mult - Wait concurrently for multiple grace periods
|
||||||
|
* @...: List of call_rcu() functions for the flavors to wait on.
|
||||||
|
*
|
||||||
|
* This macro waits concurrently for multiple flavors of RCU grace periods.
|
||||||
|
* For example, synchronize_rcu_mult(call_rcu, call_rcu_bh) would wait
|
||||||
|
* on concurrent RCU and RCU-bh grace periods. Waiting on a give SRCU
|
||||||
|
* domain requires you to write a wrapper function for that SRCU domain's
|
||||||
|
* call_srcu() function, supplying the corresponding srcu_struct.
|
||||||
|
*
|
||||||
|
* If Tiny RCU, tell _wait_rcu_gp() not to bother waiting for RCU
|
||||||
|
* or RCU-bh, given that anywhere synchronize_rcu_mult() can be called
|
||||||
|
* is automatically a grace period.
|
||||||
|
*/
|
||||||
|
#define synchronize_rcu_mult(...) \
|
||||||
|
_wait_rcu_gp(IS_ENABLED(CONFIG_TINY_RCU), __VA_ARGS__)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* call_rcu_tasks() - Queue an RCU for invocation task-based grace period
|
* call_rcu_tasks() - Queue an RCU for invocation task-based grace period
|
||||||
* @head: structure to be used for queueing the RCU updates.
|
* @head: structure to be used for queueing the RCU updates.
|
||||||
|
@ -392,10 +423,6 @@ bool __rcu_is_watching(void);
|
||||||
* TREE_RCU and rcu_barrier_() primitives in TINY_RCU.
|
* TREE_RCU and rcu_barrier_() primitives in TINY_RCU.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef void call_rcu_func_t(struct rcu_head *head,
|
|
||||||
void (*func)(struct rcu_head *head));
|
|
||||||
void wait_rcu_gp(call_rcu_func_t crf);
|
|
||||||
|
|
||||||
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU)
|
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU)
|
||||||
#include <linux/rcutree.h>
|
#include <linux/rcutree.h>
|
||||||
#elif defined(CONFIG_TINY_RCU)
|
#elif defined(CONFIG_TINY_RCU)
|
||||||
|
|
|
@ -212,6 +212,9 @@ struct callback_head {
|
||||||
};
|
};
|
||||||
#define rcu_head callback_head
|
#define rcu_head callback_head
|
||||||
|
|
||||||
|
typedef void (*rcu_callback_t)(struct rcu_head *head);
|
||||||
|
typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func);
|
||||||
|
|
||||||
/* clocksource cycle base type */
|
/* clocksource cycle base type */
|
||||||
typedef u64 cycle_t;
|
typedef u64 cycle_t;
|
||||||
|
|
||||||
|
|
|
@ -318,20 +318,37 @@ void wakeme_after_rcu(struct rcu_head *head)
|
||||||
rcu = container_of(head, struct rcu_synchronize, head);
|
rcu = container_of(head, struct rcu_synchronize, head);
|
||||||
complete(&rcu->completion);
|
complete(&rcu->completion);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(wakeme_after_rcu);
|
||||||
|
|
||||||
void wait_rcu_gp(call_rcu_func_t crf)
|
void __wait_rcu_gp(bool checktiny, int n, call_rcu_func_t *crcu_array,
|
||||||
|
struct rcu_synchronize *rs_array)
|
||||||
{
|
{
|
||||||
struct rcu_synchronize rcu;
|
int i;
|
||||||
|
|
||||||
init_rcu_head_on_stack(&rcu.head);
|
/* Initialize and register callbacks for each flavor specified. */
|
||||||
init_completion(&rcu.completion);
|
for (i = 0; i < n; i++) {
|
||||||
/* Will wake me after RCU finished. */
|
if (checktiny &&
|
||||||
crf(&rcu.head, wakeme_after_rcu);
|
(crcu_array[i] == call_rcu ||
|
||||||
/* Wait for it. */
|
crcu_array[i] == call_rcu_bh)) {
|
||||||
wait_for_completion(&rcu.completion);
|
might_sleep();
|
||||||
destroy_rcu_head_on_stack(&rcu.head);
|
continue;
|
||||||
|
}
|
||||||
|
init_rcu_head_on_stack(&rs_array[i].head);
|
||||||
|
init_completion(&rs_array[i].completion);
|
||||||
|
(crcu_array[i])(&rs_array[i].head, wakeme_after_rcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for all callbacks to be invoked. */
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (checktiny &&
|
||||||
|
(crcu_array[i] == call_rcu ||
|
||||||
|
crcu_array[i] == call_rcu_bh))
|
||||||
|
continue;
|
||||||
|
wait_for_completion(&rs_array[i].completion);
|
||||||
|
destroy_rcu_head_on_stack(&rs_array[i].head);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wait_rcu_gp);
|
EXPORT_SYMBOL_GPL(__wait_rcu_gp);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD
|
#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD
|
||||||
void init_rcu_head(struct rcu_head *head)
|
void init_rcu_head(struct rcu_head *head)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче