From patchwork Fri Jan 29 14:03:27 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Wagner X-Patchwork-Id: 8163631 Return-Path: X-Original-To: patchwork-linux-fbdev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 91B53BEEE5 for ; Fri, 29 Jan 2016 14:04:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B4080200D9 for ; Fri, 29 Jan 2016 14:04:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7BC4F20253 for ; Fri, 29 Jan 2016 14:04:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932208AbcA2OES (ORCPT ); Fri, 29 Jan 2016 09:04:18 -0500 Received: from mail.bmw-carit.de ([62.245.222.98]:46646 "EHLO linuxmail.bmw-carit.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756180AbcA2ODk (ORCPT ); Fri, 29 Jan 2016 09:03:40 -0500 Received: from localhost (handman.bmw-carit.intra [192.168.101.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by linuxmail.bmw-carit.de (Postfix) with ESMTPS id DBD1259CFE; Fri, 29 Jan 2016 14:47:32 +0100 (CET) From: Daniel Wagner To: linux-kernel@vger.kernel.org, linux-rt-users@vger.kernel.org Cc: linux-fbdev@vger.kernel.org, linux-mips@linux-mips.org, Marcelo Tosatti , Paolo Bonzini , "Paul E. McKenney" , Paul Gortmaker , "Peter Zijlstra (Intel)" , Thomas Gleixner , Steven Rostedt , Boqun Feng , Maik Broemme , Ralf Baechle , Daniel Wagner Subject: [PATCH tip v7 6/7] rcu: Do not call rcu_nocb_gp_cleanup() while holding rnp->lock Date: Fri, 29 Jan 2016 15:03:27 +0100 Message-Id: <1454076208-28354-7-git-send-email-daniel.wagner@bmw-carit.de> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1454076208-28354-1-git-send-email-daniel.wagner@bmw-carit.de> References: <1454076208-28354-1-git-send-email-daniel.wagner@bmw-carit.de> Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP rcu_nocb_gp_cleanup() is called while holding rnp->lock. Currently, this is okay because the wake_up_all() in rcu_nocb_gp_cleanup() will not enable the IRQs. lockdep is happy. By switching over using swait this is not true anymore. swake_up_all() enables the IRQs while processing the waiters. __do_softirq() can now run and will eventually call rcu_process_callbacks() which wants to grap nrp->lock. Let's move the rcu_nocb_gp_cleanup() call outside the lock before we switch over to swait. If we would hold the rnp->lock and use swait, lockdep reports following: ================================= [ INFO: inconsistent lock state ] 4.2.0-rc5-00025-g9a73ba0 #136 Not tainted --------------------------------- inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. rcu_preempt/8 [HC0[0]:SC0[0]:HE1:SE1] takes: (rcu_node_1){+.?...}, at: [] rcu_gp_kthread+0xb97/0xeb0 {IN-SOFTIRQ-W} state was registered at: [] __lock_acquire+0xd5f/0x21e0 [] lock_acquire+0xdf/0x2b0 [] _raw_spin_lock_irqsave+0x59/0xa0 [] rcu_process_callbacks+0x141/0x3c0 [] __do_softirq+0x14d/0x670 [] irq_exit+0x104/0x110 [] smp_apic_timer_interrupt+0x46/0x60 [] apic_timer_interrupt+0x70/0x80 [] rq_attach_root+0xa6/0x100 [] cpu_attach_domain+0x16d/0x650 [] build_sched_domains+0x942/0xb00 [] sched_init_smp+0x509/0x5c1 [] kernel_init_freeable+0x172/0x28f [] kernel_init+0xe/0xe0 [] ret_from_fork+0x3f/0x70 irq event stamp: 76 hardirqs last enabled at (75): [] _raw_spin_unlock_irq+0x30/0x60 hardirqs last disabled at (76): [] _raw_spin_lock_irq+0x1f/0x90 softirqs last enabled at (0): [] copy_process.part.26+0x602/0x1cf0 softirqs last disabled at (0): [< (null)>] (null) other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(rcu_node_1); lock(rcu_node_1); *** DEADLOCK *** 1 lock held by rcu_preempt/8: #0: (rcu_node_1){+.?...}, at: [] rcu_gp_kthread+0xb97/0xeb0 stack backtrace: CPU: 0 PID: 8 Comm: rcu_preempt Not tainted 4.2.0-rc5-00025-g9a73ba0 #136 Hardware name: Dell Inc. PowerEdge R820/066N7P, BIOS 2.0.20 01/16/2014 0000000000000000 000000006d7e67d8 ffff881fb081fbd8 ffffffff818379e0 0000000000000000 ffff881fb0812a00 ffff881fb081fc38 ffffffff8110813b 0000000000000000 0000000000000001 ffff881f00000001 ffffffff8102fa4f Call Trace: [] dump_stack+0x4f/0x7b [] print_usage_bug+0x1db/0x1e0 [] ? save_stack_trace+0x2f/0x50 [] mark_lock+0x66d/0x6e0 [] ? check_usage_forwards+0x150/0x150 [] mark_held_locks+0x78/0xa0 [] ? _raw_spin_unlock_irq+0x30/0x60 [] trace_hardirqs_on_caller+0x168/0x220 [] trace_hardirqs_on+0xd/0x10 [] _raw_spin_unlock_irq+0x30/0x60 [] swake_up_all+0xb7/0xe0 [] rcu_gp_kthread+0xab1/0xeb0 [] ? trace_hardirqs_on_caller+0xff/0x220 [] ? _raw_spin_unlock_irq+0x41/0x60 [] ? rcu_barrier+0x20/0x20 [] kthread+0x104/0x120 [] ? _raw_spin_unlock_irq+0x30/0x60 [] ? kthread_create_on_node+0x260/0x260 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x260/0x260 Signed-off-by: Daniel Wagner Acked-by: Peter Zijlstra (Intel) Cc: "Paul E. McKenney" Cc: Thomas Gleixner --- kernel/rcu/tree.c | 4 +++- kernel/rcu/tree.h | 3 ++- kernel/rcu/tree_plugin.h | 16 +++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index f07343b..baf6d09 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1590,7 +1590,6 @@ static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) int needmore; struct rcu_data *rdp = this_cpu_ptr(rsp->rda); - rcu_nocb_gp_cleanup(rsp, rnp); rnp->need_future_gp[c & 0x1] = 0; needmore = rnp->need_future_gp[(c + 1) & 0x1]; trace_rcu_future_gp(rnp, rdp, c, @@ -1991,6 +1990,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) int nocb = 0; struct rcu_data *rdp; struct rcu_node *rnp = rcu_get_root(rsp); + wait_queue_head_t *sq; WRITE_ONCE(rsp->gp_activity, jiffies); raw_spin_lock_irq(&rnp->lock); @@ -2029,7 +2029,9 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) needgp = __note_gp_changes(rsp, rnp, rdp) || needgp; /* smp_mb() provided by prior unlock-lock pair. */ nocb += rcu_future_gp_cleanup(rsp, rnp); + sq = rcu_nocb_gp_get(rnp); raw_spin_unlock_irq(&rnp->lock); + rcu_nocb_gp_cleanup(sq); cond_resched_rcu_qs(); WRITE_ONCE(rsp->gp_activity, jiffies); rcu_gp_slow(rsp, gp_cleanup_delay); diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 9fb4e23..aa47e2c 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -607,7 +607,8 @@ static void zero_cpu_stall_ticks(struct rcu_data *rdp); static void increment_cpu_stall_ticks(void); static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu); static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq); -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp); +static wait_queue_head_t *rcu_nocb_gp_get(struct rcu_node *rnp); +static void rcu_nocb_gp_cleanup(wait_queue_head_t *sq); static void rcu_init_one_nocb(struct rcu_node *rnp); static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, bool lazy, unsigned long flags); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 630c197..99b1ce6 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1822,9 +1822,9 @@ early_param("rcu_nocb_poll", parse_rcu_nocb_poll); * Wake up any no-CBs CPUs' kthreads that were waiting on the just-ended * grace period. */ -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) +static void rcu_nocb_gp_cleanup(wait_queue_head_t *sq) { - wake_up_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); + wake_up_all(sq); } /* @@ -1840,6 +1840,11 @@ static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) rnp->need_future_gp[(rnp->completed + 1) & 0x1] += nrq; } +static wait_queue_head_t *rcu_nocb_gp_get(struct rcu_node *rnp) +{ + return &rnp->nocb_gp_wq[rnp->completed & 0x1]; +} + static void rcu_init_one_nocb(struct rcu_node *rnp) { init_waitqueue_head(&rnp->nocb_gp_wq[0]); @@ -2514,7 +2519,7 @@ static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu) return false; } -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) +static void rcu_nocb_gp_cleanup(wait_queue_head_t *sq) { } @@ -2522,6 +2527,11 @@ static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) { } +static wait_queue_head_t *rcu_nocb_gp_get(struct rcu_node *rnp) +{ + return NULL; +} + static void rcu_init_one_nocb(struct rcu_node *rnp) { }