Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp4834196ybi; Tue, 28 May 2019 03:19:35 -0700 (PDT) X-Google-Smtp-Source: APXvYqx2RAZxl58n9TqVZVv6Z1vhl+eOdbYOrkHe8CS2midr7dRpQw/k97w8rEbuyjZ0e2lUdzLb X-Received: by 2002:a62:5487:: with SMTP id i129mr138871817pfb.68.1559038775687; Tue, 28 May 2019 03:19:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1559038775; cv=none; d=google.com; s=arc-20160816; b=pz5tbF3dEs7wtISJSx4N/PrFlhv7YTS8+4G2Hizwvg53S7v3UaieXFUQsO9IUss1Ly hEV8v4A63BYfV3f4wD88aVds/xYBQZmRfBVdd2xH25aumEdcu35n1udgBcTrTRBHB6gm QH+ZAkmBT49EX+OcPA90i3Spl1PgH/r/P2jjcMWzApg8RIFNRunzhxLVcyz1cMYPytqT yLszScRpao+lEzGnKK4+Prs1mhFFlKoGm1RsOZ6+l/7TNu5Dq1DpqMTcHQ7oQwEGpdio w8iuuEZehld5CHfMnPsBi617zs2n3eaqx5STTy7TQWy5hSWMNEjbL6PKx+4lmRYbvkCY p+Aw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=8T2GKRKpXC0rezdgTZgTwOAN8/ZONjDLy5BcfVEtpV8=; b=oV7RINS9EvDFQy+N1RDarMDIzq6wApTH/sbBDFQ9MwANJ6XWvuThPUO4F5K7d0gaG/ NPiysAHKodBI/8I2aa+p2gfw9qqvVkp40dKjsSj85svJSWyESFd02dZrdimjc6WPPwq9 I/6Pc7+bsFkeMgvYzpASMfqTXNt48nBf+XOEONdMzbx67DEHtnz33Y8tzyTIOmx01VBn cr9D1lSCy8ThCpPhoW5/Jactlw9kcxhwcfXnk53w4kEC2+kXn2a+VJcsSxdv1X71lLaL FDWW+Ta83AGuvxPA1c/w2l46mngy27SIwuZs2tAlEXMJX+07ZHM8dB16/mhfjzJPpeVr 7CPg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id n11si6205590pgl.111.2019.05.28.03.19.18; Tue, 28 May 2019 03:19:35 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726522AbfE1Jla (ORCPT + 99 others); Tue, 28 May 2019 05:41:30 -0400 Received: from mga03.intel.com ([134.134.136.65]:8912 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726203AbfE1Jla (ORCPT ); Tue, 28 May 2019 05:41:30 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 May 2019 02:41:29 -0700 X-ExtLoop1: 1 Received: from ideak-desk.fi.intel.com ([10.237.72.204]) by orsmga003.jf.intel.com with ESMTP; 28 May 2019 02:41:27 -0700 From: Imre Deak To: LKML Cc: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= , Peter Zijlstra , Ingo Molnar , Will Deacon Subject: [PATCH v3 1/2] lockdep: Fix OOO unlock when hlocks need merging Date: Tue, 28 May 2019 12:40:50 +0300 Message-Id: <20190528094051.22840-1-imre.deak@intel.com> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The sequence static DEFINE_WW_CLASS(test_ww_class); struct ww_acquire_ctx ww_ctx; struct ww_mutex ww_lock_a; struct ww_mutex ww_lock_b; struct mutex lock_c; struct mutex lock_d; ww_acquire_init(&ww_ctx, &test_ww_class); ww_mutex_init(&ww_lock_a, &test_ww_class); ww_mutex_init(&ww_lock_b, &test_ww_class); mutex_init(&lock_c); ww_mutex_lock(&ww_lock_a, &ww_ctx); mutex_lock(&lock_c); ww_mutex_lock(&ww_lock_b, &ww_ctx); mutex_unlock(&lock_c); (*) ww_mutex_unlock(&ww_lock_b); ww_mutex_unlock(&ww_lock_a); ww_acquire_fini(&ww_ctx); triggers the following WARN in __lock_release() when doing the unlock at *: DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - 1); The problem is that the WARN check doesn't take into account the merging of ww_lock_a and ww_lock_b which results in decreasing curr->lockdep_depth by 2 not only 1. Note that the following sequence doesn't trigger the WARN, since there won't be any hlock merging. ww_acquire_init(&ww_ctx, &test_ww_class); ww_mutex_init(&ww_lock_a, &test_ww_class); ww_mutex_init(&ww_lock_b, &test_ww_class); mutex_init(&lock_c); mutex_init(&lock_d); ww_mutex_lock(&ww_lock_a, &ww_ctx); mutex_lock(&lock_c); mutex_lock(&lock_d); ww_mutex_lock(&ww_lock_b, &ww_ctx); mutex_unlock(&lock_d); ww_mutex_unlock(&ww_lock_b); ww_mutex_unlock(&ww_lock_a); mutex_unlock(&lock_c); ww_acquire_fini(&ww_ctx); In general both of the above two sequences are valid and shouldn't trigger any lockdep warning. Fix this by taking the decrement due to the hlock merging into account during lock release and hlock class re-setting. Merging can't happen during lock downgrading since there won't be a new possibility to merge hlocks in that case, so add a WARN if merging still happens then. v2: - Clarify the commit log and use mutex_lock() instead of mutex_trylock() in the example sequences for simplicity. v3: (Peter) - Sanitize counting the merge in reacquire_held_locks(). - Optimize calculating the new expected lockdep depth in __lock_release(). Cc: Ville Syrjälä Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Will Deacon Signed-off-by: Imre Deak --- kernel/locking/lockdep.c | 41 ++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index c47788fa85f9..7a48649ce6bc 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -3715,7 +3715,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, hlock->references = 2; } - return 1; + return 2; } } @@ -3921,22 +3921,33 @@ static struct held_lock *find_held_lock(struct task_struct *curr, } static int reacquire_held_locks(struct task_struct *curr, unsigned int depth, - int idx) + int idx, unsigned int *merged) { struct held_lock *hlock; + int first_idx = idx; if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) return 0; for (hlock = curr->held_locks + idx; idx < depth; idx++, hlock++) { - if (!__lock_acquire(hlock->instance, + switch (__lock_acquire(hlock->instance, hlock_class(hlock)->subclass, hlock->trylock, hlock->read, hlock->check, hlock->hardirqs_off, hlock->nest_lock, hlock->acquire_ip, - hlock->references, hlock->pin_count)) + hlock->references, hlock->pin_count)) { + case 0: return 1; + case 1: + break; + case 2: + *merged += (idx == first_idx); + break; + default: + WARN_ON(1); + return 0; + } } return 0; } @@ -3947,9 +3958,9 @@ __lock_set_class(struct lockdep_map *lock, const char *name, unsigned long ip) { struct task_struct *curr = current; + unsigned int depth, merged = 0; struct held_lock *hlock; struct lock_class *class; - unsigned int depth; int i; if (unlikely(!debug_locks)) @@ -3974,14 +3985,14 @@ __lock_set_class(struct lockdep_map *lock, const char *name, curr->lockdep_depth = i; curr->curr_chain_key = hlock->prev_chain_key; - if (reacquire_held_locks(curr, depth, i)) + if (reacquire_held_locks(curr, depth, i, &merged)) return 0; /* * I took it apart and put it back together again, except now I have * these 'spare' parts.. where shall I put them. */ - if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth)) + if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - merged)) return 0; return 1; } @@ -3989,8 +4000,8 @@ __lock_set_class(struct lockdep_map *lock, const char *name, static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip) { struct task_struct *curr = current; + unsigned int depth, merged = 0; struct held_lock *hlock; - unsigned int depth; int i; if (unlikely(!debug_locks)) @@ -4015,7 +4026,7 @@ static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip) hlock->read = 1; hlock->acquire_ip = ip; - if (reacquire_held_locks(curr, depth, i)) + if (reacquire_held_locks(curr, depth, i, &merged)) return 0; /* @@ -4024,6 +4035,11 @@ static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip) */ if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth)) return 0; + + /* Merging can't happen with unchanged classes.. */ + if (DEBUG_LOCKS_WARN_ON(merged)) + return 0; + return 1; } @@ -4038,8 +4054,8 @@ static int __lock_release(struct lockdep_map *lock, int nested, unsigned long ip) { struct task_struct *curr = current; + unsigned int depth, merged = 1; struct held_lock *hlock; - unsigned int depth; int i; if (unlikely(!debug_locks)) @@ -4094,14 +4110,15 @@ __lock_release(struct lockdep_map *lock, int nested, unsigned long ip) if (i == depth-1) return 1; - if (reacquire_held_locks(curr, depth, i + 1)) + if (reacquire_held_locks(curr, depth, i + 1, &merged)) return 0; /* * We had N bottles of beer on the wall, we drank one, but now * there's not N-1 bottles of beer left on the wall... + * Pouring two of the bottles together is acceptable. */ - DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth-1); + DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - merged); /* * Since reacquire_held_locks() would have called check_chain_key() -- 2.17.1