All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boqun Feng <boqun.feng@gmail.com>
To: Eric Dumazet <edumazet@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>,
	Breno Leitao <leitao@debian.org>, Ingo Molnar <mingo@redhat.com>,
	Will Deacon <will@kernel.org>, Waiman Long <longman@redhat.com>,
	aeh@meta.com, linux-kernel@vger.kernel.org,
	netdev@vger.kernel.org, jhs@mojatatu.com, kernel-team@meta.com,
	Erik Lundgren <elundgren@meta.com>,
	"Paul E. McKenney" <paulmck@kernel.org>
Subject: Re: [PATCH] lockdep: Speed up lockdep_unregister_key() with expedited RCU synchronization
Date: Mon, 24 Mar 2025 17:47:15 -0700	[thread overview]
Message-ID: <67e1fd15.050a0220.bc49a.766e@mx.google.com> (raw)
In-Reply-To: <67e1b2c4.050a0220.353291.663c@mx.google.com>

On Mon, Mar 24, 2025 at 12:30:10PM -0700, Boqun Feng wrote:
> On Mon, Mar 24, 2025 at 12:21:07PM -0700, Boqun Feng wrote:
> > On Mon, Mar 24, 2025 at 01:23:50PM +0100, Eric Dumazet wrote:
> > [...]
> > > > > ---
> > > > >  kernel/locking/lockdep.c | 6 ++++--
> > > > >  1 file changed, 4 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> > > > > index 4470680f02269..a79030ac36dd4 100644
> > > > > --- a/kernel/locking/lockdep.c
> > > > > +++ b/kernel/locking/lockdep.c
> > > > > @@ -6595,8 +6595,10 @@ void lockdep_unregister_key(struct lock_class_key *key)
> > > > >       if (need_callback)
> > > > >               call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
> > > > >
> > > > > -     /* Wait until is_dynamic_key() has finished accessing k->hash_entry. */
> > > > > -     synchronize_rcu();
> > 
> > I feel a bit confusing even for the old comment, normally I would expect
> > the caller of lockdep_unregister_key() should guarantee the key has been
> > unpublished, in other words, there is no way a lockdep_unregister_key()
> > could race with a register_lock_class()/lockdep_init_map_type(). The
> > synchronize_rcu() is not needed then.
> > 
> > Let's say someone breaks my assumption above, then when doing a
> > register_lock_class() with a key about to be unregister, I cannot see
> > anything stops the following:
> > 
> > 	CPU 0				CPU 1
> > 	=====				=====
> > 	register_lock_class():
> > 	  ...
> > 	  } else if (... && !is_dynamic_key(lock->key)) {
> > 	  	// ->key is not unregistered yet, so this branch is not
> > 		// taken.
> > 	  	return NULL;
> > 	  }
> > 	  				lockdep_unregister_key(..);
> > 					// key unregister, can be free
> > 					// any time.
> > 	  key = lock->key->subkeys + subclass; // BOOM! UAF.
> > 
> > So either we don't need the synchronize_rcu() here or the
> > synchronize_rcu() doesn't help at all. Am I missing something subtle
> > here?
> > 
> 
> Oh! Maybe I was missing register_lock_class() must be called with irq
> disabled, which is also an RCU read-side critical section.
> 

Since register_lock_class() will be call with irq disabled, maybe hazard
pointers [1] is better because most of the case we only have nr_cpus
readers, so the potential hazard pointer slots are fixed.

So the below patch can reduce the time of the tc command from real ~1.7
second (v6.14) to real ~0.05 second (v6.14 + patch) in my test env,
which is not surprising given it's a dedicated hazard pointers for
lock_class_key.

Thoughts?

[1]: https://lore.kernel.org/lkml/20240917143402.930114-1-boqun.feng@gmail.com/

Regards,
Boqun

---------------------------------->8
From: Boqun Feng <boqun.feng@gmail.com>
Date: Mon, 24 Mar 2025 13:38:15 -0700
Subject: [PATCH] WIP

Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
---
 kernel/locking/lockdep.c | 65 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 61 insertions(+), 4 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 4470680f0226..0b6e78d56cfe 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -111,6 +111,29 @@ late_initcall(kernel_lockdep_sysctls_init);
 DEFINE_PER_CPU(unsigned int, lockdep_recursion);
 EXPORT_PER_CPU_SYMBOL_GPL(lockdep_recursion);
 
