mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v10 0/2] kernel/fork: beware of __put_task_struct calling context
@ 2023-06-14 12:23 Wander Lairson Costa
  2023-06-14 12:23 ` [PATCH v10 1/2] " Wander Lairson Costa
                   ` (2 more replies)
  0 siblings, 3 replies; 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, Kefeng Wang, Oleg Nesterov, Andrew Morton,
	Liam R. Howlett, Suren Baghdasaryan, Mathieu Desnoyers,
	Andrei Vagin, 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.

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.

Changelog
=========

v1:
* Initial implementation fixing the splat.

v2:
* Isolate the logic in its own function.
* Fix two more cases caught in review.

v3:
* Change __put_task_struct() to handle the issue internally.

v4:
* Explain why call_rcu() is safe to call from interrupt context.

v5:
* Explain why __put_task_struct() doesn't conflict with
  put_task_sruct_rcu_user.

v6:
* As per Sebastian's review, revert back the implementation of v2
  with a distinct function.
* Add a check in put_task_struct() to warning when called from a
  non-sleepable context.
* Address more call sites.

v7:
* Fix typos.
* Add an explanation why the new function doesn't conflict with
  delayed_free_task().

v8:
* Bring back v5.
* Fix coding style.

v9:
* Reorganize to not need ___put_task_struct() by Oleg's suggestion.

v10:
* Add a patch preventing a splat when compile with
CONFIG_PROVE_RAW_LOCK_NESTING.

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>

Wander Lairson Costa (2):
  kernel/fork: beware of __put_task_struct calling context
  sched: avoid false lockdep splat in put_task_struct()

 include/linux/sched/task.h | 38 +++++++++++++++++++++++++++++++++++++-
 kernel/fork.c              |  8 ++++++++
 2 files changed, 45 insertions(+), 1 deletion(-)

-- 
2.40.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [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

* [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

* 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

* [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

* [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

end of thread, other threads:[~2023-07-17 12:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-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-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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®