diff options
author | Oleg Nesterov <oleg@redhat.com> | 2015-08-21 13:42:50 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2015-10-06 14:25:16 -0400 |
commit | 3a518b76af7bb411efe6dd090fbf098e29accb2e (patch) | |
tree | 784edc06760ddaa84fbdfdcfe3ea52c9fb872fca | |
parent | 82e8c565be8a72957570d7da8dd9b441db7bb648 (diff) |
rcu_sync: Add CONFIG_PROVE_RCU checks
This commit validates that the caller of rcu_sync_is_idle() holds the
corresponding type of RCU read-side lock, but only in kernels built
with CONFIG_PROVE_RCU=y. This validation is carried out via a new
rcu_sync_ops->held() method that is checked within rcu_sync_is_idle().
Note that although this does add code to the fast path, it only does so
in kernels built with CONFIG_PROVE_RCU=y.
Suggested-by: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: Josh Triplett <josh@joshtriplett.org>
-rw-r--r-- | include/linux/rcu_sync.h | 6 | ||||
-rw-r--r-- | kernel/rcu/sync.c | 20 |
2 files changed, 26 insertions, 0 deletions
diff --git a/include/linux/rcu_sync.h b/include/linux/rcu_sync.h index c6d2272c4459..1f2d4fc30b04 100644 --- a/include/linux/rcu_sync.h +++ b/include/linux/rcu_sync.h | |||
@@ -40,6 +40,8 @@ struct rcu_sync { | |||
40 | enum rcu_sync_type gp_type; | 40 | enum rcu_sync_type gp_type; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | extern bool __rcu_sync_is_idle(struct rcu_sync *); | ||
44 | |||
43 | /** | 45 | /** |
44 | * rcu_sync_is_idle() - Are readers permitted to use their fastpaths? | 46 | * rcu_sync_is_idle() - Are readers permitted to use their fastpaths? |
45 | * @rsp: Pointer to rcu_sync structure to use for synchronization | 47 | * @rsp: Pointer to rcu_sync structure to use for synchronization |
@@ -50,7 +52,11 @@ struct rcu_sync { | |||
50 | */ | 52 | */ |
51 | static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) | 53 | static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) |
52 | { | 54 | { |
55 | #ifdef CONFIG_PROVE_RCU | ||
56 | return __rcu_sync_is_idle(rsp); | ||
57 | #else | ||
53 | return !rsp->gp_state; /* GP_IDLE */ | 58 | return !rsp->gp_state; /* GP_IDLE */ |
59 | #endif | ||
54 | } | 60 | } |
55 | 61 | ||
56 | extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type); | 62 | extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type); |
diff --git a/kernel/rcu/sync.c b/kernel/rcu/sync.c index 5a9aa4c394f1..01c9807a7f73 100644 --- a/kernel/rcu/sync.c +++ b/kernel/rcu/sync.c | |||
@@ -23,21 +23,33 @@ | |||
23 | #include <linux/rcu_sync.h> | 23 | #include <linux/rcu_sync.h> |
24 | #include <linux/sched.h> | 24 | #include <linux/sched.h> |
25 | 25 | ||
26 | #ifdef CONFIG_PROVE_RCU | ||
27 | #define __INIT_HELD(func) .held = func, | ||
28 | #else | ||
29 | #define __INIT_HELD(func) | ||
30 | #endif | ||
31 | |||
26 | static const struct { | 32 | static const struct { |
27 | void (*sync)(void); | 33 | void (*sync)(void); |
28 | void (*call)(struct rcu_head *, void (*)(struct rcu_head *)); | 34 | void (*call)(struct rcu_head *, void (*)(struct rcu_head *)); |
35 | #ifdef CONFIG_PROVE_RCU | ||
36 | int (*held)(void); | ||
37 | #endif | ||
29 | } gp_ops[] = { | 38 | } gp_ops[] = { |
30 | [RCU_SYNC] = { | 39 | [RCU_SYNC] = { |
31 | .sync = synchronize_rcu, | 40 | .sync = synchronize_rcu, |
32 | .call = call_rcu, | 41 | .call = call_rcu, |
42 | __INIT_HELD(rcu_read_lock_held) | ||
33 | }, | 43 | }, |
34 | [RCU_SCHED_SYNC] = { | 44 | [RCU_SCHED_SYNC] = { |
35 | .sync = synchronize_sched, | 45 | .sync = synchronize_sched, |
36 | .call = call_rcu_sched, | 46 | .call = call_rcu_sched, |
47 | __INIT_HELD(rcu_read_lock_sched_held) | ||
37 | }, | 48 | }, |
38 | [RCU_BH_SYNC] = { | 49 | [RCU_BH_SYNC] = { |
39 | .sync = synchronize_rcu_bh, | 50 | .sync = synchronize_rcu_bh, |
40 | .call = call_rcu_bh, | 51 | .call = call_rcu_bh, |
52 | __INIT_HELD(rcu_read_lock_bh_held) | ||
41 | }, | 53 | }, |
42 | }; | 54 | }; |
43 | 55 | ||
@@ -46,6 +58,14 @@ enum { CB_IDLE = 0, CB_PENDING, CB_REPLAY }; | |||
46 | 58 | ||
47 | #define rss_lock gp_wait.lock | 59 | #define rss_lock gp_wait.lock |
48 | 60 | ||
61 | #ifdef CONFIG_PROVE_RCU | ||
62 | bool __rcu_sync_is_idle(struct rcu_sync *rsp) | ||
63 | { | ||
64 | WARN_ON(!gp_ops[rsp->gp_type].held()); | ||
65 | return rsp->gp_state == GP_IDLE; | ||
66 | } | ||
67 | #endif | ||
68 | |||
49 | /** | 69 | /** |
50 | * rcu_sync_init() - Initialize an rcu_sync structure | 70 | * rcu_sync_init() - Initialize an rcu_sync structure |
51 | * @rsp: Pointer to rcu_sync structure to be initialized | 71 | * @rsp: Pointer to rcu_sync structure to be initialized |