+/* hazptr free always clears the slot */
+DEFINE_FREE(lockdep_key_hazptr, struct lock_class_key **, if (_T) smp_store_release((_T), NULL));
+DEFINE_PER_CPU(struct lock_class_key *, lockdep_key_hazptr);
+
+static void synchronize_lockdep_key_hazptr(struct lock_class_key *key)
+{
+	int cpu;
+
+	if (!key)
+		return;
+	/*
+	 * Synchronizes with the smp_mb() after protecting the pointer with
+	 * WRITE_ONCE().
+	 */
+	smp_mb();
+
+	for_each_possible_cpu(cpu) {
+		struct lock_class_key **hazptr = per_cpu_ptr(&lockdep_key_hazptr, cpu);
+
+		smp_cond_load_acquire(hazptr, VAL != key);
+	}
+}
+
 static __always_inline bool lockdep_enabled(void)
 {
 	if (!debug_locks)
@@ -1283,6 +1306,7 @@ static struct lock_class *
 register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
 {
 	struct lockdep_subclass_key *key;
+	struct lock_class_key **key_hazptr __free(lockdep_key_hazptr) = NULL;
 	struct hlist_head *hash_head;
 	struct lock_class *class;
 	int idx;
@@ -1293,11 +1317,25 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
 	if (likely(class))
 		goto out_set_class_cache;
 
+	/* Interrupts are disabled hence it's safe to acquire the hazptr slot */
+	key_hazptr = this_cpu_ptr(&lockdep_key_hazptr);
+
+	/* hazptr slot must be unusued */
+	BUG_ON(*key_hazptr);
+
 	if (!lock->key) {
 		if (!assign_lock_key(lock))
 			return NULL;
-	} else if (!static_obj(lock->key) && !is_dynamic_key(lock->key)) {
-		return NULL;
+	} else {
+		/* hazptr: protect the key */
+		WRITE_ONCE(*key_hazptr, lock->key);
+
+		/* Synchronizes with the smp_mb() in synchronize_lockdep_key_hazptr() */
+		smp_mb();
+
+		if (!static_obj(lock->key) && !is_dynamic_key(lock->key)) {
+			return NULL;
+		}
 	}
 
 	key = lock->key->subkeys + subclass;
@@ -4964,16 +5002,35 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,
 	 */
 	if (DEBUG_LOCKS_WARN_ON(!key))
 		return;
+
+	preempt_disable();
+	struct lock_class_key **hazptr __free(lockdep_key_hazptr) = this_cpu_ptr(&lockdep_key_hazptr);
+
+	/* hapztr: Protect the key */
+	WRITE_ONCE(*hazptr, key);
+
+	/* Synchronizes with the smp_mb() in synchronize_lockdep_key_hazptr() */
+	smp_mb();
+
 	/*
 	 * Sanity check, the lock-class key must either have been allocated
 	 * statically or must have been registered as a dynamic key.
 	 */
 	if (!static_obj(key) && !is_dynamic_key(key)) {
+		preempt_enable();
 		if (debug_locks)
 			printk(KERN_ERR "BUG: key %px has not been registered!\n", key);
 		DEBUG_LOCKS_WARN_ON(1);
 		return;
 	}
+
+	/* hazptr: Release the key */
+	smp_store_release(hazptr, NULL);
+
+	/* Do not auto clean up*/
+	hazptr = NULL;
+	preempt_enable();
+
 	lock->key = key;
 
 	if (unlikely(!debug_locks))
@@ -6595,8 +6652,8 @@ void lockdep_unregister_key(struct lock_class_key *key)
 	if (need_callback)
 		call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
 
-	/* Wait until is_dynamic_key() has finished accessing k->hash_entry. */
-	synchronize_rcu();
+	/* Wait until register_lock_class() has finished accessing key. */
+	synchronize_lockdep_key_hazptr(key);
 }
 EXPORT_SYMBOL_GPL(lockdep_unregister_key);
 
-- 
2.48.1



  reply	other threads:[~2025-03-25  0:47 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-21  9:30 [PATCH] lockdep: Speed up lockdep_unregister_key() with expedited RCU synchronization Breno Leitao
2025-03-21 10:37 ` Eric Dumazet
2025-03-21 14:22   ` Breno Leitao
2025-03-24 12:12 ` Peter Zijlstra
2025-03-24 12:23   ` Eric Dumazet
2025-03-24 12:24     ` Eric Dumazet
2025-03-24 19:21     ` Boqun Feng
2025-03-24 19:30       ` Boqun Feng
2025-03-25  0:47         ` Boqun Feng [this message]
2025-03-25  1:56           ` Waiman Long
2025-03-25  3:41             ` Boqun Feng
     [not found]               ` <934d794b-7ebc-422c-b4fe-3e658a2e5e7a@redhat.com>
2025-03-25 14:57                 ` Waiman Long
2025-03-25 18:45                 ` Boqun Feng
2025-03-25 19:23                   ` Waiman Long
2025-03-25 19:42                     ` Boqun Feng
2025-03-25 23:20                       ` Waiman Long
2025-03-26  5:25                         ` Boqun Feng
     [not found]                           ` <df237702-55c3-466b-b51e-f3fe46ae03ba@redhat.com>
2025-03-26 16:40                             ` Waiman Long
2025-03-26 16:47                               ` Boqun Feng
2025-03-26 17:02                                 ` Waiman Long
2025-03-26 17:10                                   ` Paul E. McKenney
2025-03-26 18:42                                     ` Boqun Feng
2025-03-26 21:37                                       ` Paul E. McKenney
2025-03-31 16:48                                       ` Breno Leitao
2025-03-31 17:34                                         ` Boqun Feng
2025-03-31 17:26                             ` Boqun Feng
2025-03-31 17:33                               ` Waiman Long
2025-03-31 18:33                                 ` Paul E. McKenney
2025-03-31 18:57                                   ` Waiman Long
2025-03-31 21:21                                     ` Boqun Feng
2025-03-31 21:47                                       ` Waiman Long
2025-03-31 17:42                               ` Eric Dumazet
2025-07-09 10:00 ` Breno Leitao
2025-07-09 13:57   ` Waiman Long
2025-07-09 14:57     ` Boqun Feng
2025-07-19 17:40 ` [tip: locking/core] " tip-bot2 for Breno Leitao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=67e1fd15.050a0220.bc49a.766e@mx.google.com \
    --to=boqun.feng@gmail.com \
    --cc=aeh@meta.com \
    --cc=edumazet@google.com \
    --cc=elundgren@meta.com \
    --cc=jhs@mojatatu.com \
    --cc=kernel-team@meta.com \
    --cc=leitao@debian.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=mingo@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=paulmck@kernel.org \
    --cc=peterz@infradead.org \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.