summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-10-30 17:03:32 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2023-10-30 17:03:32 -1000
commit9cc6fea175e41580000419a90fa744ba46aa4722 (patch)
treefcb4aa5c2a67862d9a415f1aa67dede57c5173fb /kernel
parentecb8cd2a9f7af7f99a6d4fa0a5a31822f6cfe255 (diff)
parent1aabbc532413ced293952f8e149ad0a607d6e470 (diff)
Merge tag 'core-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core updates from Thomas Gleixner: "Two small updates to ptrace_stop(): - Add a comment to explain that the preempt_disable() before unlocking tasklist lock is not a correctness problem and just avoids the tracer to preempt the tracee before the tracee schedules out. - Make that preempt_disable() conditional on PREEMPT_RT=n. RT enabled kernels cannot disable preemption at this point because cgroup_enter_frozen() and sched_submit_work() acquire spinlocks or rwlocks which are substituted by sleeping locks on RT. Acquiring a sleeping lock in a preemption disable region is obviously not possible. This obviously brings back the potential slowdown of ptrace() for RT enabled kernels, but that's a price to be paid for latency guarantees" * tag 'core-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: signal: Don't disable preemption in ptrace_stop() on PREEMPT_RT signal: Add a proper comment about preempt_disable() in ptrace_stop()
Diffstat (limited to 'kernel')
-rw-r--r--kernel/signal.c33
1 files changed, 28 insertions, 5 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index 09019017d669..f2a5578326ad 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2329,15 +2329,38 @@ static int ptrace_stop(int exit_code, int why, unsigned long message,
do_notify_parent_cldstop(current, false, why);
/*
- * Don't want to allow preemption here, because
- * sys_ptrace() needs this task to be inactive.
+ * The previous do_notify_parent_cldstop() invocation woke ptracer.
+ * One a PREEMPTION kernel this can result in preemption requirement
+ * which will be fulfilled after read_unlock() and the ptracer will be
+ * put on the CPU.
+ * The ptracer is in wait_task_inactive(, __TASK_TRACED) waiting for
+ * this task wait in schedule(). If this task gets preempted then it
+ * remains enqueued on the runqueue. The ptracer will observe this and
+ * then sleep for a delay of one HZ tick. In the meantime this task
+ * gets scheduled, enters schedule() and will wait for the ptracer.
*
- * XXX: implement read_unlock_no_resched().
+ * This preemption point is not bad from a correctness point of
+ * view but extends the runtime by one HZ tick time due to the
+ * ptracer's sleep. The preempt-disable section ensures that there
+ * will be no preemption between unlock and schedule() and so
+ * improving the performance since the ptracer will observe that
+ * the tracee is scheduled out once it gets on the CPU.
+ *
+ * On PREEMPT_RT locking tasklist_lock does not disable preemption.
+ * Therefore the task can be preempted after do_notify_parent_cldstop()
+ * before unlocking tasklist_lock so there is no benefit in doing this.
+ *
+ * In fact disabling preemption is harmful on PREEMPT_RT because
+ * the spinlock_t in cgroup_enter_frozen() must not be acquired
+ * with preemption disabled due to the 'sleeping' spinlock
+ * substitution of RT.
*/
- preempt_disable();
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+ preempt_disable();
read_unlock(&tasklist_lock);
cgroup_enter_frozen();
- preempt_enable_no_resched();
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+ preempt_enable_no_resched();
schedule();
cgroup_leave_frozen(true);