locking/lockdep: Fix merging of hlocks with non-zero references [Linux 4.4.187]

This Linux kernel change "locking/lockdep: Fix merging of hlocks with non-zero references" is included in the Linux 4.4.187 release. This change is authored by Imre Deak <imre.deak [at] intel.com> on Fri May 24 23:15:09 2019 +0300. The commit for this change in Linux stable tree is aa147b3 (patch) which is from upstream commit d934985. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream d934985.

locking/lockdep: Fix merging of hlocks with non-zero references

[ Upstream commit d9349850e188b8b59e5322fda17ff389a1c0cd7d ]

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 ww_mutex ww_lock_c;
    struct mutex lock_c;

    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);
    ww_mutex_init(&ww_lock_c, &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);
    ww_mutex_lock(&ww_lock_c, &ww_ctx);

    mutex_unlock(&lock_c);  (*)

    ww_mutex_unlock(&ww_lock_c);
    ww_mutex_unlock(&ww_lock_b);
    ww_mutex_unlock(&ww_lock_a);

    ww_acquire_fini(&ww_ctx); (**)

will trigger the following error in __lock_release() when calling
mutex_release() at **:

    DEBUG_LOCKS_WARN_ON(depth <= 0)

The problem is that the hlock merging happening at * updates the
references for test_ww_class incorrectly to 3 whereas it should've
updated it to 4 (representing all the instances for ww_ctx and
ww_lock_[abc]).

Fix this by updating the references during merging correctly taking into
account that we can have non-zero references (both for the hlock that we
merge into another hlock or for the hlock we are merging into).

Signed-off-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Will Deacon <will.deacon@arm.com>
Link: https://lkml.kernel.org/r/20190524201509.9199-2-imre.deak@intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 18 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 kernel/locking/lockdep.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 774ab79..f2df5f8 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3128,17 +3128,17 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
    if (depth) {
        hlock = curr->held_locks + depth - 1;
        if (hlock->class_idx == class_idx && nest_lock) {
-           if (hlock->references) {
-               /*
-                * Check: unsigned int references:12, overflow.
-                */
-               if (DEBUG_LOCKS_WARN_ON(hlock->references == (1 << 12)-1))
-                   return 0;
+           if (!references)
+               references++;

+           if (!hlock->references)
                hlock->references++;
-           } else {
-               hlock->references = 2;
-           }
+
+           hlock->references += references;
+
+           /* Overflow */
+           if (DEBUG_LOCKS_WARN_ON(hlock->references < references))
+               return 0;

            return 1;
        }

Leave a Reply

Your email address will not be published. Required fields are marked *