Files
kernel/kernel-std/debian/patches/0016-locking-rtmutex.c-disable-lock-stealing.patch
Joao Tognolli Jr dd909993ff Disable lock stealing to prevent starvation under extreme circumstances
This patch disables lock stealing for rt_mutex. While lock stealing is
an efficient optimization that allows higher-priority tasks to bypass
lower-priority waiters, it can lead to a deadlock in specific, highly
constrained scenarios.

In an edge case where a lower-priority real-time task (B) is pinned to
the same CPU as a continuous stream of higher-priority real-time tasks
(A), the B task can become the top waiter for a lock, such as the
tasklist_lock. Because the A tasks are monopolizing the CPU and can
always steal the lock, the B task never gets scheduled to release the
lock, causing perpetual starvation for any other tasks that need it.

By disabling lock stealing, higher-priority tasks are instead added to
the waiter list, allowing priority inheritance to boost the
lower-priority B task. This ensures the B task gets scheduled, releases
the lock, and prevents the described starvation condition.

TestPlan:
PASS: build-pkgs && build-image
PASS: Jenkins Installation
PASS: real-time throttling tests

Story: 2011384
Task: 52692

Signed-off-by: Joao Tognolli Jr <Joao.TognolliJunior@windriver.com>
Change-Id: Ifa6582ab479bb19333010acc42c06d7e1fa05fa8
2025-09-22 09:51:40 -03:00

71 lines
3.1 KiB
Diff

From 7e61c0d0cd295008bc2d895eff474c21f4051040 Mon Sep 17 00:00:00 2001
From: Mark Asselstine <mark.asselstine@windriver.com>
Date: Sat, 2 Aug 2025 10:48:05 -0400
Subject: [PATCH] locking/rtmutex.c: disable lock stealing
The rt_mutex allows lock stealing. If a process with a higher priority
wants a lock and the top waiter is of lower priority the higher
priority process will steal the lock. This avoid adding the higher
priority task from having to be added to the waiter list and then
relying on priority inheritance to be applied to get the lock in place
of the lower priority top waiter.
This lock stealing approach makes a lot of sense. But under an extreme
circumstance this will cause an issue.
If we have 3 tasks all pinned to a CPU
- High priority RT tasks, let's call these A
- Lower priority RT tasks, let's call these B
- Non-RT tasks with lower priority, let's call these Z
We exec enough A tasks to monopolize the CPU, the scheduler will run A
tasks, except thanks to "sched_rt_runtime_us" we can ensure the
scheduler will run some of Z tasks. But this results in no CPU time
for B tasks. If any of these B tasks were in the process of being
created or were exiting when A tasks started to monopolize the CPU
then a B task will be in the tasklist_lock waiter list, and eventually
be the top waiter. Thanks to lock stealing, A tasks can continue to be
created, run and exit, as they will steal the tasklist_lock. However,
the B task that is top waiter will be stuck waiting for time on CPU
(which the scheduler never gives it) and it along with any other tasks
on any CPU requiring the tasklist_lock will wait perpetually. The only
thing that can happen to break this is for the CPU to get through all
A tasks work, at which point the B task top waiter will get on CPU,
then all other waiters will be worked through.
Under normal circumstances the above scenario would not happen, the B
task would be migrated to another CPU for example. But with the B task
pinned to the same CPU as A tasks using all RT time on CPU and new A
tasks continuing to be created without issue, due to stealing, we end
up in this situation.
Here we disable lock stealing. The expected outcome is that the higher
priority tasks would not be able to steal the lock, instead they will
be added to the waiter tree where priority inheritance will be
applied. The lower priority top waiter will be boosted, get time on
CPU and we avoid the situation described above.
Signed-off-by: Mark Asselstine <mark.asselstine@windriver.com>
---
kernel/locking/rtmutex.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 4a10e8c16fd2..7ae6f052e2fe 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -426,8 +426,8 @@ static __always_inline int rt_waiter_node_equal(struct rt_waiter_node *left,
static inline bool rt_mutex_steal(struct rt_mutex_waiter *waiter,
struct rt_mutex_waiter *top_waiter)
{
- if (rt_waiter_node_less(&waiter->tree, &top_waiter->tree))
- return true;
+ /* Disable lock stealing to force PI */
+ return false;
#ifdef RT_MUTEX_BUILD_SPINLOCKS
/*
--
2.34.1