From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752541AbdHBOiA (ORCPT ); Wed, 2 Aug 2017 10:38:00 -0400 Received: from merlin.infradead.org ([205.233.59.134]:48978 "EHLO merlin.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752255AbdHBOh7 (ORCPT ); Wed, 2 Aug 2017 10:37:59 -0400 Date: Wed, 2 Aug 2017 16:37:48 +0200 From: Peter Zijlstra To: Marc Zyngier Cc: Thomas Gleixner , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Leo Yan , Paolo Bonzini Subject: Re: [PATCH v2 3/4] jump_label: Provide hotplug context variants Message-ID: <20170802143748.dmthd3hvbq5pbelt@hirez.programming.kicks-ass.net> References: <20170801080257.5056-1-marc.zyngier@arm.com> <20170801080257.5056-4-marc.zyngier@arm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20170801080257.5056-4-marc.zyngier@arm.com> User-Agent: NeoMutt/20170609 (1.8.3) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, Aug 01, 2017 at 09:02:56AM +0100, Marc Zyngier wrote: > 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 > --- > Documentation/static-keys.txt | 15 +++++++++++++++ > include/linux/jump_label.h | 11 +++++++++-- > kernel/jump_label.c | 20 ++++++++++++++++++++ > 3 files changed, 44 insertions(+), 2 deletions(-) I stuffed them in my locking/core branch on top of Paolo's patches: https://lkml.kernel.org/r/1501601046-35683-1-git-send-email-pbonzini@redhat.com This patch now looks like so. --- Subject: jump_label: Provide hotplug context variants From: Marc Zyngier Date: Tue, 1 Aug 2017 09:02:56 +0100 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. Cc: linux-arm-kernel@lists.infradead.org Cc: Leo Yan Cc: Thomas Gleixner Signed-off-by: Marc Zyngier Signed-off-by: Peter Zijlstra (Intel) Link: http://lkml.kernel.org/r/20170801080257.5056-4-marc.zyngier@arm.com --- Documentation/static-keys.txt | 15 +++++++++++++++ include/linux/jump_label.h | 11 +++++++++-- kernel/jump_label.c | 22 ++++++++++++++++++---- 3 files changed, 42 insertions(+), 6 deletions(-) --- a/Documentation/static-keys.txt +++ b/Documentation/static-keys.txt @@ -154,6 +154,21 @@ and 'static_key_count()'. In general, i should be protected with the same mutex used around the enable/disable or increment/decrement function. +Note that switching branches results in some locks being taken, +particularly the CPU hotplug lock (in order to avoid races against +CPUs being brought in the kernel whilst the kernel is getting +patched). Calling the static key API from within a hotplug notifier is +thus a sure deadlock recipe. In order to still allow use of the +functionnality, the following functions are provided: + + static_key_enable_cpuslocked() + static_key_disable_cpuslocked() + static_branch_enable_cpuslocked() + static_branch_disable_cpuslocked() + +These functions are *not* general purpose, and must only be used when +you really know that you're in the above context, and no other. + Where an array of keys is required, it can be defined as:: DEFINE_STATIC_KEY_ARRAY_TRUE(keys, count); --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -163,6 +163,8 @@ extern void jump_label_apply_nops(struct extern int static_key_count(struct static_key *key); extern void static_key_enable(struct static_key *key); extern void static_key_disable(struct static_key *key); +extern void static_key_enable_cpuslocked(struct static_key *key); +extern void static_key_disable_cpuslocked(struct static_key *key); /* * We should be using ATOMIC_INIT() for initializing .enabled, but @@ -254,6 +256,9 @@ static inline void static_key_disable(st atomic_set(&key->enabled, 0); } +#define static_key_enable_cpuslocked(k) static_key_enable((k)) +#define static_key_disable_cpuslocked(k) static_key_disable((k)) + #define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) } #define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) } @@ -415,8 +420,10 @@ extern bool ____wrong_branch_error(void) * Normal usage; boolean enable/disable. */ -#define static_branch_enable(x) static_key_enable(&(x)->key) -#define static_branch_disable(x) static_key_disable(&(x)->key) +#define static_branch_enable(x) static_key_enable(&(x)->key) +#define static_branch_disable(x) static_key_disable(&(x)->key) +#define static_branch_enable_cpuslocked(x) static_key_enable_cpuslocked(&(x)->key) +#define static_branch_disable_cpuslocked(x) static_key_disable_cpuslocked(&(x)->key) #endif /* __ASSEMBLY__ */ --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -126,15 +126,15 @@ void static_key_slow_inc(struct static_k } EXPORT_SYMBOL_GPL(static_key_slow_inc); -void static_key_enable(struct static_key *key) +void static_key_enable_cpuslocked(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); @@ -145,23 +145,37 @@ void static_key_enable(struct static_key atomic_set_release(&key->enabled, 1); } jump_label_unlock(); +} +EXPORT_SYMBOL_GPL(static_key_enable_cpuslocked); + +void static_key_enable(struct static_key *key) +{ + cpus_read_lock(); + static_key_enable_cpuslocked(key); cpus_read_unlock(); } EXPORT_SYMBOL_GPL(static_key_enable); -void static_key_disable(struct static_key *key) +void static_key_disable_cpuslocked(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(); +} +EXPORT_SYMBOL_GPL(static_key_disable_cpuslocked); + +void static_key_disable(struct static_key *key) +{ + cpus_read_lock(); + static_key_disable_cpuslocked(key); cpus_read_unlock(); } EXPORT_SYMBOL_GPL(static_key_disable);