diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2017-08-01 04:02:56 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-08-10 06:28:59 -0400 |
commit | 5a40527f8f0798553764fc8db4111d7d9c33ea51 (patch) | |
tree | 8147a2345202a3e7d5b1d3b14ca1c804d660f300 | |
parent | 8b7b412807053ab5f059ffae426a280e769a5bda (diff) |
jump_label: Provide hotplug context variants
As using the normal static key API under the hotplug lock is
pretty much impossible, let's provide a variant of some of them
that require the hotplug lock to have already been taken.
These function are only meant to be used in CPU hotplug callbacks.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lkml.kernel.org/r/20170801080257.5056-4-marc.zyngier@arm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | Documentation/static-keys.txt | 15 | ||||
-rw-r--r-- | include/linux/jump_label.h | 11 | ||||
-rw-r--r-- | kernel/jump_label.c | 22 |
3 files changed, 42 insertions, 6 deletions
diff --git a/Documentation/static-keys.txt b/Documentation/static-keys.txt index 870b4be3cb11..ab16efe0c79d 100644 --- a/Documentation/static-keys.txt +++ b/Documentation/static-keys.txt | |||
@@ -154,6 +154,21 @@ and 'static_key_count()'. In general, if you use these functions, they | |||
154 | should be protected with the same mutex used around the enable/disable | 154 | should be protected with the same mutex used around the enable/disable |
155 | or increment/decrement function. | 155 | or increment/decrement function. |
156 | 156 | ||
157 | Note that switching branches results in some locks being taken, | ||
158 | particularly the CPU hotplug lock (in order to avoid races against | ||
159 | CPUs being brought in the kernel whilst the kernel is getting | ||
160 | patched). Calling the static key API from within a hotplug notifier is | ||
161 | thus a sure deadlock recipe. In order to still allow use of the | ||
162 | functionnality, the following functions are provided: | ||
163 | |||
164 | static_key_enable_cpuslocked() | ||
165 | static_key_disable_cpuslocked() | ||
166 | static_branch_enable_cpuslocked() | ||
167 | static_branch_disable_cpuslocked() | ||
168 | |||
169 | These functions are *not* general purpose, and must only be used when | ||
170 | you really know that you're in the above context, and no other. | ||
171 | |||
157 | Where an array of keys is required, it can be defined as:: | 172 | Where an array of keys is required, it can be defined as:: |
158 | 173 | ||
159 | DEFINE_STATIC_KEY_ARRAY_TRUE(keys, count); | 174 | DEFINE_STATIC_KEY_ARRAY_TRUE(keys, count); |
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 740a42ea7f7f..cd5861651b17 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -163,6 +163,8 @@ extern void jump_label_apply_nops(struct module *mod); | |||
163 | extern int static_key_count(struct static_key *key); | 163 | extern int static_key_count(struct static_key *key); |
164 | extern void static_key_enable(struct static_key *key); | 164 | extern void static_key_enable(struct static_key *key); |
165 | extern void static_key_disable(struct static_key *key); | 165 | extern void static_key_disable(struct static_key *key); |
166 | extern void static_key_enable_cpuslocked(struct static_key *key); | ||
167 | extern void static_key_disable_cpuslocked(struct static_key *key); | ||
166 | 168 | ||
167 | /* | 169 | /* |
168 | * We should be using ATOMIC_INIT() for initializing .enabled, but | 170 | * We should be using ATOMIC_INIT() for initializing .enabled, but |
@@ -254,6 +256,9 @@ static inline void static_key_disable(struct static_key *key) | |||
254 | atomic_set(&key->enabled, 0); | 256 | atomic_set(&key->enabled, 0); |
255 | } | 257 | } |
256 | 258 | ||
259 | #define static_key_enable_cpuslocked(k) static_key_enable((k)) | ||
260 | #define static_key_disable_cpuslocked(k) static_key_disable((k)) | ||
261 | |||
257 | #define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) } | 262 | #define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) } |
258 | #define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) } | 263 | #define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) } |
259 | 264 | ||
@@ -415,8 +420,10 @@ extern bool ____wrong_branch_error(void); | |||
415 | * Normal usage; boolean enable/disable. | 420 | * Normal usage; boolean enable/disable. |
416 | */ | 421 | */ |
417 | 422 | ||
418 | #define static_branch_enable(x) static_key_enable(&(x)->key) | 423 | #define static_branch_enable(x) static_key_enable(&(x)->key) |
419 | #define static_branch_disable(x) static_key_disable(&(x)->key) | 424 | #define static_branch_disable(x) static_key_disable(&(x)->key) |
425 | #define static_branch_enable_cpuslocked(x) static_key_enable_cpuslocked(&(x)->key) | ||
426 | #define static_branch_disable_cpuslocked(x) static_key_disable_cpuslocked(&(x)->key) | ||
420 | 427 | ||
421 | #endif /* __ASSEMBLY__ */ | 428 | #endif /* __ASSEMBLY__ */ |
422 | 429 | ||
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index cc6d815c75ed..0bf2e8f5244a 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -126,15 +126,15 @@ void static_key_slow_inc(struct static_key *key) | |||
126 | } | 126 | } |
127 | EXPORT_SYMBOL_GPL(static_key_slow_inc); | 127 | EXPORT_SYMBOL_GPL(static_key_slow_inc); |
128 | 128 | ||
129 | void static_key_enable(struct static_key *key) | 129 | void static_key_enable_cpuslocked(struct static_key *key) |
130 | { | 130 | { |
131 | STATIC_KEY_CHECK_USE(); | 131 | STATIC_KEY_CHECK_USE(); |
132 | |||
132 | if (atomic_read(&key->enabled) > 0) { | 133 | if (atomic_read(&key->enabled) > 0) { |
133 | WARN_ON_ONCE(atomic_read(&key->enabled) != 1); | 134 | WARN_ON_ONCE(atomic_read(&key->enabled) != 1); |
134 | return; | 135 | return; |
135 | } | 136 | } |
136 | 137 | ||
137 | cpus_read_lock(); | ||
138 | jump_label_lock(); | 138 | jump_label_lock(); |
139 | if (atomic_read(&key->enabled) == 0) { | 139 | if (atomic_read(&key->enabled) == 0) { |
140 | atomic_set(&key->enabled, -1); | 140 | atomic_set(&key->enabled, -1); |
@@ -145,23 +145,37 @@ void static_key_enable(struct static_key *key) | |||
145 | atomic_set_release(&key->enabled, 1); | 145 | atomic_set_release(&key->enabled, 1); |
146 | } | 146 | } |
147 | jump_label_unlock(); | 147 | jump_label_unlock(); |
148 | } | ||
149 | EXPORT_SYMBOL_GPL(static_key_enable_cpuslocked); | ||
150 | |||
151 | void static_key_enable(struct static_key *key) | ||
152 | { | ||
153 | cpus_read_lock(); | ||
154 | static_key_enable_cpuslocked(key); | ||
148 | cpus_read_unlock(); | 155 | cpus_read_unlock(); |
149 | } | 156 | } |
150 | EXPORT_SYMBOL_GPL(static_key_enable); | 157 | EXPORT_SYMBOL_GPL(static_key_enable); |
151 | 158 | ||
152 | void static_key_disable(struct static_key *key) | 159 | void static_key_disable_cpuslocked(struct static_key *key) |
153 | { | 160 | { |
154 | STATIC_KEY_CHECK_USE(); | 161 | STATIC_KEY_CHECK_USE(); |
162 | |||
155 | if (atomic_read(&key->enabled) != 1) { | 163 | if (atomic_read(&key->enabled) != 1) { |
156 | WARN_ON_ONCE(atomic_read(&key->enabled) != 0); | 164 | WARN_ON_ONCE(atomic_read(&key->enabled) != 0); |
157 | return; | 165 | return; |
158 | } | 166 | } |
159 | 167 | ||
160 | cpus_read_lock(); | ||
161 | jump_label_lock(); | 168 | jump_label_lock(); |
162 | if (atomic_cmpxchg(&key->enabled, 1, 0)) | 169 | if (atomic_cmpxchg(&key->enabled, 1, 0)) |
163 | jump_label_update(key); | 170 | jump_label_update(key); |
164 | jump_label_unlock(); | 171 | jump_label_unlock(); |
172 | } | ||
173 | EXPORT_SYMBOL_GPL(static_key_disable_cpuslocked); | ||
174 | |||
175 | void static_key_disable(struct static_key *key) | ||
176 | { | ||
177 | cpus_read_lock(); | ||
178 | static_key_disable_cpuslocked(key); | ||
165 | cpus_read_unlock(); | 179 | cpus_read_unlock(); |
166 | } | 180 | } |
167 | EXPORT_SYMBOL_GPL(static_key_disable); | 181 | EXPORT_SYMBOL_GPL(static_key_disable); |