[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20190819143802.038794711@linutronix.de>
Date: Mon, 19 Aug 2019 16:31:48 +0200
From: Thomas Gleixner <tglx@...utronix.de>
To: LKML <linux-kernel@...r.kernel.org>
Cc: Oleg Nesterov <oleg@...hat.com>, Ingo Molnar <mingo@...nel.org>,
Peter Zijlstra <peterz@...radead.org>,
John Stultz <john.stultz@...aro.org>,
Frederic Weisbecker <fweisbec@...il.com>,
Anna-Maria Behnsen <anna-maria@...utronix.de>
Subject: [patch 07/44] posix-cpu-timers: Simplify sighand locking in
run_posix_cpu_timers()
run_posix_cpu_timers() is called from the timer interrupt. The posix timer
expiry always affects the current task which got interrupted.
sighand locking is only racy when done on a foreign task, which must use
lock_task_sighand(). But in case of run_posix_cpu_timers() that's
pointless.
sighand of a task can only be dropped or changed by the task itself. Drop
happens in do_exit(), changing sighand happens in execve().
So the timer interrupt can see one of the following states of the current
task which it interrupted:
- executing: sighand is set
- exiting: sighand is either set or NULL
- execing: sighand is either the original one or the new one
The 'check -> lock -> check again' logic in lock_task_sighand() is not
required here at all. The state cannot change as the interrupted task
context obviously is not executing instructions.
So the only interesting case here is to check whether current->sighand is
NULL or not.
Add the check for sighand == NULL right at the beginning and replace
lock_task_sighand() with a regular spin_lock() invocation along with proper
comments why this is safe.
Signed-off-by: Thomas Gleixner <tglx@...utronix.de>
---
kernel/time/posix-cpu-timers.c | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -1141,20 +1141,34 @@ void run_posix_cpu_timers(void)
{
struct task_struct *tsk = current;
struct k_itimer *timer, *next;
- unsigned long flags;
LIST_HEAD(firing);
lockdep_assert_irqs_disabled();
/*
- * The fast path checks that there are no expired thread or thread
- * group timers. If that's so, just return.
+ * Verify that sighand exits. If not, the task is exiting and has
+ * dropped sighand already. Without sighand, timer expiry is
+ * pointless.
*/
- if (!fastpath_timer_check(tsk))
+ if (!tsk->sighand)
return;
- if (!lock_task_sighand(tsk, &flags))
+ /*
+ * Now do a fast check for expired task and group timers. If
+ * no expired timers found, return.
+ */
+ if(!fastpath_timer_check(tsk))
return;
+
+ /*
+ * The timer interrupt hit current. It's verified that sighand
+ * exists, so it cannot go away before the timer interrupt returns
+ * as only current can remove or change its own sighand in exit()
+ * or exec() with sighand lock held and interrupts disabled. Ergo
+ * the interrupt can only observe a valid sighand or NULL.
+ */
+ spin_lock(&tsk->sighand->siglock);
+
/*
* Here we take off tsk->signal->cpu_timers[N] and
* tsk->cpu_timers[N] all the timers that are firing, and
@@ -1172,7 +1186,7 @@ void run_posix_cpu_timers(void)
* that gets the timer lock before we do will give it up and
* spin until we've taken care of that timer below.
*/
- unlock_task_sighand(tsk, &flags);
+ spin_unlock(&tsk->sighand->siglock);
/*
* Now that all the timers on our list have the firing flag,
Powered by blists - more mailing lists