LOCKDEP and Rust locking changes for v6.15
Subsystems (6)
Hi Peter & Ingo,
Please pull the changes of lockdep and Rust locking for v6.15 into tip.
The following changes since commit 337369f8ce9e20226402cf139c4f0d3ada7d1705:
locking/mutex: Add MUTEX_WARN_ON() into fast path (2025-02-21 20:19:12 +0100)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/boqun/linux.git tags/lockdep-for-tip.2025.02.25
for you to fetch changes up to 3b5b307cf84ca3a73abe797df31e0efe897411a9:
rust: lockdep: Use Pin for all LockClassKey usages (2025-02-25 08:53:08 -0800)
Regards,
Boqun
----------------------------------------------------------------
Lockdep changes for v6.15:
- Fix kernel-doc for rtmutex.
- Fix lock wait context check on softirq for PREEMPT_RT.
- Disable KASAN for lockdep to improve debug kernel performance.
Miscellaneous locking changes for v6.15:
- Use wake_q for semaphore to avoid wake-up in lock critical sections.
Rust locking changes for v6.15:
- Fix the soundness issue around dynamically allocated lock class keys.
- Add CondVar::wait_interruptible_freezable() to allow freezable waits.
- Add Guard::lock_ref() to access the lock reference behind a lock guard.
----------------------------------------------------------------
Alice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Add wait_interruptible_freezable()
Boqun Feng (1):
rust: sync: lock: Add an example for Guard::lock_ref()
Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usages
Randy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc comment
Ryo Takakura (1):
lockdep: Fix wait context check on softirq for PREEMPT_RT
Waiman Long (5):
locking/semaphore: Use wake_q to wake up processes outside lock critical section locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation of lockdep.c locking/lockdep: Add kasan_check_byte() check in lock_acquire()
include/linux/bottom_half.h | 8 +++++ kernel/locking/Makefile | 3 +- kernel/locking/lock_events_list.h | 28 +++++++++++++++++ kernel/locking/lockdep.c | 17 ++++++++++- kernel/locking/rtmutex.c | 29 ++++++++++++++---- kernel/locking/rtmutex_common.h | 4 +-- kernel/locking/semaphore.c | 13 +++++--- kernel/softirq.c | 12 ++++++++ rust/helpers/helpers.c | 1 + rust/helpers/sync.c | 13 ++++++++ rust/kernel/sync.rs | 63 ++++++++++++++++++++++++++++++++------- rust/kernel/sync/condvar.rs | 28 ++++++++++++++--- rust/kernel/sync/lock.rs | 35 ++++++++++++++++++++-- rust/kernel/sync/lock/global.rs | 5 ++-- rust/kernel/sync/poll.rs | 2 +- rust/kernel/task.rs | 2 ++ rust/kernel/workqueue.rs | 2 +- 17 files changed, 231 insertions(+), 34 deletions(-) create mode 100644 rust/helpers/sync.c
Quoted 2 lines · Boqun Feng
Hi Peter & Ingo,
Ping ;-)
Regards,
Boqun
Quoted 80 lines
Please pull the changes of lockdep and Rust locking for v6.15 into tip.
The following changes since commit
337369f8ce9e20226402cf139c4f0d3ada7d1705:locking/mutex: Add
MUTEX_WARN_ON()into fast path (2025-02-21 20:19:12 +0100)are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/boqun/linux.gittags/lockdep-for-tip.2025.02.25for you to fetch changes up to
3b5b307cf84ca3a73abe797df31e0efe897411a9:rust: lockdep: Use Pin for all LockClassKey usages (2025-02-25 08:53:08 -0800)
Regards,
Boqun----------------------------------------------------------------
Lockdep changes for v6.15:- Fix kernel-doc for rtmutex.
- Fix lock wait context check on softirq forPREEMPT_RT.
- Disable KASAN for lockdep to improve debug kernel performance.Miscellaneous locking changes for v6.15:
- Use
wake_qfor semaphore to avoid wake-up in lock critical sections.Rust locking changes for v6.15:
- Fix the soundness issue around dynamically allocated lock class keys.
- AddCondVar::wait_interruptible_freezable()to allow freezable waits.
- AddGuard::lock_ref()to access the lock reference behind a lock guard.----------------------------------------------------------------
Alice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Addwait_interruptible_freezable()Boqun Feng (1):
rust: sync: lock: Add an example forGuard::lock_ref()Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usagesRandy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc commentRyo Takakura (1):
lockdep: Fix wait context check on softirq forPREEMPT_RTWaiman Long (5):
locking/semaphore: Usewake_qto wake up processes outside lock critical section locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation oflockdep.clocking/lockdep: Addkasan_check_byte()check inlock_acquire()include/linux/bottom_half.h | 8 +++++ kernel/locking/Makefile | 3 +- kernel/locking/lock_events_list.h | 28 +++++++++++++++++ kernel/locking/lockdep.c | 17 ++++++++++- kernel/locking/rtmutex.c | 29 ++++++++++++++---- kernel/locking/rtmutex_common.h | 4 +-- kernel/locking/semaphore.c | 13 +++++--- kernel/softirq.c | 12 ++++++++ rust/helpers/helpers.c | 1 + rust/helpers/sync.c | 13 ++++++++ rust/kernel/sync.rs | 63 ++++++++++++++++++++++++++++++++------- rust/kernel/sync/condvar.rs | 28 ++++++++++++++--- rust/kernel/sync/lock.rs | 35 ++++++++++++++++++++-- rust/kernel/sync/lock/global.rs | 5 ++-- rust/kernel/sync/poll.rs | 2 +- rust/kernel/task.rs | 2 ++ rust/kernel/workqueue.rs | 2 +- 17 files changed, 231 insertions(+), 34 deletions(-) create mode 100644 rust/helpers/sync.c
Quoted 4 lines · Boqun Feng
Quoted 2 lines · Boqun Feng
Hi Peter & Ingo,
Ping ;-)
Hi Peter, are you going to pull this lockep branch into tip?
Thanks,
Longman
Peter Zijlstra
maintainerQuoted 17 lines · Boqun Feng
Alice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Addwait_interruptible_freezable()Boqun Feng (1):
rust: sync: lock: Add an example forGuard::lock_ref()Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usagesRandy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc commentRyo Takakura (1):
lockdep: Fix wait context check on softirq forPREEMPT_RT
Quoted 6 lines
Waiman Long (5):
locking/semaphore: Usewake_qto wake up processes outside lock critical section locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation oflockdep.clocking/lockdep: Addkasan_check_byte()check inlock_acquire()
OK, done!
Thanks, and sorry for being tardy, somehow time got away from me :/
cover LOCKDEP and Rust locking changes for v6.15
In reply to Boqun FengHi Ingo & Peter,
As discussed, I'm resending the pull request for lockdep and Rust locking for v6.15 in the format of email patches. I dropped one patch and will postpone it for v6.16 because of a bug [1], and I think the fix
[2] needs more reviews.
[1]: https://lore.kernel.org/lkml/20250306122413.GBZ8mT7Z61Tmgnh5Y9@fat_crate.local/
[2]: https://lore.kernel.org/lkml/Z8t8imzJVhWyDvhC@boqun-archlinux/
Regards,
Boqun
Alice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Add wait_interruptible_freezable()
Boqun Feng (1):
rust: sync: lock: Add an example for Guard::lock_ref()
Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usages
Randy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc comment
Waiman Long (5):
locking/semaphore: Use wake_q to wake up processes outside lock
critical section
locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation of lockdep.c locking/lockdep: Add kasan_check_byte() check in lock_acquire()
kernel/locking/Makefile | 3 +- kernel/locking/lock_events_list.h | 28 ++++++++++++++ kernel/locking/lockdep.c | 17 ++++++++- kernel/locking/rtmutex.c | 29 +++++++++++--- kernel/locking/rtmutex_common.h | 4 +- kernel/locking/semaphore.c | 13 +++++-- rust/helpers/helpers.c | 1 + rust/helpers/sync.c | 13 +++++++ rust/kernel/sync.rs | 63 ++++++++++++++++++++++++++----- rust/kernel/sync/condvar.rs | 28 ++++++++++++-- rust/kernel/sync/lock.rs | 35 +++++++++++++++-- rust/kernel/sync/lock/global.rs | 5 ++- rust/kernel/sync/poll.rs | 2 +- rust/kernel/task.rs | 2 + rust/kernel/workqueue.rs | 2 +- 15 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 rust/helpers/sync.c
Signature · 1 line
2.47.1
PATCH 1/11 locking/rtmutex: Use struct keyword in kernel-doc comment
From: Randy Dunlap <[email protected]>
Add the "struct" keyword to prevent a kernel-doc warning:
rtmutex_common.h:67: warning: cannot understand function prototype: 'struct rt_wake_q_head '
--- kernel/locking/rtmutex_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/locking/rtmutex_common.h +2 -2
@@ -59,8 +59,8 @@ struct rt_mutex_waiter { };/** -* rt_wake_q_head - Wrapper around regular wake_q_head to support -* "sleeping" spinlocks on RT +* struct rt_wake_q_head - Wrapper around regular wake_q_head to support +* "sleeping" spinlocks on RT * @head: The regular wake_q_head for sleeping lock variants * @rtlock_task: Task pointer for RT lock (spin/rwlock) wakeups */
Signature · 1 line
2.47.1
[tip: locking/core] locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: b3c5ec8b79bf6bc49cc4850d0949d712830283d7 Gitweb: https://git.kernel.org/tip/b3c5ec8b79bf6bc49cc4850d0949d712830283d7 Author: Randy Dunlap <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:51 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:52:01 +01:00
locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
Add the "struct" keyword to prevent a kernel-doc warning:
rtmutex_common.h:67: warning: cannot understand function prototype: 'struct rt_wake_q_head '
--- kernel/locking/rtmutex_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/locking/rtmutex_common.h +2 -2
@@ -59,8 +59,8 @@ struct rt_mutex_waiter { };/** -* rt_wake_q_head - Wrapper around regular wake_q_head to support -* "sleeping" spinlocks on RT +* struct rt_wake_q_head - Wrapper around regular wake_q_head to support +* "sleeping" spinlocks on RT * @head: The regular wake_q_head for sleeping lock variants * @rtlock_task: Task pointer for RT lock (spin/rwlock) wakeups */
PATCH 2/11 locking/semaphore: Use wake_q to wake up processes outside lock critical section
In reply to Boqun FengFrom: Waiman Long <[email protected]>
A circular lock dependency splat has been seen involving down_trylock().
[ 4011.795602] ======================================================
[ 4011.795603] WARNING: possible circular locking dependency detected
[ 4011.795607] 6.12.0-41.el10.s390x+debug
[ 4011.795612] ------------------------------------------------------
[ 4011.795613] dd/32479 is trying to acquire lock:
[ 4011.795617] 0015a20accd0d4f8 ((console_sem).lock){-.-.}-{2:2}, at: down_trylock+0x26/0x90
[ 4011.795636]
[ 4011.795636] but task is already holding lock:
[ 4011.795637] 000000017e461698 (&zone->lock){-.-.}-{2:2}, at: rmqueue_bulk+0xac/0x8f0 the existing dependency chain (in reverse order) is:
-> #4 (&zone->lock){-.-.}-{2:2}:
-> #3 (hrtimer_bases.lock){-.-.}-{2:2}:
-> #2 (&rq->__lock){-.-.}-{2:2}:
-> #1 (&p->pi_lock){-.-.}-{2:2}:
-> #0 ((console_sem).lock){-.-.}-{2:2}:
The console_sem -> pi_lock dependency is due to calling try_to_wake_up() while holding the console.sem raw_spinlock. This dependency can be broken by using wake_q to do the wakeup instead of calling try_to_wake_up() under the console_sem lock. This will also make the semaphore's raw_spinlock become a terminal lock without taking any further locks underneath it.
The hrtimer_bases.lock is a raw_spinlock while zone->lock is a spinlock. The hrtimer_bases.lock -> zone->lock dependency happens via the debug_objects_fill_pool() helper function in the debugobjects code.
[ 4011.795646] -> #4 (&zone->lock){-.-.}-{2:2}:
[ 4011.795650] __lock_acquire+0xe86/0x1cc0
[ 4011.795655] lock_acquire.part.0+0x258/0x630
[ 4011.795657] lock_acquire+0xb8/0xe0
[ 4011.795659] _raw_spin_lock_irqsave+0xb4/0x120
[ 4011.795663] rmqueue_bulk+0xac/0x8f0
[ 4011.795665] __rmqueue_pcplist+0x580/0x830
[ 4011.795667] rmqueue_pcplist+0xfc/0x470
[ 4011.795669] rmqueue.isra.0+0xdec/0x11b0
[ 4011.795671] get_page_from_freelist+0x2ee/0xeb0
[ 4011.795673] __alloc_pages_noprof+0x2c2/0x520
[ 4011.795676] alloc_pages_mpol_noprof+0x1fc/0x4d0
[ 4011.795681] alloc_pages_noprof+0x8c/0xe0
[ 4011.795684] allocate_slab+0x320/0x460
[ 4011.795686] ___slab_alloc+0xa58/0x12b0
[ 4011.795688] __slab_alloc.isra.0+0x42/0x60
[ 4011.795690] kmem_cache_alloc_noprof+0x304/0x350
[ 4011.795692] fill_pool+0xf6/0x450
[ 4011.795697] debug_object_activate+0xfe/0x360
[ 4011.795700] enqueue_hrtimer+0x34/0x190
[ 4011.795703] __run_hrtimer+0x3c8/0x4c0
[ 4011.795705] __hrtimer_run_queues+0x1b2/0x260
[ 4011.795707] hrtimer_interrupt+0x316/0x760
[ 4011.795709] do_IRQ+0x9a/0xe0
[ 4011.795712] do_irq_async+0xf6/0x160Normally raw_spinlock to spinlock dependency is not legit and will be warned if PROVE_RAW_LOCK_NESTING is enabled, but debug_objects_fill_pool() is an exception as it explicitly allows this dependency for non-PREEMPT_RT kernel without causing PROVE_RAW_LOCK_NESTING lockdep splat. As a result, this dependency is legit and not a bug.
Anyway, semaphore is the only locking primitive left that is still using try_to_wake_up() to do wakeup inside critical section, all the other locking primitives had been migrated to use wake_q to do wakeup outside of the critical section. It is also possible that there are other circular locking dependencies involving printk/console_sem or other existing/new semaphores lurking somewhere which may show up in the future. Let just do the migration now to wake_q to avoid headache like this.
--- kernel/locking/semaphore.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
kernel/locking/semaphore.c +9 -4
@@ -29,6 +29,7 @@#include <linux/export.h> #include <linux/sched.h> #include <linux/sched/debug.h> +#include <linux/sched/wake_q.h> #include <linux/semaphore.h> #include <linux/spinlock.h> #include <linux/ftrace.h>
@@ -38,7 +39,7 @@ static noinline void __down(struct semaphore *sem); static noinline int __down_interruptible(struct semaphore *sem); static noinline int __down_killable(struct semaphore *sem); static noinline int __down_timeout(struct semaphore *sem, long timeout); -static noinline void __up(struct semaphore *sem); +static noinline void __up(struct semaphore *sem, struct wake_q_head *wake_q);/** * down - acquire the semaphore
@@ -183,13 +184,16 @@ EXPORT_SYMBOL(down_timeout); void __sched up(struct semaphore *sem) {unsigned long flags; +DEFINE_WAKE_Q (wake_q);raw_spin_lock_irqsave (&sem->lock, flags);if (likely (list_empty (&sem->wait_list))) sem->count++;else -__up (sem); +__up (sem, &wake_q);raw_spin_unlock_irqrestore (&sem->lock, flags); +if (!wake_q_empty (&wake_q)) +wake_up_q (&wake_q); }EXPORT_SYMBOL (up);
@@ -269,11 +273,12 @@ static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)return __down_common (sem,TASK_UNINTERRUPTIBLE , timeout); } -static noinline void __sched __up(struct semaphore *sem) +static noinline void __sched __up(struct semaphore *sem, +struct wake_q_head *wake_q) {struct semaphore_waiter *waiter =list_first_entry (&sem->wait_list, struct semaphore_waiter, list);list_del (&waiter->list); waiter->up = true; -wake_up_process (waiter->task); +wake_q_add (wake_q, waiter->task); }
Signature · 1 line
2.47.1
[tip: locking/core] locking/semaphore: Use wake_q to wake up processes outside lock critical section
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: 85b2b9c16d053364e2004883140538e73b333cdb Gitweb: https://git.kernel.org/tip/85b2b9c16d053364e2004883140538e73b333cdb Author: Waiman Long <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:52 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:52:01 +01:00
locking/semaphore: Use wake_q to wake up processes outside lock critical section
A circular lock dependency splat has been seen involving down_trylock():
======================================================
WARNING: possible circular locking dependency detected 6.12.0-41.el10.s390x+debug
------------------------------------------------------
dd/32479 is trying to acquire lock:
0015a20accd0d4f8 ((console_sem).lock){-.-.}-{2:2}, at: down_trylock+0x26/0x90
but task is already holding lock:
000000017e461698 (&zone->lock){-.-.}-{2:2}, at: rmqueue_bulk+0xac/0x8f0
the existing dependency chain (in reverse order) is:
-> #4 (&zone->lock){-.-.}-{2:2}:
-> #3 (hrtimer_bases.lock){-.-.}-{2:2}:
-> #2 (&rq->__lock){-.-.}-{2:2}:
-> #1 (&p->pi_lock){-.-.}-{2:2}:
-> #0 ((console_sem).lock){-.-.}-{2:2}:
The console_sem -> pi_lock dependency is due to calling try_to_wake_up() while holding the console_sem raw_spinlock. This dependency can be broken by using wake_q to do the wakeup instead of calling try_to_wake_up() under the console_sem lock. This will also make the semaphore's raw_spinlock become a terminal lock without taking any further locks underneath it.
The hrtimer_bases.lock is a raw_spinlock while zone->lock is a spinlock. The hrtimer_bases.lock -> zone->lock dependency happens via the debug_objects_fill_pool() helper function in the debugobjects code.
-> #4 (&zone->lock){-.-.}-{2:2}:
__lock_acquire+0xe86/0x1cc0
lock_acquire.part.0+0x258/0x630
lock_acquire+0xb8/0xe0
_raw_spin_lock_irqsave+0xb4/0x120
rmqueue_bulk+0xac/0x8f0
__rmqueue_pcplist+0x580/0x830
rmqueue_pcplist+0xfc/0x470
rmqueue.isra.0+0xdec/0x11b0
get_page_from_freelist+0x2ee/0xeb0
__alloc_pages_noprof+0x2c2/0x520
alloc_pages_mpol_noprof+0x1fc/0x4d0
alloc_pages_noprof+0x8c/0xe0
allocate_slab+0x320/0x460
___slab_alloc+0xa58/0x12b0
__slab_alloc.isra.0+0x42/0x60
kmem_cache_alloc_noprof+0x304/0x350
fill_pool+0xf6/0x450
debug_object_activate+0xfe/0x360
enqueue_hrtimer+0x34/0x190
__run_hrtimer+0x3c8/0x4c0
__hrtimer_run_queues+0x1b2/0x260
hrtimer_interrupt+0x316/0x760
do_IRQ+0x9a/0xe0
do_irq_async+0xf6/0x160Normally a raw_spinlock to spinlock dependency is not legitimate and will be warned if CONFIG_PROVE_RAW_LOCK_NESTING is enabled, but debug_objects_fill_pool() is an exception as it explicitly allows this dependency for non-PREEMPT_RT kernel without causing PROVE_RAW_LOCK_NESTING lockdep splat. As a result, this dependency is legitimate and not a bug.
Anyway, semaphore is the only locking primitive left that is still using try_to_wake_up() to do wakeup inside critical section, all the other locking primitives had been migrated to use wake_q to do wakeup outside of the critical section. It is also possible that there are other circular locking dependencies involving printk/console_sem or other existing/new semaphores lurking somewhere which may show up in the future. Let just do the migration now to wake_q to avoid headache like this.
--- kernel/locking/semaphore.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
kernel/locking/semaphore.c +9 -4
@@ -29,6 +29,7 @@#include <linux/export.h> #include <linux/sched.h> #include <linux/sched/debug.h> +#include <linux/sched/wake_q.h> #include <linux/semaphore.h> #include <linux/spinlock.h> #include <linux/ftrace.h>
@@ -38,7 +39,7 @@ static noinline void __down(struct semaphore *sem); static noinline int __down_interruptible(struct semaphore *sem); static noinline int __down_killable(struct semaphore *sem); static noinline int __down_timeout(struct semaphore *sem, long timeout); -static noinline void __up(struct semaphore *sem); +static noinline void __up(struct semaphore *sem, struct wake_q_head *wake_q);/** * down - acquire the semaphore
@@ -183,13 +184,16 @@ EXPORT_SYMBOL(down_timeout); void __sched up(struct semaphore *sem) {unsigned long flags; +DEFINE_WAKE_Q (wake_q);raw_spin_lock_irqsave (&sem->lock, flags);if (likely (list_empty (&sem->wait_list))) sem->count++;else -__up (sem); +__up (sem, &wake_q);raw_spin_unlock_irqrestore (&sem->lock, flags); +if (!wake_q_empty (&wake_q)) +wake_up_q (&wake_q); }EXPORT_SYMBOL (up);
@@ -269,11 +273,12 @@ static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)return __down_common (sem,TASK_UNINTERRUPTIBLE , timeout); } -static noinline void __sched __up(struct semaphore *sem) +static noinline void __sched __up(struct semaphore *sem, +struct wake_q_head *wake_q) {struct semaphore_waiter *waiter =list_first_entry (&sem->wait_list, struct semaphore_waiter, list);list_del (&waiter->list); waiter->up = true; -wake_up_process (waiter->task); +wake_q_add (wake_q, waiter->task); }
PATCH 3/11 locking/lock_events: Add locking events for rtmutex slow paths
In reply to Boqun FengFrom: Waiman Long <[email protected]>
Add locking events for rtlock_slowlock() and rt_mutex_slowlock() for profiling the slow path behavior of rt_spin_lock() and rt_mutex_lock().
--- kernel/locking/lock_events_list.h | 21 +++++++++++++++++++++ kernel/locking/rtmutex.c | 29 ++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-)
kernel/locking/lock_events_list.h +21 -0
@@ -67,3 +67,24 @@ LOCK_EVENT(rwsem_rlock_handoff) /* # of read lock handoffs */ LOCK_EVENT(rwsem_wlock) /* # of write locks acquired */ LOCK_EVENT(rwsem_wlock_fail) /* # of failed write lock acquisitions */ LOCK_EVENT(rwsem_wlock_handoff) /* # of write lock handoffs */ + +/* + * Locking events for rtlock_slowlock() + */ +LOCK_EVENT(rtlock_slowlock) /* # of rtlock_slowlock() calls */ +LOCK_EVENT(rtlock_slow_acq1) /* # of locks acquired after wait_lock */ +LOCK_EVENT(rtlock_slow_acq2) /* # of locks acquired in for loop */ +LOCK_EVENT(rtlock_slow_sleep) /* # of sleeps */ +LOCK_EVENT(rtlock_slow_wake) /* # of wakeup's */ + +/* + * Locking events for rt_mutex_slowlock() + */ +LOCK_EVENT(rtmutex_slowlock) /* # of rt_mutex_slowlock() calls */ +LOCK_EVENT(rtmutex_slow_block) /* # of rt_mutex_slowlock_block() calls */ +LOCK_EVENT(rtmutex_slow_acq1) /* # of locks acquired after wait_lock */ +LOCK_EVENT(rtmutex_slow_acq2) /* # of locks acquired at the end */ +LOCK_EVENT(rtmutex_slow_acq3) /* # of locks acquired in *block() */ +LOCK_EVENT(rtmutex_slow_sleep) /* # of sleeps */ +LOCK_EVENT(rtmutex_slow_wake) /* # of wakeup's */ +LOCK_EVENT(rtmutex_deadlock) /* # of rt_mutex_handle_deadlock()'s */
kernel/locking/rtmutex.c +24 -5
@@ -27,6 +27,7 @@#include <trace/events/lock.h> #include "rtmutex_common.h" +#include "lock_events.h" #ifndef WW_RT # define build_ww_mutex () (false)
@@ -1612,10 +1613,13 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,struct task_struct *owner;int ret =0 ; +lockevent_inc (rtmutex_slow_block);for (;;) {/* Try to acquire the lock: */ -if (try_to_take_rt_mutex (lock, current, waiter)) +if (try_to_take_rt_mutex (lock, current, waiter)) { +lockevent_inc (rtmutex_slow_acq3);break ; + }if (timeout && !timeout->task) { ret = -ETIMEDOUT ;
@@ -1638,8 +1642,10 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, owner =NULL ;raw_spin_unlock_irq_wake (&lock->wait_lock, wake_q); -if (!owner || !rtmutex_spin_on_owner (lock, waiter, owner)) +if (!owner || !rtmutex_spin_on_owner (lock, waiter, owner)) { +lockevent_inc (rtmutex_slow_sleep);rt_mutex_schedule (); + }raw_spin_lock_irq (&lock->wait_lock);set_current_state (state);
@@ -1694,6 +1700,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,int ret;lockdep_assert_held (&lock->wait_lock); +lockevent_inc (rtmutex_slowlock);/* Try to acquire the lock again: */ if (try_to_take_rt_mutex (lock, current,NULL )) {
@@ -1701,6 +1708,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,__ww_mutex_check_waiters (rtm, ww_ctx, wake_q);ww_mutex_lock_acquired (ww, ww_ctx); } +lockevent_inc (rtmutex_slow_acq1);return 0 ; }
@@ -1719,10 +1727,12 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,__ww_mutex_check_waiters (rtm, ww_ctx, wake_q);ww_mutex_lock_acquired (ww, ww_ctx); } +lockevent_inc (rtmutex_slow_acq2); }else {__set_current_state (TASK_RUNNING );remove_waiter (lock, waiter);rt_mutex_handle_deadlock (ret, chwalk, lock, waiter); +lockevent_inc (rtmutex_deadlock); }/*
@@ -1751,6 +1761,7 @@ static inline int __rt_mutex_slowlock_locked(struct rt_mutex_base *lock, &waiter, wake_q);debug_rt_mutex_free_waiter (&waiter); +lockevent_cond_inc (rtmutex_slow_wake, !wake_q_empty (wake_q));return ret; }
@@ -1823,9 +1834,12 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,struct task_struct *owner;lockdep_assert_held (&lock->wait_lock); +lockevent_inc (rtlock_slowlock); -if (try_to_take_rt_mutex (lock, current,NULL )) +if (try_to_take_rt_mutex (lock, current,NULL )) { +lockevent_inc (rtlock_slow_acq1);return ; + }rt_mutex_init_rtlock_waiter (&waiter);
@@ -1838,8 +1852,10 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,for (;;) {/* Try to acquire the lock again */ -if (try_to_take_rt_mutex (lock, current, &waiter)) +if (try_to_take_rt_mutex (lock, current, &waiter)) { +lockevent_inc (rtlock_slow_acq2);break ; + }if (&waiter ==rt_mutex_top_waiter (lock)) owner =rt_mutex_owner (lock);
@@ -1847,8 +1863,10 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock, owner =NULL ;raw_spin_unlock_irq_wake (&lock->wait_lock, wake_q); -if (!owner || !rtmutex_spin_on_owner (lock, &waiter, owner)) +if (!owner || !rtmutex_spin_on_owner (lock, &waiter, owner)) { +lockevent_inc (rtlock_slow_sleep);schedule_rtlock (); + }raw_spin_lock_irq (&lock->wait_lock);set_current_state (TASK_RTLOCK_WAIT );
@@ -1865,6 +1883,7 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,debug_rt_mutex_free_waiter (&waiter);trace_contention_end (lock,0 ); +lockevent_cond_inc (rtlock_slow_wake, !wake_q_empty (wake_q)); } static __always_inline void __sched rtlock_slowlock(struct rt_mutex_base *lock)
Signature · 1 line
2.47.1
[tip: locking/core] locking/lock_events: Add locking events for rtmutex slow paths
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: b76b44fb656100fcd8482d9102d35d1b6a5129e9 Gitweb: https://git.kernel.org/tip/b76b44fb656100fcd8482d9102d35d1b6a5129e9 Author: Waiman Long <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:53 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:03 +01:00
locking/lock_events: Add locking events for rtmutex slow paths
Add locking events for rtlock_slowlock() and rt_mutex_slowlock() for profiling the slow path behavior of rt_spin_lock() and rt_mutex_lock().
--- kernel/locking/lock_events_list.h | 21 +++++++++++++++++++++ kernel/locking/rtmutex.c | 29 ++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-)
kernel/locking/lock_events_list.h +21 -0
@@ -67,3 +67,24 @@ LOCK_EVENT(rwsem_rlock_handoff) /* # of read lock handoffs */ LOCK_EVENT(rwsem_wlock) /* # of write locks acquired */ LOCK_EVENT(rwsem_wlock_fail) /* # of failed write lock acquisitions */ LOCK_EVENT(rwsem_wlock_handoff) /* # of write lock handoffs */ + +/* + * Locking events for rtlock_slowlock() + */ +LOCK_EVENT(rtlock_slowlock) /* # of rtlock_slowlock() calls */ +LOCK_EVENT(rtlock_slow_acq1) /* # of locks acquired after wait_lock */ +LOCK_EVENT(rtlock_slow_acq2) /* # of locks acquired in for loop */ +LOCK_EVENT(rtlock_slow_sleep) /* # of sleeps */ +LOCK_EVENT(rtlock_slow_wake) /* # of wakeup's */ + +/* + * Locking events for rt_mutex_slowlock() + */ +LOCK_EVENT(rtmutex_slowlock) /* # of rt_mutex_slowlock() calls */ +LOCK_EVENT(rtmutex_slow_block) /* # of rt_mutex_slowlock_block() calls */ +LOCK_EVENT(rtmutex_slow_acq1) /* # of locks acquired after wait_lock */ +LOCK_EVENT(rtmutex_slow_acq2) /* # of locks acquired at the end */ +LOCK_EVENT(rtmutex_slow_acq3) /* # of locks acquired in *block() */ +LOCK_EVENT(rtmutex_slow_sleep) /* # of sleeps */ +LOCK_EVENT(rtmutex_slow_wake) /* # of wakeup's */ +LOCK_EVENT(rtmutex_deadlock) /* # of rt_mutex_handle_deadlock()'s */
kernel/locking/rtmutex.c +24 -5
@@ -27,6 +27,7 @@#include <trace/events/lock.h> #include "rtmutex_common.h" +#include "lock_events.h" #ifndef WW_RT # define build_ww_mutex () (false)
@@ -1612,10 +1613,13 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,struct task_struct *owner;int ret =0 ; +lockevent_inc (rtmutex_slow_block);for (;;) {/* Try to acquire the lock: */ -if (try_to_take_rt_mutex (lock, current, waiter)) +if (try_to_take_rt_mutex (lock, current, waiter)) { +lockevent_inc (rtmutex_slow_acq3);break ; + }if (timeout && !timeout->task) { ret = -ETIMEDOUT ;
@@ -1638,8 +1642,10 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, owner =NULL ;raw_spin_unlock_irq_wake (&lock->wait_lock, wake_q); -if (!owner || !rtmutex_spin_on_owner (lock, waiter, owner)) +if (!owner || !rtmutex_spin_on_owner (lock, waiter, owner)) { +lockevent_inc (rtmutex_slow_sleep);rt_mutex_schedule (); + }raw_spin_lock_irq (&lock->wait_lock);set_current_state (state);
@@ -1694,6 +1700,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,int ret;lockdep_assert_held (&lock->wait_lock); +lockevent_inc (rtmutex_slowlock);/* Try to acquire the lock again: */ if (try_to_take_rt_mutex (lock, current,NULL )) {
@@ -1701,6 +1708,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,__ww_mutex_check_waiters (rtm, ww_ctx, wake_q);ww_mutex_lock_acquired (ww, ww_ctx); } +lockevent_inc (rtmutex_slow_acq1);return 0 ; }
@@ -1719,10 +1727,12 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,__ww_mutex_check_waiters (rtm, ww_ctx, wake_q);ww_mutex_lock_acquired (ww, ww_ctx); } +lockevent_inc (rtmutex_slow_acq2); }else {__set_current_state (TASK_RUNNING );remove_waiter (lock, waiter);rt_mutex_handle_deadlock (ret, chwalk, lock, waiter); +lockevent_inc (rtmutex_deadlock); }/*
@@ -1751,6 +1761,7 @@ static inline int __rt_mutex_slowlock_locked(struct rt_mutex_base *lock, &waiter, wake_q);debug_rt_mutex_free_waiter (&waiter); +lockevent_cond_inc (rtmutex_slow_wake, !wake_q_empty (wake_q));return ret; }
@@ -1823,9 +1834,12 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,struct task_struct *owner;lockdep_assert_held (&lock->wait_lock); +lockevent_inc (rtlock_slowlock); -if (try_to_take_rt_mutex (lock, current,NULL )) +if (try_to_take_rt_mutex (lock, current,NULL )) { +lockevent_inc (rtlock_slow_acq1);return ; + }rt_mutex_init_rtlock_waiter (&waiter);
@@ -1838,8 +1852,10 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,for (;;) {/* Try to acquire the lock again */ -if (try_to_take_rt_mutex (lock, current, &waiter)) +if (try_to_take_rt_mutex (lock, current, &waiter)) { +lockevent_inc (rtlock_slow_acq2);break ; + }if (&waiter ==rt_mutex_top_waiter (lock)) owner =rt_mutex_owner (lock);
@@ -1847,8 +1863,10 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock, owner =NULL ;raw_spin_unlock_irq_wake (&lock->wait_lock, wake_q); -if (!owner || !rtmutex_spin_on_owner (lock, &waiter, owner)) +if (!owner || !rtmutex_spin_on_owner (lock, &waiter, owner)) { +lockevent_inc (rtlock_slow_sleep);schedule_rtlock (); + }raw_spin_lock_irq (&lock->wait_lock);set_current_state (TASK_RTLOCK_WAIT );
@@ -1865,6 +1883,7 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock,debug_rt_mutex_free_waiter (&waiter);trace_contention_end (lock,0 ); +lockevent_cond_inc (rtlock_slow_wake, !wake_q_empty (wake_q)); } static __always_inline void __sched rtlock_slowlock(struct rt_mutex_base *lock)
PATCH 4/11 locking/lock_events: Add locking events for lockdep
In reply to Boqun FengFrom: Waiman Long <[email protected]>
Add some lock events to the lockdep for profiling its behavior.
--- kernel/locking/lock_events_list.h | 7 +++++++ kernel/locking/lockdep.c | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-)
kernel/locking/lock_events_list.h +7 -0
@@ -88,3 +88,10 @@ LOCK_EVENT(rtmutex_slow_acq3) /* # of locks acquired in *block() */ LOCK_EVENT(rtmutex_slow_sleep) /* # of sleeps */ LOCK_EVENT(rtmutex_slow_wake) /* # of wakeup's */ LOCK_EVENT(rtmutex_deadlock) /* # of rt_mutex_handle_deadlock()'s */ + +/* + * Locking events for lockdep + */ +LOCK_EVENT(lockdep_acquire) +LOCK_EVENT(lockdep_lock) +LOCK_EVENT(lockdep_nocheck)
kernel/locking/lockdep.c +7 -1
@@ -61,6 +61,7 @@#include <asm/sections.h> #include "lockdep_internals.h" +#include "lock_events.h" #include <trace/events/lock.h>
@@ -170,6 +171,7 @@ static struct task_struct *lockdep_selftest_task_struct;static int graph_lock (void ) {lockdep_lock (); +lockevent_inc (lockdep_lock);/* * Make sure that if another CPU detected a bug while * walking the graph we dont change it (while the other
@@ -5091,8 +5093,12 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (unlikely (lock->key == &__lockdep_no_track__))return 0 ; -if (!prove_locking || lock->key == &__lockdep_no_validate__) +lockevent_inc (lockdep_acquire); + +if (!prove_locking || lock->key == &__lockdep_no_validate__) { check =0 ; +lockevent_inc (lockdep_nocheck); + }if (subclass <NR_LOCKDEP_CACHING_CLASSES ) class = lock->class_cache[subclass];
Signature · 1 line
2.47.1
[tip: locking/core] locking/lock_events: Add locking events for lockdep
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: a94d32446ab5e85c7ba97e48a8c9bd85be4fe889 Gitweb: https://git.kernel.org/tip/a94d32446ab5e85c7ba97e48a8c9bd85be4fe889 Author: Waiman Long <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:54 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:03 +01:00
locking/lock_events: Add locking events for lockdep
Add some lock events to lockdep to profile its behavior.
--- kernel/locking/lock_events_list.h | 7 +++++++ kernel/locking/lockdep.c | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-)
kernel/locking/lock_events_list.h +7 -0
@@ -88,3 +88,10 @@ LOCK_EVENT(rtmutex_slow_acq3) /* # of locks acquired in *block() */ LOCK_EVENT(rtmutex_slow_sleep) /* # of sleeps */ LOCK_EVENT(rtmutex_slow_wake) /* # of wakeup's */ LOCK_EVENT(rtmutex_deadlock) /* # of rt_mutex_handle_deadlock()'s */ + +/* + * Locking events for lockdep + */ +LOCK_EVENT(lockdep_acquire) +LOCK_EVENT(lockdep_lock) +LOCK_EVENT(lockdep_nocheck)
kernel/locking/lockdep.c +7 -1
@@ -61,6 +61,7 @@#include <asm/sections.h> #include "lockdep_internals.h" +#include "lock_events.h" #include <trace/events/lock.h>
@@ -170,6 +171,7 @@ static struct task_struct *lockdep_selftest_task_struct;static int graph_lock (void ) {lockdep_lock (); +lockevent_inc (lockdep_lock);/* * Make sure that if another CPU detected a bug while * walking the graph we dont change it (while the other
@@ -5091,8 +5093,12 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (unlikely (lock->key == &__lockdep_no_track__))return 0 ; -if (!prove_locking || lock->key == &__lockdep_no_validate__) +lockevent_inc (lockdep_acquire); + +if (!prove_locking || lock->key == &__lockdep_no_validate__) { check =0 ; +lockevent_inc (lockdep_nocheck); + }if (subclass <NR_LOCKDEP_CACHING_CLASSES ) class = lock->class_cache[subclass];
PATCH 5/11 locking/lockdep: Disable KASAN instrumentation of lockdep.c
In reply to Boqun FengFrom: Waiman Long <[email protected]>
Both KASAN and LOCKDEP are commonly enabled in building a debug kernel. Each of them can significantly slow down the speed of a debug kernel. Enabling KASAN instrumentation of the LOCKDEP code will further slow thing down.
Since LOCKDEP is a high overhead debugging tool, it will never get enabled in a production kernel. The LOCKDEP code is also pretty mature and is unlikely to get major changes. There is also a possibility of recursion similar to KCSAN.
To evaluate the performance impact of disabling KASAN instrumentation of lockdep.c, the time to do a parallel build of the Linux defconfig kernel was used as the benchmark. Two x86-64 systems (Skylake & Zen 2) and an arm64 system were used as test beds. Two sets of non-RT and RT kernels with similar configurations except mainly CONFIG_PREEMPT_RT were used for evaulation.
For the Skylake system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 0m47.642s 4m19.811s
[CONFIG_KASAN_INLINE=y]
Debug kernel 2m11.108s (x2.8) 38m20.467s (x8.9)
Debug kernel (patched) 1m49.602s (x2.3) 31m28.501s (x7.3)
Debug kernel
(patched + mitigations=off) 1m30.988s (x1.9) 26m41.993s (x6.2)
RT kernel (baseline) 0m54.871s 7m15.340s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 6m07.151s (x6.7) 135m47.428s (x18.7)
RT debug kernel (patched) 3m42.434s (x4.1) 74m51.636s (x10.3)
RT debug kernel
(patched + mitigations=off) 2m40.383s (x2.9) 57m54.369s (x8.0)
[CONFIG_KASAN_INLINE=y]
RT debug kernel 3m22.155s (x3.7) 77m53.018s (x10.7)
RT debug kernel (patched) 2m36.700s (x2.9) 54m31.195s (x7.5)
RT debug kernel
(patched + mitigations=off) 2m06.110s (x2.3) 45m49.493s (x6.3)
For the Zen 2 system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 1m42.806s 39m48.714s
[CONFIG_KASAN_INLINE=y]
Debug kernel 4m04.524s (x2.4) 125m35.904s (x3.2)
Debug kernel (patched) 3m56.241s (x2.3) 127m22.378s (x3.2)
Debug kernel
(patched + mitigations=off) 2m38.157s (x1.5) 92m35.680s (x2.3)
RT kernel (baseline) 1m51.500s 14m56.322s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 16m04.962s (x8.7) 244m36.463s (x16.4)
RT debug kernel (patched) 9m09.073s (x4.9) 129m28.439s (x8.7)
RT debug kernel
(patched + mitigations=off) 3m31.662s (x1.9) 51m01.391s (x3.4)
For the arm64 system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 1m56.844s 8m47.150s
Debug kernel 3m54.774s (x2.0) 92m30.098s (x10.5)
Debug kernel (patched) 3m32.429s (x1.8) 77m40.779s (x8.8)
RT kernel (baseline) 4m01.641s 18m16.777s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 19m32.977s (x4.9) 304m23.965s (x16.7)
RT debug kernel (patched) 16m28.354s (x4.1) 234m18.149s (x12.8)
Turning the mitigations off doesn't seems to have any noticeable impact on the performance of the arm64 system. So the mitigation=off entries aren't included.
For the x86 CPUs, cpu mitigations has a much bigger
impact on performance, especially the RT debug kernel with CONFIG_KASAN_INLINE=n. The SRSO mitigation in Zen 2 has an especially big impact on the debug kernel. It is also the majority of the slowdown with mitigations on. It is because the patched ret instruction slows down function returns. A lot of helper functions that are normally compiled out or inlined may become real function calls in the debug kernel.
With CONFIG_KASAN_INLINE=n, the KASAN instrumentation inserts a lot of __asan_loadX*() and __kasan_check_read() function calls to memory access portion of the code. The lockdep's __lock_acquire() function, for instance, has 66 __asan_loadX*() and 6 __kasan_check_read() calls added with KASAN instrumentation. Of course, the actual numbers may vary depending on the compiler used and the exact version of the lockdep code.
With the Skylake test system, the parallel kernel build times reduction of the RT debug kernel with this patch are:
CONFIG_KASAN_INLINE=n: -37%
CONFIG_KASAN_INLINE=y: -22%
The time reduction is less with CONFIG_KASAN_INLINE=y, but it is still significant.
Setting CONFIG_KASAN_INLINE=y can result in a significant performance improvement. The major drawback is a significant increase in the size of kernel text. In the case of vmlinux, its text size increases from 45997948 to 67606807. That is a 47% size increase (about 21 Mbytes). The size increase of other kernel modules should be similar.
With the newly added rtmutex and lockdep lock events, the relevant event counts for the test runs with the Skylake system were:
Event type Debug kernel RT debug kernel
---------- ------------ ---------------
lockdep_acquire 1,968,663,277 5,425,313,953
rtlock_slowlock - 401,701,156
rtmutex_slowlock - 139,672
The __lock_acquire() calls in the RT debug kernel are x2.8 times of the non-RT debug kernel with the same workload. Since the __lock_acquire() function is a big hitter in term of performance slowdown, this makes the RT debug kernel much slower than the non-RT one. The average lock nesting depth is likely to be higher in the RT debug kernel too leading to longer execution time in the __lock_acquire() function.
As the small advantage of enabling KASAN instrumentation to catch potential memory access error in the lockdep debugging tool is probably not worth the drawback of further slowing down a debug kernel, disable KASAN instrumentation in the lockdep code to allow the debug kernels to regain some performance back, especially for the RT debug kernels.
--- kernel/locking/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/locking/Makefile +2 -1
@@ -5,7 +5,8 @@ KCOV_INSTRUMENT := n obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o -# Avoid recursion lockdep -> sanitizer -> ... -> lockdep. +# Avoid recursion lockdep -> sanitizer -> ... -> lockdep & improve performance. +KASAN_SANITIZE_lockdep.o := n KCSAN_SANITIZE_lockdep.o := n ifdef CONFIG_FUNCTION_TRACER
Signature · 1 line
2.47.1
[tip: locking/core] locking/lockdep: Disable KASAN instrumentation of lockdep.c
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: ee57ab5a32129f599ee1d358548dbebcb5e45953 Gitweb: https://git.kernel.org/tip/ee57ab5a32129f599ee1d358548dbebcb5e45953 Author: Waiman Long <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:55 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:03 +01:00
locking/lockdep: Disable KASAN instrumentation of lockdep.c
Both KASAN and LOCKDEP are commonly enabled in building a debug kernel. Each of them can significantly slow down the speed of a debug kernel. Enabling KASAN instrumentation of the LOCKDEP code will further slow things down.
Since LOCKDEP is a high overhead debugging tool, it will never get enabled in a production kernel. The LOCKDEP code is also pretty mature and is unlikely to get major changes. There is also a possibility of recursion similar to KCSAN.
To evaluate the performance impact of disabling KASAN instrumentation of lockdep.c, the time to do a parallel build of the Linux defconfig kernel was used as the benchmark. Two x86-64 systems (Skylake & Zen 2) and an arm64 system were used as test beds. Two sets of non-RT and RT kernels with similar configurations except mainly CONFIG_PREEMPT_RT were used for evaluation.
For the Skylake system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 0m47.642s 4m19.811s
[CONFIG_KASAN_INLINE=y]
Debug kernel 2m11.108s (x2.8) 38m20.467s (x8.9)
Debug kernel (patched) 1m49.602s (x2.3) 31m28.501s (x7.3)
Debug kernel
(patched + mitigations=off) 1m30.988s (x1.9) 26m41.993s (x6.2)
RT kernel (baseline) 0m54.871s 7m15.340s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 6m07.151s (x6.7) 135m47.428s (x18.7)
RT debug kernel (patched) 3m42.434s (x4.1) 74m51.636s (x10.3)
RT debug kernel
(patched + mitigations=off) 2m40.383s (x2.9) 57m54.369s (x8.0)
[CONFIG_KASAN_INLINE=y]
RT debug kernel 3m22.155s (x3.7) 77m53.018s (x10.7)
RT debug kernel (patched) 2m36.700s (x2.9) 54m31.195s (x7.5)
RT debug kernel
(patched + mitigations=off) 2m06.110s (x2.3) 45m49.493s (x6.3)
For the Zen 2 system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 1m42.806s 39m48.714s
[CONFIG_KASAN_INLINE=y]
Debug kernel 4m04.524s (x2.4) 125m35.904s (x3.2)
Debug kernel (patched) 3m56.241s (x2.3) 127m22.378s (x3.2)
Debug kernel
(patched + mitigations=off) 2m38.157s (x1.5) 92m35.680s (x2.3)
RT kernel (baseline) 1m51.500s 14m56.322s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 16m04.962s (x8.7) 244m36.463s (x16.4)
RT debug kernel (patched) 9m09.073s (x4.9) 129m28.439s (x8.7)
RT debug kernel
(patched + mitigations=off) 3m31.662s (x1.9) 51m01.391s (x3.4)
For the arm64 system:
Kernel Run time Sys time
------ -------- --------
Non-debug kernel (baseline) 1m56.844s 8m47.150s
Debug kernel 3m54.774s (x2.0) 92m30.098s (x10.5)
Debug kernel (patched) 3m32.429s (x1.8) 77m40.779s (x8.8)
RT kernel (baseline) 4m01.641s 18m16.777s
[CONFIG_KASAN_INLINE=n]
RT debug kernel 19m32.977s (x4.9) 304m23.965s (x16.7)
RT debug kernel (patched) 16m28.354s (x4.1) 234m18.149s (x12.8)
Turning the mitigations off doesn't seems to have any noticeable impact on the performance of the arm64 system. So the mitigation=off entries aren't included.
For the x86 CPUs, CPU mitigations has a much bigger
impact on performance, especially the RT debug kernel with CONFIG_KASAN_INLINE=n. The SRSO mitigation in Zen 2 has an especially big impact on the debug kernel. It is also the majority of the slowdown with mitigations on. It is because the patched RET instruction slows down function returns. A lot of helper functions that are normally compiled out or inlined may become real function calls in the debug kernel.
With !CONFIG_KASAN_INLINE, the KASAN instrumentation inserts a lot of __asan_loadX*() and __kasan_check_read() function calls to memory access portion of the code. The lockdep's __lock_acquire() function, for instance, has 66 __asan_loadX*() and 6 __kasan_check_read() calls added with KASAN instrumentation. Of course, the actual numbers may vary depending on the compiler used and the exact version of the lockdep code.
With the Skylake test system, the parallel kernel build times reduction of the RT debug kernel with this patch are:
CONFIG_KASAN_INLINE=n: -37%
CONFIG_KASAN_INLINE=y: -22%
The time reduction is less with CONFIG_KASAN_INLINE=y, but it is still significant.
Setting CONFIG_KASAN_INLINE=y can result in a significant performance improvement. The major drawback is a significant increase in the size of kernel text. In the case of vmlinux, its text size increases from 45997948 to 67606807. That is a 47% size increase (about 21 Mbytes). The size increase of other kernel modules should be similar.
With the newly added rtmutex and lockdep lock events, the relevant event counts for the test runs with the Skylake system were:
Event type Debug kernel RT debug kernel
---------- ------------ ---------------
lockdep_acquire 1,968,663,277 5,425,313,953
rtlock_slowlock - 401,701,156
rtmutex_slowlock - 139,672
The __lock_acquire() calls in the RT debug kernel are x2.8 times of the non-RT debug kernel with the same workload. Since the __lock_acquire() function is a big hitter in term of performance slowdown, this makes the RT debug kernel much slower than the non-RT one. The average lock nesting depth is likely to be higher in the RT debug kernel too leading to longer execution time in the __lock_acquire() function.
As the small advantage of enabling KASAN instrumentation to catch potential memory access error in the lockdep debugging tool is probably not worth the drawback of further slowing down a debug kernel, disable KASAN instrumentation in the lockdep code to allow the debug kernels to regain some performance back, especially for the RT debug kernels.
--- kernel/locking/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/locking/Makefile +2 -1
@@ -5,7 +5,8 @@ KCOV_INSTRUMENT := n obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o -# Avoid recursion lockdep -> sanitizer -> ... -> lockdep. +# Avoid recursion lockdep -> sanitizer -> ... -> lockdep & improve performance. +KASAN_SANITIZE_lockdep.o := n KCSAN_SANITIZE_lockdep.o := n ifdef CONFIG_FUNCTION_TRACER
PATCH 6/11 locking/lockdep: Add kasan_check_byte() check in lock_acquire()
In reply to Boqun FengFrom: Waiman Long <[email protected]>
KASAN instrumentation of lockdep has been disabled as we don't need KASAN to check the validity of lockdep internal data structures and incur unnecessary performance overhead. However, the lockdep_map pointer passed in externally may not be valid (e.g. use-after-free) and we run the risk of using garbage data resulting in false lockdep reports.
Add kasan_check_byte() call in lock_acquire() for non kernel core data object to catch invalid lockdep_map and print out a KASAN report before any lockdep splat, if any.
--- kernel/locking/lockdep.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/locking/lockdep.c +9 -0
@@ -57,6 +57,7 @@#include <linux/lockdep.h> #include <linux/context_tracking.h> #include <linux/console.h> +#include <linux/kasan.h> #include <asm/sections.h>
@@ -5830,6 +5831,14 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (!debug_locks)return ; +/* +* As KASAN instrumentation is disabled and lock_acquire() is usually +* the first lockdep call when a task tries to acquire a lock, add +* kasan_check_byte() here to check for use-after-free and other +* memory errors. +*/ +kasan_check_byte (lock); +if (unlikely (!lockdep_enabled ())) {/* XXX allow trylock from NMI ?!? */ if (lockdep_nmi () && !trylock) {
Signature · 1 line
2.47.1
[tip: locking/core] locking/lockdep: Add kasan_check_byte() check in lock_acquire()
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: de4b59d652646cf00cf632174348ca2266099edc Gitweb: https://git.kernel.org/tip/de4b59d652646cf00cf632174348ca2266099edc Author: Waiman Long <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:56 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:04 +01:00
locking/lockdep: Add kasan_check_byte() check in lock_acquire()
KASAN instrumentation of lockdep has been disabled, as we don't need KASAN to check the validity of lockdep internal data structures and incur unnecessary performance overhead. However, the lockdep_map pointer passed in externally may not be valid (e.g. use-after-free) and we run the risk of using garbage data resulting in false lockdep reports.
Add kasan_check_byte() call in lock_acquire() for non kernel core data object to catch invalid lockdep_map and print out a KASAN report before any lockdep splat, if any.
--- kernel/locking/lockdep.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/locking/lockdep.c +9 -0
@@ -57,6 +57,7 @@#include <linux/lockdep.h> #include <linux/context_tracking.h> #include <linux/console.h> +#include <linux/kasan.h> #include <asm/sections.h>
@@ -5830,6 +5831,14 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (!debug_locks)return ; +/* +* As KASAN instrumentation is disabled and lock_acquire() is usually +* the first lockdep call when a task tries to acquire a lock, add +* kasan_check_byte() here to check for use-after-free and other +* memory errors. +*/ +kasan_check_byte (lock); +if (unlikely (!lockdep_enabled ())) {/* XXX allow trylock from NMI ?!? */ if (lockdep_nmi () && !trylock) {
PATCH 7/11 rust: sync: Add accessor for the lock behind a given guard
In reply to Boqun FengFrom: Alice Ryhl <[email protected]>
In order to assert a particular Guard is associated with a particular Lock, add an accessor to obtain a reference to the underlying Lock of a Guard.
Binder needs this assertion to ensure unsafe list operations are done with the correct lock held.
[Boqun: Capitalize the title and reword the commit log]
--- rust/kernel/sync/lock.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
rust/kernel/sync/lock.rs +6 -1
@@ -199,7 +199,12 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {// SAFETY: `Guard` is sync when the data protected by the lock is also sync. unsafe impl <T :Sync + ?Sized ,B :Backend >Sync for Guard <'_,T ,B > {} -impl <T : ?Sized ,B :Backend >Guard <'_,T ,B > { +impl <'a,T : ?Sized ,B :Backend >Guard <'a,T ,B > { +/// Returns the lock that this guard originates from. +pub fn lock_ref (&self) -> &'aLock <T ,B > { + self.lock + } +pub (crate )fn do_unlocked <U >(&mut self, cb:impl FnOnce () ->U ) ->U {// SAFETY: The caller owns the lock, so it is safe to unlock it. unsafe {B ::unlock (self.lock.state.get (), &self.state) };
Signature · 1 line
2.47.1
[tip: locking/core] rust: sync: Add accessor for the lock behind a given guard
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: 8f65291dae0e75941cf76e427088e5612c4d692e Gitweb: https://git.kernel.org/tip/8f65291dae0e75941cf76e427088e5612c4d692e Author: Alice Ryhl <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:57 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:04 +01:00
rust: sync: Add accessor for the lock behind a given guard
In order to assert a particular Guard is associated with a particular Lock, add an accessor to obtain a reference to the underlying Lock of a Guard.
Binder needs this assertion to ensure unsafe list operations are done with the correct lock held.
[Boqun: Capitalize the title and reword the commit log]
--- rust/kernel/sync/lock.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
rust/kernel/sync/lock.rs +6 -1
@@ -199,7 +199,12 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {// SAFETY: `Guard` is sync when the data protected by the lock is also sync. unsafe impl <T :Sync + ?Sized ,B :Backend >Sync for Guard <'_,T ,B > {} -impl <T : ?Sized ,B :Backend >Guard <'_,T ,B > { +impl <'a,T : ?Sized ,B :Backend >Guard <'a,T ,B > { +/// Returns the lock that this guard originates from. +pub fn lock_ref (&self) -> &'aLock <T ,B > { + self.lock + } +pub (crate )fn do_unlocked <U >(&mut self, cb:impl FnOnce () ->U ) ->U {// SAFETY: The caller owns the lock, so it is safe to unlock it. unsafe {B ::unlock (self.lock.state.get (), &self.state) };
PATCH 8/11 rust: sync: lock: Add an example for Guard::lock_ref()
In reply to Boqun FengTo provide examples on usage of Guard::lock_ref() along with the unit test, an "assert a lock is held by a guard" example is added.
[boqun: Apply feedback from Benno]
--- rust/kernel/sync/lock.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
rust/kernel/sync/lock.rs +24 -0
@@ -201,6 +201,30 @@ unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}impl <'a,T : ?Sized ,B :Backend >Guard <'a,T ,B > {/// Returns the lock that this guard originates from. +/// +/// # Examples +/// +/// The following example shows how to use [`Guard::lock_ref()`] to assert the corresponding +/// lock is held. +/// +/// ``` +/// # use kernel::{new_spinlock, stack_pin_init, sync::lock::{Backend, Guard, Lock}}; +/// +/// fn assert_held<T, B: Backend>(guard: &Guard<'_, T, B>, lock: &Lock<T, B>) { +/// // Address-equal means the same lock. +/// assert!(core::ptr::eq(guard.lock_ref(), lock)); +/// } +/// +/// // Creates a new lock on the stack. +/// stack_pin_init!{ +/// let l = new_spinlock!(42) +/// } +/// +/// let g = l.lock(); +/// +/// // `g` originates from `l`. +/// assert_held(&g, &l); +/// ``` pub fn lock_ref (&self) -> &'aLock <T ,B > { self.lock }
Signature · 1 line
2.47.1
[tip: locking/core] rust: sync: lock: Add an example for Guard:: Lock_ref()
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: c2849afafd08a1c3ad881129de48ebe1642f7675 Gitweb: https://git.kernel.org/tip/c2849afafd08a1c3ad881129de48ebe1642f7675 Author: Boqun Feng <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:58 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:04 +01:00
rust: sync: lock: Add an example for Guard:: Lock_ref()
To provide examples on usage of Guard::lock_ref() along with the unit test, an "assert a lock is held by a guard" example is added.
(Also apply feedback from Benno.)
--- rust/kernel/sync/lock.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
rust/kernel/sync/lock.rs +24 -0
@@ -201,6 +201,30 @@ unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}impl <'a,T : ?Sized ,B :Backend >Guard <'a,T ,B > {/// Returns the lock that this guard originates from. +/// +/// # Examples +/// +/// The following example shows how to use [`Guard::lock_ref()`] to assert the corresponding +/// lock is held. +/// +/// ``` +/// # use kernel::{new_spinlock, stack_pin_init, sync::lock::{Backend, Guard, Lock}}; +/// +/// fn assert_held<T, B: Backend>(guard: &Guard<'_, T, B>, lock: &Lock<T, B>) { +/// // Address-equal means the same lock. +/// assert!(core::ptr::eq(guard.lock_ref(), lock)); +/// } +/// +/// // Creates a new lock on the stack. +/// stack_pin_init!{ +/// let l = new_spinlock!(42) +/// } +/// +/// let g = l.lock(); +/// +/// // `g` originates from `l`. +/// assert_held(&g, &l); +/// ``` pub fn lock_ref (&self) -> &'aLock <T ,B > { self.lock }
PATCH 9/11 rust: sync: condvar: Add wait_interruptible_freezable()
In reply to Boqun FengFrom: Alice Ryhl <[email protected]>
To support waiting for a CondVar as a freezable process, add a wait_interruptible_freezable() function.
Binder needs this function in the appropriate places to freeze a process where some of its threads are blocked on the Binder driver.
[Boqun: Capitalize the title, reword the commit log and rephrase the function doc comment with the impersonal style to align with rest of the file]
--- rust/kernel/sync/condvar.rs | 23 ++++++++++++++++++++++- rust/kernel/task.rs | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-)
rust/kernel/sync/condvar.rs +22 -1
@@ -11,7 +11,9 @@ init::PinInit, pin_init, str::CStr, - task::{MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE}, + task::{ + MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE, + }, time::Jiffies, types::Opaque, };
@@ -159,6 +161,25 @@ pub fn wait_interruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T,crate ::current!().signal_pending () } +/// Releases the lock and waits for a notification in interruptible and freezable mode. +/// +/// The process is allowed to be frozen during this sleep. No lock should be held when calling +/// this function, and there is a lockdep assertion for this. Freezing a task that holds a lock +/// can trivially deadlock vs another task that needs that lock to complete before it too can +/// hit freezable. +#[must_use = "wait_interruptible_freezable returns if a signal is pending, so the caller must check the return value" ] +pub fn wait_interruptible_freezable <T : ?Sized ,B :Backend >( + &self, + guard: &mut Guard <'_,T ,B >, + ) ->bool { + self.wait_internal ( +TASK_INTERRUPTIBLE |TASK_FREEZABLE , + guard, +MAX_SCHEDULE_TIMEOUT , + ); +crate ::current!().signal_pending () + } +/// Releases the lock and waits for a notification in interruptible mode. /// /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
rust/kernel/task.rs +2 -0
@@ -23,6 +23,8 @@pub const TASK_INTERRUPTIBLE :c_int = bindings::TASK_INTERRUPTIBLE as c_int ;/// Bitmask for tasks that are sleeping in an uninterruptible state. pub const TASK_UNINTERRUPTIBLE :c_int = bindings::TASK_UNINTERRUPTIBLE as c_int ; +/// Bitmask for tasks that are sleeping in a freezable state. +pub const TASK_FREEZABLE :c_int = bindings::TASK_FREEZABLE as c_int ;/// Convenience constant for waking up tasks regardless of whether they are in interruptible or /// uninterruptible sleep. pub const TASK_NORMAL :c_uint = bindings::TASK_NORMAL as c_uint ;
Signature · 1 line
2.47.1
[tip: locking/core] rust: sync: condvar: Add wait_interruptible_freezable()
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: 70b9c8563c9c29102a785d4822f0d77d33fee808 Gitweb: https://git.kernel.org/tip/70b9c8563c9c29102a785d4822f0d77d33fee808 Author: Alice Ryhl <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:26:59 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:04 +01:00
rust: sync: condvar: Add wait_interruptible_freezable()
To support waiting for a CondVar as a freezable process, add a wait_interruptible_freezable() function.
Binder needs this function in the appropriate places to freeze a process where some of its threads are blocked on the Binder driver.
[ Boqun: Cleaned up the changelog and documentation. ]
--- rust/kernel/sync/condvar.rs | 23 ++++++++++++++++++++++- rust/kernel/task.rs | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-)
rust/kernel/sync/condvar.rs +22 -1
@@ -11,7 +11,9 @@ use crate::{ init::PinInit, pin_init, str::CStr, - task::{MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE}, + task::{ + MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE, + }, time::Jiffies, types::Opaque, };
@@ -159,6 +161,25 @@ impl CondVar {crate ::current!().signal_pending () } +/// Releases the lock and waits for a notification in interruptible and freezable mode. +/// +/// The process is allowed to be frozen during this sleep. No lock should be held when calling +/// this function, and there is a lockdep assertion for this. Freezing a task that holds a lock +/// can trivially deadlock vs another task that needs that lock to complete before it too can +/// hit freezable. +#[must_use = "wait_interruptible_freezable returns if a signal is pending, so the caller must check the return value" ] +pub fn wait_interruptible_freezable <T : ?Sized ,B :Backend >( + &self, + guard: &mut Guard <'_,T ,B >, + ) ->bool { + self.wait_internal ( +TASK_INTERRUPTIBLE |TASK_FREEZABLE , + guard, +MAX_SCHEDULE_TIMEOUT , + ); +crate ::current!().signal_pending () + } +/// Releases the lock and waits for a notification in interruptible mode. /// /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
rust/kernel/task.rs +2 -0
@@ -23,6 +23,8 @@ pub const MAX_SCHEDULE_TIMEOUT: c_long = c_long::MAX;pub const TASK_INTERRUPTIBLE :c_int = bindings::TASK_INTERRUPTIBLE as c_int ;/// Bitmask for tasks that are sleeping in an uninterruptible state. pub const TASK_UNINTERRUPTIBLE :c_int = bindings::TASK_UNINTERRUPTIBLE as c_int ; +/// Bitmask for tasks that are sleeping in a freezable state. +pub const TASK_FREEZABLE :c_int = bindings::TASK_FREEZABLE as c_int ;/// Convenience constant for waking up tasks regardless of whether they are in interruptible or /// uninterruptible sleep. pub const TASK_NORMAL :c_uint = bindings::TASK_NORMAL as c_uint ;
PATCH 10/11 rust: lockdep: Remove support for dynamically allocated LockClassKeys
In reply to Boqun FengFrom: Mitchell Levy <[email protected]>
Currently, dynamically allocated LockCLassKeys can be used from the Rust side without having them registered. This is a soundness issue, so remove them.
--- rust/kernel/sync.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
rust/kernel/sync.rs +4 -12
@@ -30,28 +30,20 @@unsafe impl Sync for LockClassKey {}impl LockClassKey { -/// Creates a new lock class key. -pub const fn new () ->Self { -Self (Opaque ::uninit ()) - } -pub (crate )fn as_ptr (&self) -> *mut bindings::lock_class_key { self.0 .get () } } -impl Default for LockClassKey { -fn default () ->Self { -Self ::new () - } -} -/// Defines a new static lock class and returns a pointer to it. #[doc(hidden)] #[macro_export] macro_rules! static_lock_class { () => {{ -static CLASS : $crate::sync::LockClassKey = $crate::sync::LockClassKey ::new (); +static CLASS : $crate::sync::LockClassKey = +// SAFETY: lockdep expects uninitialized memory when it's handed a statically allocated +// lock_class_key +unsafe { ::core::mem::MaybeUninit ::uninit ().assume_init () }; &CLASS }}; }
Signature · 1 line
2.47.1
[tip: locking/core] rust: lockdep: Remove support for dynamically allocated LockClassKeys
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: 966944f3711665db13e214fef6d02982c49bb972 Gitweb: https://git.kernel.org/tip/966944f3711665db13e214fef6d02982c49bb972 Author: Mitchell Levy <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:27:00 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:52:00 +01:00
rust: lockdep: Remove support for dynamically allocated LockClassKeys
Currently, dynamically allocated LockCLassKeys can be used from the Rust side without having them registered. This is a soundness issue, so remove them.
--- rust/kernel/sync.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
rust/kernel/sync.rs +4 -12
@@ -30,28 +30,20 @@ pub struct LockClassKey(Opaque<bindings::lock_class_key>);unsafe impl Sync for LockClassKey {}impl LockClassKey { -/// Creates a new lock class key. -pub const fn new () ->Self { -Self (Opaque ::uninit ()) - } -pub (crate )fn as_ptr (&self) -> *mut bindings::lock_class_key { self.0 .get () } } -impl Default for LockClassKey { -fn default () ->Self { -Self ::new () - } -} -/// Defines a new static lock class and returns a pointer to it. #[doc(hidden)] #[macro_export] macro_rules! static_lock_class { () => {{ -static CLASS : $crate::sync::LockClassKey = $crate::sync::LockClassKey ::new (); +static CLASS : $crate::sync::LockClassKey = +// SAFETY: lockdep expects uninitialized memory when it's handed a statically allocated +// lock_class_key +unsafe { ::core::mem::MaybeUninit ::uninit ().assume_init () }; &CLASS }}; }
PATCH 11/11 rust: lockdep: Use Pin for all LockClassKey usages
In reply to Boqun FengFrom: Mitchell Levy <[email protected]>
Reintroduce dynamically-allocated LockClassKeys such that they are automatically (de)registered. Require that all usages of LockClassKeys ensure that they are Pin'd.
Currently, only 'static LockClassKeys are supported, so Pin is redundant. However, it is intended that dynamically-allocated LockClassKeys will eventually be supported, so using Pin from the outset will make that change simpler.
--- rust/helpers/helpers.c | 1 + rust/helpers/sync.c | 13 ++++++++ rust/kernel/sync.rs | 57 +++++++++++++++++++++++++++++++-- rust/kernel/sync/condvar.rs | 5 ++- rust/kernel/sync/lock.rs | 4 +-- rust/kernel/sync/lock/global.rs | 5 +-- rust/kernel/sync/poll.rs | 2 +- rust/kernel/workqueue.rs | 2 +- 8 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 rust/helpers/sync.c
rust/helpers/helpers.c +1 -0
@@ -30,6 +30,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "sync.c" #include "task.c" #include "uaccess.c" #include "vmalloc.c"
rust/helpers/sync.c new file +13 -0
@@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/lockdep.h> + +void rust_helper_lockdep_register_key (struct lock_class_key *k) +{ +lockdep_register_key (k); +} + +void rust_helper_lockdep_unregister_key (struct lock_class_key *k) +{ +lockdep_unregister_key (k); +}
rust/kernel/sync.rs +54 -3
82 lines not on this page · raw message
rust/kernel/sync/condvar.rs +2 -3
@@ -17,8 +17,7 @@ time::Jiffies, types::Opaque, }; -use core::marker::PhantomPinned ; -use core::ptr; +use core::{marker::PhantomPinned , pin::Pin , ptr};use macros::pin_data;/// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class.
@@ -103,7 +102,7 @@ unsafe impl Sync for CondVar {}impl CondVar {/// Constructs a new condvar initialiser. -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { _pin:PhantomPinned ,// SAFETY: `slot` is valid while the closure is called and both `name` and `key` have
rust/kernel/sync/lock.rs +2 -2
@@ -12,7 +12,7 @@ str::CStr, types::{NotThreadSafe, Opaque, ScopeGuard}, }; -use core::{cell::UnsafeCell , marker::PhantomPinned }; +use core::{cell::UnsafeCell , marker::PhantomPinned , pin::Pin };use macros::pin_data;pub mod mutex;
@@ -129,7 +129,7 @@ unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}impl <T ,B :Backend >Lock <T ,B > {/// Constructs a new lock initialiser. -pub fn new (t:T , name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (t:T , name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { data:UnsafeCell ::new (t), _pin:PhantomPinned ,
rust/kernel/sync/lock/global.rs +3 -2
@@ -13,6 +13,7 @@use core::{ cell::UnsafeCell , marker::{PhantomData ,PhantomPinned }, + pin::Pin , };/// Trait implemented for marker types for global locks.
@@ -26,7 +27,7 @@ pub trait GlobalLockBackend {/// The backend used for this global lock. type Backend :Backend + 'static;/// The class for this global lock. -fn get_lock_class () -> &'staticLockClassKey ; +fn get_lock_class () ->Pin <&'staticLockClassKey >; }/// Type used for global locks.
@@ -270,7 +271,7 @@ impl $crate::sync::lock::GlobalLockBackend for $name {type Item = $valuety;type Backend = $crate::global_lock_inner!(backend $kind); -fn get_lock_class () -> &'static $crate::sync::LockClassKey { +fn get_lock_class () ->Pin <&'static $crate::sync::LockClassKey > { $crate::static_lock_class!() } }
rust/kernel/sync/poll.rs +1 -1
@@ -89,7 +89,7 @@ pub struct PollCondVar {impl PollCondVar {/// Constructs a new condvar initialiser. -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { inner <-CondVar ::new (name, key), })
rust/kernel/workqueue.rs +1 -1
@@ -369,7 +369,7 @@ unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}impl <T : ?Sized ,const ID :u64 >Work <T ,ID > {/// Creates a new instance of [`Work`]. #[inline] -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self >where T :WorkItem <ID >, {
Signature · 1 line
2.47.1
[tip: locking/core] rust: lockdep: Use Pin for all LockClassKey usages
The following commit has been merged into the locking/core branch of tip:
The following commit has been merged into the locking/core branch of tip:
Commit-ID: f73ca66f0d7f4371d172d6f5b1f9a00e367ba921 Gitweb: https://git.kernel.org/tip/f73ca66f0d7f4371d172d6f5b1f9a00e367ba921 Author: Mitchell Levy <[email protected]> AuthorDate: Fri, 07 Mar 2025 15:27:01 -08:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Sat, 08 Mar 2025 00:55:04 +01:00
rust: lockdep: Use Pin for all LockClassKey usages
Reintroduce dynamically-allocated LockClassKeys such that they are automatically (de)registered. Require that all usages of LockClassKeys ensure that they are Pin'd.
Currently, only 'static LockClassKeys are supported, so Pin is redundant. However, it is intended that dynamically-allocated LockClassKeys will eventually be supported, so using Pin from the outset will make that change simpler.
--- rust/helpers/helpers.c | 1 +- rust/helpers/sync.c | 13 +++++++- rust/kernel/sync.rs | 57 ++++++++++++++++++++++++++++++-- rust/kernel/sync/condvar.rs | 5 +-- rust/kernel/sync/lock.rs | 4 +- rust/kernel/sync/lock/global.rs | 5 +-- rust/kernel/sync/poll.rs | 2 +- rust/kernel/workqueue.rs | 2 +- 8 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 rust/helpers/sync.c
rust/helpers/helpers.c +1 -0
@@ -30,6 +30,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "sync.c" #include "task.c" #include "uaccess.c" #include "vmalloc.c"
rust/helpers/sync.c new file +13 -0
@@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/lockdep.h> + +void rust_helper_lockdep_register_key (struct lock_class_key *k) +{ +lockdep_register_key (k); +} + +void rust_helper_lockdep_unregister_key (struct lock_class_key *k) +{ +lockdep_unregister_key (k); +}
rust/kernel/sync.rs +54 -3
82 lines not on this page · raw message
rust/kernel/sync/condvar.rs +2 -3
@@ -17,8 +17,7 @@ use crate::{ time::Jiffies, types::Opaque, }; -use core::marker::PhantomPinned ; -use core::ptr; +use core::{marker::PhantomPinned , pin::Pin , ptr};use macros::pin_data;/// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class.
@@ -103,7 +102,7 @@ unsafe impl Sync for CondVar {}impl CondVar {/// Constructs a new condvar initialiser. -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { _pin:PhantomPinned ,// SAFETY: `slot` is valid while the closure is called and both `name` and `key` have
rust/kernel/sync/lock.rs +2 -2
@@ -12,7 +12,7 @@ use crate::{ str::CStr, types::{NotThreadSafe, Opaque, ScopeGuard}, }; -use core::{cell::UnsafeCell , marker::PhantomPinned }; +use core::{cell::UnsafeCell , marker::PhantomPinned , pin::Pin };use macros::pin_data;pub mod mutex;
@@ -129,7 +129,7 @@ unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}impl <T ,B :Backend >Lock <T ,B > {/// Constructs a new lock initialiser. -pub fn new (t:T , name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (t:T , name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { data:UnsafeCell ::new (t), _pin:PhantomPinned ,
rust/kernel/sync/lock/global.rs +3 -2
@@ -13,6 +13,7 @@ use crate::{use core::{ cell::UnsafeCell , marker::{PhantomData ,PhantomPinned }, + pin::Pin , };/// Trait implemented for marker types for global locks.
@@ -26,7 +27,7 @@ pub trait GlobalLockBackend {/// The backend used for this global lock. type Backend :Backend + 'static;/// The class for this global lock. -fn get_lock_class () -> &'staticLockClassKey ; +fn get_lock_class () ->Pin <&'staticLockClassKey >; }/// Type used for global locks.
@@ -270,7 +271,7 @@ macro_rules! global_lock {type Item = $valuety;type Backend = $crate::global_lock_inner!(backend $kind); -fn get_lock_class () -> &'static $crate::sync::LockClassKey { +fn get_lock_class () ->Pin <&'static $crate::sync::LockClassKey > { $crate::static_lock_class!() } }
rust/kernel/sync/poll.rs +1 -1
@@ -89,7 +89,7 @@ pub struct PollCondVar {impl PollCondVar {/// Constructs a new condvar initialiser. -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > { +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self > {pin_init! (Self { inner <-CondVar ::new (name, key), })
rust/kernel/workqueue.rs +1 -1
@@ -369,7 +369,7 @@ unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}impl <T : ?Sized ,const ID :u64 >Work <T ,ID > {/// Creates a new instance of [`Work`]. #[inline] -pub fn new (name: &'staticCStr , key: &'staticLockClassKey ) ->impl PinInit <Self > +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self >where T :WorkItem <ID >, {
Ingo Molnar
AppliedQuoted 27 lines · Boqun Feng
Hi Ingo & Peter,
As discussed, I'm resending the pull request for lockdep and Rust locking for v6.15 in the format of email patches. I dropped one patch and will postpone it for v6.16 because of a bug [1], and I think the fix
[2] needs more reviews.[1]: https://lore.kernel.org/lkml/20250306122413.GBZ8mT7Z61Tmgnh5Y9@fat_crate.local/
[2]: https://lore.kernel.org/lkml/Z8t8imzJVhWyDvhC@boqun-archlinux/Regards,
BoqunAlice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Addwait_interruptible_freezable()Boqun Feng (1):
rust: sync: lock: Add an example forGuard::lock_ref()Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usagesRandy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc comment
Quoted 7 lines
Waiman Long (5):
locking/semaphore: Usewake_qto wake up processes outside lock
critical section
locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation oflockdep.clocking/lockdep: Addkasan_check_byte()check inlock_acquire()
Thanks Boqun!
I've applied these 3 patches to the tip:locking/urgent tree:
locking/semaphore: Use wake_q to wake up processes outside lock critical section locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
rust: lockdep: Remove support for dynamically allocated LockClassKeys
As a general rule, if a patch is marked Cc: stable, it must also be applied to current upstream.
I have applied the others to tip:locking/core:
locking/lock_events: Add locking events for rtmutex slow pathsa94d32446ab5 locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation of lockdep.c
locking/lockdep: Add kasan_check_byte() check in lock_acquire()
rust: sync: Add accessor for the lock behind a given guard
rust: sync: lock: Add an example for Guard:: Lock_ref()
rust: sync: condvar: Add wait_interruptible_freezable()
rust: lockdep: Use Pin for all LockClassKey usages
I cleaned up changelogs where necessary - for example we generally don't include printk timestamps in changelogs, unless it's relevant, plus there were a number of typos and grammar mistakes left in some of the changelogs.
Thanks,
Ingo
Quoted 38 lines · Ingo Molnar
Quoted 27 lines · Boqun Feng
Hi Ingo & Peter,
As discussed, I'm resending the pull request for lockdep and Rust locking for v6.15 in the format of email patches. I dropped one patch and will postpone it for v6.16 because of a bug [1], and I think the fix
[2] needs more reviews.[1]: https://lore.kernel.org/lkml/20250306122413.GBZ8mT7Z61Tmgnh5Y9@fat_crate.local/
[2]: https://lore.kernel.org/lkml/Z8t8imzJVhWyDvhC@boqun-archlinux/Regards,
BoqunAlice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Addwait_interruptible_freezable()Boqun Feng (1):
rust: sync: lock: Add an example forGuard::lock_ref()Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usagesRandy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc commentQuoted 7 lines
Waiman Long (5):
locking/semaphore: Usewake_qto wake up processes outside lock
critical section
locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation oflockdep.clocking/lockdep: Addkasan_check_byte()check inlock_acquire()
Quoted 2 lines
Thanks Boqun!
Thanks.
Quoted 9 lines
I've applied these 3 patches to the tip:locking/urgent tree:
locking/semaphore: Use
wake_qto wake up processes outside lock critical section locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
rust: lockdep: Remove support for dynamically allocated LockClassKeysAs a general rule, if a patch is marked Cc: stable, it must also be applied to current upstream.
Do you prefer a separate pull request for the future? I can send one for urgent and one for locking/core.
Quoted 10 lines
I have applied the others to tip:locking/core:
locking/lock_events: Add locking events for rtmutex slow pathsa94d32446ab5 locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation of
lockdep.c
locking/lockdep: Addkasan_check_byte()check inlock_acquire()
rust: sync: Add accessor for the lock behind a given guard
rust: sync: lock: Add an example for Guard::Lock_ref()
rust: sync: condvar: Addwait_interruptible_freezable()
rust: lockdep: Use Pin for all LockClassKey usages
Quoted 2 lines
I cleaned up changelogs where necessary - for example we generally don't include printk timestamps in changelogs, unless it's relevant,
Got it, I will avoid them in the future.
Quoted 3 lines
plus there were a number of typos and grammar mistakes left in some of the changelogs.
Will take a look at your updates and keep working on improve them, thanks!
Regards,
Boqun
Quoted 3 lines
Thanks,
Ingo
Quoted 50 lines · Boqun Feng
Quoted 38 lines · Ingo Molnar
Quoted 27 lines · Boqun Feng
Hi Ingo & Peter,
As discussed, I'm resending the pull request for lockdep and Rust locking for v6.15 in the format of email patches. I dropped one patch and will postpone it for v6.16 because of a bug [1], and I think the fix
[2] needs more reviews.[1]: https://lore.kernel.org/lkml/20250306122413.GBZ8mT7Z61Tmgnh5Y9@fat_crate.local/
[2]: https://lore.kernel.org/lkml/Z8t8imzJVhWyDvhC@boqun-archlinux/Regards,
BoqunAlice Ryhl (2):
rust: sync: Add accessor for the lock behind a given guard
rust: sync: condvar: Addwait_interruptible_freezable()Boqun Feng (1):
rust: sync: lock: Add an example forGuard::lock_ref()Mitchell Levy (2):
rust: lockdep: Remove support for dynamically allocated LockClassKeys
rust: lockdep: Use Pin for all LockClassKey usagesRandy Dunlap (1):
locking/rtmutex: Use struct keyword in kernel-doc commentQuoted 7 lines
Waiman Long (5):
locking/semaphore: Usewake_qto wake up processes outside lock
critical section
locking/lock_events: Add locking events for rtmutex slow paths locking/lock_events: Add locking events for lockdep locking/lockdep: Disable KASAN instrumentation oflockdep.clocking/lockdep: Addkasan_check_byte()check inlock_acquire()Quoted 2 lines
Thanks Boqun!
Thanks.
Quoted 6 lines
I've applied these 3 patches to the tip:locking/urgent tree:
locking/semaphore: Use
wake_qto wake up processes outside lock critical section locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
rust: lockdep: Remove support for dynamically allocated LockClassKeys
Quoted 6 lines
Quoted 3 lines
As a general rule, if a patch is marked Cc: stable, it must also be applied to current upstream.
Do you prefer a separate pull request for the future? I can send one for urgent and one for locking/core.
One tree is fine - maybe indicate which ones are urgent material and keep them at the head of the tree?
Thanks,
Ingo
On Sat, Mar 08, 2025 at 01:11:36AM +0100, Ingo Molnar wrote:
[...]
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
I've applied these 3 patches to the tip:locking/urgent tree:
locking/semaphore: Use
wake_qto wake up processes outside lock critical section locking/rtmutex: Use the 'struct' keyword in kernel-doc comment
rust: lockdep: Remove support for dynamically allocated LockClassKeysAs a general rule, if a patch is marked Cc: stable, it must also be applied to current upstream.
Quoted 6 lines
Quoted 2 lines
Do you prefer a separate pull request for the future? I can send one for urgent and one for locking/core.
One tree is fine - maybe indicate which ones are urgent material and keep them at the head of the tree?
Sounds good to me, will do.
Regards,
Boqun
Quoted 3 lines
Thanks,
Ingo