Skip to content

Commit

Permalink
jump_label: Fix concurrent static_key_enable/disable()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Jason Baron <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
bonzini authored and Ingo Molnar committed Aug 10, 2017
1 parent 83ced16 commit 1dbb670
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 32 deletions.
22 changes: 12 additions & 10 deletions include/linux/jump_label.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,22 +234,24 @@ static inline int jump_label_apply_nops(struct module *mod)

static inline void static_key_enable(struct static_key *key)
{
int count = static_key_count(key);

WARN_ON_ONCE(count < 0 || count > 1);
STATIC_KEY_CHECK_USE();

if (!count)
static_key_slow_inc(key);
if (atomic_read(&key->enabled) != 0) {
WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
return;
}
atomic_set(&key->enabled, 1);
}

static inline void static_key_disable(struct static_key *key)
{
int count = static_key_count(key);

WARN_ON_ONCE(count < 0 || count > 1);
STATIC_KEY_CHECK_USE();

if (count)
static_key_slow_dec(key);
if (atomic_read(&key->enabled) != 1) {
WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
return;
}
atomic_set(&key->enabled, 0);
}

#define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) }
Expand Down
59 changes: 37 additions & 22 deletions kernel/jump_label.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,6 @@ int static_key_count(struct static_key *key)
}
EXPORT_SYMBOL_GPL(static_key_count);

void static_key_enable(struct static_key *key)
{
int count = static_key_count(key);

WARN_ON_ONCE(count < 0 || count > 1);

if (!count)
static_key_slow_inc(key);
}
EXPORT_SYMBOL_GPL(static_key_enable);

void static_key_disable(struct static_key *key)
{
int count = static_key_count(key);

WARN_ON_ONCE(count < 0 || count > 1);

if (count)
static_key_slow_dec(key);
}
EXPORT_SYMBOL_GPL(static_key_disable);

void static_key_slow_inc(struct static_key *key)
{
int v, v1;
Expand Down Expand Up @@ -139,6 +117,43 @@ void static_key_slow_inc(struct static_key *key)
}
EXPORT_SYMBOL_GPL(static_key_slow_inc);

void static_key_enable(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
if (atomic_read(&key->enabled) > 0) {
WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
return;
}

cpus_read_lock();
jump_label_lock();
if (atomic_read(&key->enabled) == 0) {
atomic_set(&key->enabled, -1);
jump_label_update(key);
atomic_set(&key->enabled, 1);
}
jump_label_unlock();
cpus_read_unlock();
}
EXPORT_SYMBOL_GPL(static_key_enable);

void static_key_disable(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
if (atomic_read(&key->enabled) != 1) {
WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
return;
}

cpus_read_lock();
jump_label_lock();
if (atomic_cmpxchg(&key->enabled, 1, 0))
jump_label_update(key);
jump_label_unlock();
cpus_read_unlock();
}
EXPORT_SYMBOL_GPL(static_key_disable);

static void __static_key_slow_dec(struct static_key *key,
unsigned long rate_limit, struct delayed_work *work)
{
Expand Down

0 comments on commit 1dbb670

Please sign in to comment.