* [PATCH v10 1/2] kernel/fork: beware of __put_task_struct calling context
2023-06-14 12:23 [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context Wander Lairson Costa
@ 2023-06-14 12:23 ` Wander Lairson Costa
2023-07-17 12:56 ` [tip: sched/core] kernel/fork: beware of __put_task_struct() " tip-bot2 for Wander Lairson Costa
2023-06-14 12:23 ` [PATCH v10 2/2] sched: avoid false lockdep splat in put_task_struct() Wander Lairson Costa
2023-06-19 11:07 ` [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context Peter Zijlstra
2 siblings, 1 reply; 6+ messages in thread
From: Wander Lairson Costa @ 2023-06-14 12:23 UTC (permalink / raw)
To: Christian Brauner (Microsoft),
Michael S. Tsirkin, Mike Christie, Peter Zijlstra,
Wander Lairson Costa, Oleg Nesterov, Kefeng Wang, Andrew Morton,
Suren Baghdasaryan, Liam R. Howlett, Andrei Vagin,
Mathieu Desnoyers, Nicholas Piggin, open list
Cc: Hu Chunyu, Valentin Schneider, Sebastian Andrzej Siewior,
Paul McKenney, Steven Rostedt, Luis Goncalves
Under PREEMPT_RT, __put_task_struct() indirectly acquires sleeping
locks. Therefore, it can't be called from an non-preemptible context.
One practical example is splat inside inactive_task_timer(), which is
called in a interrupt context:
CPU: 1 PID: 2848 Comm: life Kdump: loaded Tainted: G W ---------
Hardware name: HP ProLiant DL388p Gen8, BIOS P70 07/15/2012
Call Trace:
dump_stack_lvl+0x57/0x7d
mark_lock_irq.cold+0x33/0xba
? stack_trace_save+0x4b/0x70
? save_trace+0x55/0x150
mark_lock+0x1e7/0x400
mark_usage+0x11d/0x140
__lock_acquire+0x30d/0x930
lock_acquire.part.0+0x9c/0x210
? refill_obj_stock+0x3d/0x3a0
? rcu_read_lock_sched_held+0x3f/0x70
? trace_lock_acquire+0x38/0x140
? lock_acquire+0x30/0x80
? refill_obj_stock+0x3d/0x3a0
rt_spin_lock+0x27/0xe0
? refill_obj_stock+0x3d/0x3a0
refill_obj_stock+0x3d/0x3a0
? inactive_task_timer+0x1ad/0x340
kmem_cache_free+0x357/0x560
inactive_task_timer+0x1ad/0x340
? switched_from_dl+0x2d0/0x2d0
__run_hrtimer+0x8a/0x1a0
__hrtimer_run_queues+0x91/0x130
hrtimer_interrupt+0x10f/0x220
__sysvec_apic_timer_interrupt+0x7b/0xd0
sysvec_apic_timer_interrupt+0x4f/0xd0
? asm_sysvec_apic_timer_interrupt+0xa/0x20
asm_sysvec_apic_timer_interrupt+0x12/0x20
RIP: 0033:0x7fff196bf6f5
Instead of calling __put_task_struct() directly, we defer it using
call_rcu(). A more natural approach would use a workqueue, but since
in PREEMPT_RT, we can't allocate dynamic memory from atomic context,
the code would become more complex because we would need to put the
work_struct instance in the task_struct and initialize it when we
allocate a new task_struct.
The issue is reproducible with stress-ng:
while true; do
stress-ng --sched deadline --sched-period 1000000000 \
--sched-runtime 800000000 --sched-deadline \
1000000000 --mmapfork 23 -t 20
done
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Reported-by: Hu Chunyu <chuhu@redhat.com>
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Valentin Schneider <vschneid@redhat.com>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Paul McKenney <paulmck@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Luis Goncalves <lgoncalv@redhat.com>
---
include/linux/sched/task.h | 28 +++++++++++++++++++++++++++-
kernel/fork.c | 8 ++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index e0f5ac90a228..d20de91e3b95 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -118,10 +118,36 @@ static inline struct task_struct *get_task_struct(struct task_struct *t)
}
extern void __put_task_struct(struct task_struct *t);
+extern void __put_task_struct_rcu_cb(struct rcu_head *rhp);
static inline void put_task_struct(struct task_struct *t)
{
- if (refcount_dec_and_test(&t->usage))
+ if (!refcount_dec_and_test(&t->usage))
+ return;
+
+ /*
+ * under PREEMPT_RT, we can't call put_task_struct
+ * in atomic context because it will indirectly
+ * acquire sleeping locks.
+ *
+ * call_rcu() will schedule delayed_put_task_struct_rcu()
+ * to be called in process context.
+ *
+ * __put_task_struct() is called when
+ * refcount_dec_and_test(&t->usage) succeeds.
+ *
+ * This means that it can't "conflict" with
+ * put_task_struct_rcu_user() which abuses ->rcu the same
+ * way; rcu_users has a reference so task->usage can't be
+ * zero after rcu_users 1 -> 0 transition.
+ *
+ * delayed_free_task() also uses ->rcu, but it is only called
+ * when it fails to fork a process. Therefore, there is no
+ * way it can conflict with put_task_struct().
+ */
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible())
+ call_rcu(&t->rcu, __put_task_struct_rcu_cb);
+ else
__put_task_struct(t);
}
diff --git a/kernel/fork.c b/kernel/fork.c
index 41c964104b58..5dce5ad3e410 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -988,6 +988,14 @@ void __put_task_struct(struct task_struct *tsk)
}
EXPORT_SYMBOL_GPL(__put_task_struct);
+void __put_task_struct_rcu_cb(struct rcu_head *rhp)
+{
+ struct task_struct *task = container_of(rhp, struct task_struct, rcu);
+
+ __put_task_struct(task);
+}
+EXPORT_SYMBOL_GPL(__put_task_struct_rcu_cb);
+
void __init __weak arch_task_cache_init(void) { }
/*
--
2.40.1
^ permalink raw reply [flat|nested] 6+ messages in thread* [tip: sched/core] kernel/fork: beware of __put_task_struct() calling context
2023-06-14 12:23 ` [PATCH v10 1/2] " Wander Lairson Costa
@ 2023-07-17 12:56 ` tip-bot2 for Wander Lairson Costa
0 siblings, 0 replies; 6+ messages in thread
From: tip-bot2 for Wander Lairson Costa @ 2023-07-17 12:56 UTC (permalink / raw)
To: linux-tip-commits
Cc: Hu Chunyu, Oleg Nesterov, Valentin Schneider, Peter Zijlstra,
Wander Lairson Costa, x86, linux-kernel
The following commit has been merged into the sched/core branch of tip:
Commit-ID: d243b34459cea30cfe5f3a9b2feb44e7daff9938
Gitweb: https://git.kernel.org/tip/d243b34459cea30cfe5f3a9b2feb44e7daff9938
Author: Wander Lairson Costa <wander@redhat.com>
AuthorDate: Wed, 14 Jun 2023 09:23:21 -03:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Thu, 13 Jul 2023 15:21:48 +02:00
kernel/fork: beware of __put_task_struct() calling context
Under PREEMPT_RT, __put_task_struct() indirectly acquires sleeping
locks. Therefore, it can't be called from an non-preemptible context.
One practical example is splat inside inactive_task_timer(), which is
called in a interrupt context:
CPU: 1 PID: 2848 Comm: life Kdump: loaded Tainted: G W ---------
Hardware name: HP ProLiant DL388p Gen8, BIOS P70 07/15/2012
Call Trace:
dump_stack_lvl+0x57/0x7d
mark_lock_irq.cold+0x33/0xba
mark_lock+0x1e7/0x400
mark_usage+0x11d/0x140
__lock_acquire+0x30d/0x930
lock_acquire.part.0+0x9c/0x210
rt_spin_lock+0x27/0xe0
refill_obj_stock+0x3d/0x3a0
kmem_cache_free+0x357/0x560
inactive_task_timer+0x1ad/0x340
__run_hrtimer+0x8a/0x1a0
__hrtimer_run_queues+0x91/0x130
hrtimer_interrupt+0x10f/0x220
__sysvec_apic_timer_interrupt+0x7b/0xd0
sysvec_apic_timer_interrupt+0x4f/0xd0
asm_sysvec_apic_timer_interrupt+0x12/0x20
RIP: 0033:0x7fff196bf6f5
Instead of calling __put_task_struct() directly, we defer it using
call_rcu(). A more natural approach would use a workqueue, but since
in PREEMPT_RT, we can't allocate dynamic memory from atomic context,
the code would become more complex because we would need to put the
work_struct instance in the task_struct and initialize it when we
allocate a new task_struct.
The issue is reproducible with stress-ng:
while true; do
stress-ng --sched deadline --sched-period 1000000000 \
--sched-runtime 800000000 --sched-deadline \
1000000000 --mmapfork 23 -t 20
done
Reported-by: Hu Chunyu <chuhu@redhat.com>
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Valentin Schneider <vschneid@redhat.com>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20230614122323.37957-2-wander@redhat.com
---
include/linux/sched/task.h | 28 +++++++++++++++++++++++++++-
kernel/fork.c | 8 ++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index dd35ce2..6b687c1 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -118,10 +118,36 @@ static inline struct task_struct *get_task_struct(struct task_struct *t)
}
extern void __put_task_struct(struct task_struct *t);
+extern void __put_task_struct_rcu_cb(struct rcu_head *rhp);
static inline void put_task_struct(struct task_struct *t)
{
- if (refcount_dec_and_test(&t->usage))
+ if (!refcount_dec_and_test(&t->usage))
+ return;
+
+ /*
+ * under PREEMPT_RT, we can't call put_task_struct
+ * in atomic context because it will indirectly
+ * acquire sleeping locks.
+ *
+ * call_rcu() will schedule delayed_put_task_struct_rcu()
+ * to be called in process context.
+ *
+ * __put_task_struct() is called when
+ * refcount_dec_and_test(&t->usage) succeeds.
+ *
+ * This means that it can't "conflict" with
+ * put_task_struct_rcu_user() which abuses ->rcu the same
+ * way; rcu_users has a reference so task->usage can't be
+ * zero after rcu_users 1 -> 0 transition.
+ *
+ * delayed_free_task() also uses ->rcu, but it is only called
+ * when it fails to fork a process. Therefore, there is no
+ * way it can conflict with put_task_struct().
+ */
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible())
+ call_rcu(&t->rcu, __put_task_struct_rcu_cb);
+ else
__put_task_struct(t);
}
diff --git a/kernel/fork.c b/kernel/fork.c
index d2e12b6..f811497 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -985,6 +985,14 @@ void __put_task_struct(struct task_struct *tsk)
}
EXPORT_SYMBOL_GPL(__put_task_struct);
+void __put_task_struct_rcu_cb(struct rcu_head *rhp)
+{
+ struct task_struct *task = container_of(rhp, struct task_struct, rcu);
+
+ __put_task_struct(task);
+}
+EXPORT_SYMBOL_GPL(__put_task_struct_rcu_cb);
+
void __init __weak arch_task_cache_init(void) { }
/*
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v10 2/2] sched: avoid false lockdep splat in put_task_struct()
2023-06-14 12:23 [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context Wander Lairson Costa
2023-06-14 12:23 ` [PATCH v10 1/2] " Wander Lairson Costa
@ 2023-06-14 12:23 ` Wander Lairson Costa
2023-07-17 12:56 ` [tip: sched/core] " tip-bot2 for Wander Lairson Costa
2023-06-19 11:07 ` [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context Peter Zijlstra
2 siblings, 1 reply; 6+ messages in thread
From: Wander Lairson Costa @ 2023-06-14 12:23 UTC (permalink / raw)
To: Christian Brauner (Microsoft),
Mike Christie, Michael S. Tsirkin, Peter Zijlstra,
Wander Lairson Costa, Oleg Nesterov, Kefeng Wang, Andrew Morton,
Liam R. Howlett, Suren Baghdasaryan, Andrei Vagin,
Nicholas Piggin, open list
Cc: Sebastian Andrzej Siewior, Steven Rostedt, Luis Goncalves
In put_task_struct(), a spin_lock is indirectly acquired under the kernel
stock. When running the kernel in real-time (RT) configuration, the
operation is dispatched to a preemptible context call to ensure
guaranteed preemption. However, if PROVE_RAW_LOCK_NESTING is enabled
and __put_task_struct() is called while holding a raw_spinlock, lockdep
incorrectly reports an "Invalid lock context" in the stock kernel.
This false splat occurs because lockdep is unaware of the different
route taken under RT. To address this issue, override the inner wait
type to prevent the false lockdep splat.
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Luis Goncalves <lgoncalv@redhat.com>
---
include/linux/sched/task.h | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index d20de91e3b95..b53909027771 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -125,6 +125,19 @@ static inline void put_task_struct(struct task_struct *t)
if (!refcount_dec_and_test(&t->usage))
return;
+ /*
+ * In !RT, it is always safe to call __put_task_struct().
+ * Under RT, we can only call it in preemptible context.
+ */
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT) || preemptible()) {
+ static DEFINE_WAIT_OVERRIDE_MAP(put_task_map, LD_WAIT_SLEEP);
+
+ lock_map_acquire_try(&put_task_map);
+ __put_task_struct(t);
+ lock_map_release(&put_task_map);
+ return;
+ }
+
/*
* under PREEMPT_RT, we can't call put_task_struct
* in atomic context because it will indirectly
@@ -145,10 +158,7 @@ static inline void put_task_struct(struct task_struct *t)
* when it fails to fork a process. Therefore, there is no
* way it can conflict with put_task_struct().
*/
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible())
- call_rcu(&t->rcu, __put_task_struct_rcu_cb);
- else
- __put_task_struct(t);
+ call_rcu(&t->rcu, __put_task_struct_rcu_cb);
}
static inline void put_task_struct_many(struct task_struct *t, int nr)
--
2.40.1
^ permalink raw reply [flat|nested] 6+ messages in thread* [tip: sched/core] sched: avoid false lockdep splat in put_task_struct()
2023-06-14 12:23 ` [PATCH v10 2/2] sched: avoid false lockdep splat in put_task_struct() Wander Lairson Costa
@ 2023-07-17 12:56 ` tip-bot2 for Wander Lairson Costa
0 siblings, 0 replies; 6+ messages in thread
From: tip-bot2 for Wander Lairson Costa @ 2023-07-17 12:56 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Sebastian Andrzej Siewior, Peter Zijlstra,
Wander Lairson Costa, x86, linux-kernel
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 893cdaaa3977be6afb3a7f756fbfd7be83f68d8c
Gitweb: https://git.kernel.org/tip/893cdaaa3977be6afb3a7f756fbfd7be83f68d8c
Author: Wander Lairson Costa <wander@redhat.com>
AuthorDate: Wed, 14 Jun 2023 09:23:22 -03:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Thu, 13 Jul 2023 15:21:48 +02:00
sched: avoid false lockdep splat in put_task_struct()
In put_task_struct(), a spin_lock is indirectly acquired under the kernel
stock. When running the kernel in real-time (RT) configuration, the
operation is dispatched to a preemptible context call to ensure
guaranteed preemption. However, if PROVE_RAW_LOCK_NESTING is enabled
and __put_task_struct() is called while holding a raw_spinlock, lockdep
incorrectly reports an "Invalid lock context" in the stock kernel.
This false splat occurs because lockdep is unaware of the different
route taken under RT. To address this issue, override the inner wait
type to prevent the false lockdep splat.
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20230614122323.37957-3-wander@redhat.com
---
include/linux/sched/task.h | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index 6b687c1..a23af22 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -126,6 +126,19 @@ static inline void put_task_struct(struct task_struct *t)
return;
/*
+ * In !RT, it is always safe to call __put_task_struct().
+ * Under RT, we can only call it in preemptible context.
+ */
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT) || preemptible()) {
+ static DEFINE_WAIT_OVERRIDE_MAP(put_task_map, LD_WAIT_SLEEP);
+
+ lock_map_acquire_try(&put_task_map);
+ __put_task_struct(t);
+ lock_map_release(&put_task_map);
+ return;
+ }
+
+ /*
* under PREEMPT_RT, we can't call put_task_struct
* in atomic context because it will indirectly
* acquire sleeping locks.
@@ -145,10 +158,7 @@ static inline void put_task_struct(struct task_struct *t)
* when it fails to fork a process. Therefore, there is no
* way it can conflict with put_task_struct().
*/
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible())
- call_rcu(&t->rcu, __put_task_struct_rcu_cb);
- else
- __put_task_struct(t);
+ call_rcu(&t->rcu, __put_task_struct_rcu_cb);
}
DEFINE_FREE(put_task, struct task_struct *, if (_T) put_task_struct(_T))
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context
2023-06-14 12:23 [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context Wander Lairson Costa
2023-06-14 12:23 ` [PATCH v10 1/2] " Wander Lairson Costa
2023-06-14 12:23 ` [PATCH v10 2/2] sched: avoid false lockdep splat in put_task_struct() Wander Lairson Costa
@ 2023-06-19 11:07 ` Peter Zijlstra
2 siblings, 0 replies; 6+ messages in thread
From: Peter Zijlstra @ 2023-06-19 11:07 UTC (permalink / raw)
To: Wander Lairson Costa
Cc: Christian Brauner (Microsoft),
Michael S. Tsirkin, Mike Christie, Kefeng Wang, Oleg Nesterov,
Andrew Morton, Liam R. Howlett, Suren Baghdasaryan,
Mathieu Desnoyers, Andrei Vagin, Nicholas Piggin, open list,
Hu Chunyu, Valentin Schneider, Sebastian Andrzej Siewior,
Paul McKenney, Steven Rostedt, Luis Goncalves
On Wed, Jun 14, 2023 at 09:23:20AM -0300, Wander Lairson Costa wrote:
> Under PREEMPT_RT, __put_task_struct() indirectly acquires sleeping
> locks. Therefore, it can't be called from an non-preemptible context.
>
> Instead of calling __put_task_struct() directly, we defer it using
> call_rcu(). A more natural approach would use a workqueue, but since
> in PREEMPT_RT, we can't allocate dynamic memory from atomic context,
> the code would become more complex because we would need to put the
> work_struct instance in the task_struct and initialize it when we
> allocate a new task_struct.
>
> Wander Lairson Costa (2):
> kernel/fork: beware of __put_task_struct calling context
> sched: avoid false lockdep splat in put_task_struct()
Thanks!
^ permalink raw reply [flat|nested] 6+ messages in thread