diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2017-08-01 11:24:04 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-08-10 06:28:56 -0400 |
commit | 1dbb6704de91b169a58d0c8221624afd6a95cfc7 (patch) | |
tree | 46273515a9b79413d2f00629b7e253189e903219 /kernel/jump_label.c | |
parent | 83ced169d9a01f22eb39f1fcc1f89ad9d223238f (diff) |
jump_label: Fix concurrent static_key_enable/disable()
static_key_enable/disable are trying to cap the static key count to
0/1. However, their use of key->enabled is outside jump_label_lock
so they do not really ensure that.
Rewrite them to do a quick check for an already enabled (respectively,
already disabled), and then recheck under the jump label lock. Unlike
static_key_slow_inc/dec, a failed check under the jump label lock does
not modify key->enabled.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Jason Baron <jbaron@akamai.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1501601046-35683-2-git-send-email-pbonzini@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/jump_label.c')
-rw-r--r-- | kernel/jump_label.c | 59 |
1 files changed, 37 insertions, 22 deletions
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index d11c506a6ac3..833eecae825e 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -79,28 +79,6 @@ int static_key_count(struct static_key *key) | |||
79 | } | 79 | } |
80 | EXPORT_SYMBOL_GPL(static_key_count); | 80 | EXPORT_SYMBOL_GPL(static_key_count); |
81 | 81 | ||
82 | void static_key_enable(struct static_key *key) | ||
83 | { | ||
84 | int count = static_key_count(key); | ||
85 | |||
86 | WARN_ON_ONCE(count < 0 || count > 1); | ||
87 | |||
88 | if (!count) | ||
89 | static_key_slow_inc(key); | ||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(static_key_enable); | ||
92 | |||
93 | void static_key_disable(struct static_key *key) | ||
94 | { | ||
95 | int count = static_key_count(key); | ||
96 | |||
97 | WARN_ON_ONCE(count < 0 || count > 1); | ||
98 | |||
99 | if (count) | ||
100 | static_key_slow_dec(key); | ||
101 | } | ||
102 | EXPORT_SYMBOL_GPL(static_key_disable); | ||
103 | |||
104 | void static_key_slow_inc(struct static_key *key) | 82 | void static_key_slow_inc(struct static_key *key) |
105 | { | 83 | { |
106 | int v, v1; | 84 | int v, v1; |
@@ -139,6 +117,43 @@ void static_key_slow_inc(struct static_key *key) | |||
139 | } | 117 | } |
140 | EXPORT_SYMBOL_GPL(static_key_slow_inc); | 118 | EXPORT_SYMBOL_GPL(static_key_slow_inc); |
141 | 119 | ||
120 | void static_key_enable(struct static_key *key) | ||
121 | { | ||
122 | STATIC_KEY_CHECK_USE(); | ||
123 | if (atomic_read(&key->enabled) > 0) { | ||
124 | WARN_ON_ONCE(atomic_read(&key->enabled) != 1); | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | cpus_read_lock(); | ||
129 | jump_label_lock(); | ||
130 | if (atomic_read(&key->enabled) == 0) { | ||
131 | atomic_set(&key->enabled, -1); | ||
132 | jump_label_update(key); | ||
133 | atomic_set(&key->enabled, 1); | ||
134 | } | ||
135 | jump_label_unlock(); | ||
136 | cpus_read_unlock(); | ||
137 | } | ||
138 | EXPORT_SYMBOL_GPL(static_key_enable); | ||
139 | |||
140 | void static_key_disable(struct static_key *key) | ||
141 | { | ||
142 | STATIC_KEY_CHECK_USE(); | ||
143 | if (atomic_read(&key->enabled) != 1) { | ||
144 | WARN_ON_ONCE(atomic_read(&key->enabled) != 0); | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | cpus_read_lock(); | ||
149 | jump_label_lock(); | ||
150 | if (atomic_cmpxchg(&key->enabled, 1, 0)) | ||
151 | jump_label_update(key); | ||
152 | jump_label_unlock(); | ||
153 | cpus_read_unlock(); | ||
154 | } | ||
155 | EXPORT_SYMBOL_GPL(static_key_disable); | ||
156 | |||
142 | static void __static_key_slow_dec(struct static_key *key, | 157 | static void __static_key_slow_dec(struct static_key *key, |
143 | unsigned long rate_limit, struct delayed_work *work) | 158 | unsigned long rate_limit, struct delayed_work *work) |
144 | { | 159 | { |