Skip to contentSkip to conversation

run_posix_cpu_timers: don't check ->exit_state, use lock_task_sighand()

PATCH In review Oleg Nesterov 1 participant 5 messages Jun 10, 2010 – Jun 18, 2010

lore
Share & tools
lore
#0 lore raw

PATCH 5/5 run_posix_cpu_timers: don't check ->exit_state, use lock_task_sighand()

run_posix_cpu_timers() doesn't work if current has already passed exit_notify(). This was needed to prevent the races with do_wait().

Since ea6d290c ->signal is always valid and can't go away. We can remove the "tsk->exit_state == 0" in fastpath_timer_check() and convert run_posix_cpu_timers() to use lock_task_sighand().

Note: it makes sense to take group_leader's sighand instead, the sub-thread still uses CPU after release_task(). But we need more changes to do this.

---

 kernel/posix-cpu-timers.c |   10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)
kernel/posix-cpu-timers.c renamed from kernel/posix-cpu-timers.c~5_RUN_PCT_DONT_CK_EXIT_STATE +4 -6
@@ -1272,10 +1272,6 @@ static inline int fastpath_timer_check(s
 {
 	struct signal_struct *sig;
 
-	/* tsk == current, ensure it is safe to use ->signal/sighand */
-	if (unlikely(tsk->exit_state))
-		return 0;
-
 	if (!task_cputime_zero(&tsk->cputime_expires)) {
 		struct task_cputime task_sample = {
 			.utime = tsk->utime,
@@ -1308,6 +1304,7 @@ void run_posix_cpu_timers(struct task_st
 {
 	LIST_HEAD(firing);
 	struct k_itimer *timer, *next;
+	unsigned long flags;
 
 	BUG_ON(!irqs_disabled());
 
@@ -1318,7 +1315,8 @@ void run_posix_cpu_timers(struct task_st
 	if (!fastpath_timer_check(tsk))
 		return;
 
-	spin_lock(&tsk->sighand->siglock);
+	if (!lock_task_sighand(tsk, &flags))
+		return;
 	/*
 	 * Here we take off tsk->signal->cpu_timers[N] and
 	 * tsk->cpu_timers[N] all the timers that are firing, and
@@ -1340,7 +1338,7 @@ void run_posix_cpu_timers(struct task_st
 	 * that gets the timer lock before we do will give it up and
 	 * spin until we've taken care of that timer below.
 	 */
-	spin_unlock(&tsk->sighand->siglock);
+	unlock_task_sighand(tsk, &flags);
 
 	/*
 	 * Now that all the timers on our list have the firing flag,
#1 lore raw

[PATCH 6/5] fix the racy usage of thread_group_cputimer() in fastpath_timer_check()

fastpath_timer_check()->thread_group_cputimer() is racy and unneeded.

It is racy because another thread can clear ->running before thread_group_cputimer() takes cputimer->lock. In this case thread_group_cputimer() will set ->running = true again and call thread_group_cputime(). But since we do not hold tasklist or siglock, we can race with fork/exit and copy the wrong results into cputimer->cputime.

It is unneeded because if ->running == true we can just use the numbers in cputimer->cputime we already have.

Change fastpath_timer_check() to copy cputimer->cputime into the local variable under cputimer->lock. We do not re-check ->running under cputimer->lock, run_posix_cpu_timers() does this check later.

Note: we can add more optimizations on top of this change.

---

 kernel/posix-cpu-timers.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
kernel/posix-cpu-timers.c renamed from kernel/posix-cpu-timers.c~6_FPTC_DONT_SET_RUNNING +4 -1
@@ -1287,7 +1287,10 @@ static inline int fastpath_timer_check(s
 	if (sig->cputimer.running) {
 		struct task_cputime group_sample;
 
-		thread_group_cputimer(tsk, &group_sample);
+		spin_lock(&sig->cputimer.lock);
+		group_sample = sig->cputimer.cputime;
+		spin_unlock(&sig->cputimer.lock);
+
 		if (task_cputime_expired(&group_sample, &sig->cputime_expires))
 			return 1;
 	}
#2 lore raw

Re: [PATCH 6/5] fix the racy usage of thread_group_cputimer() in fastpath_timer_check()

Quoted 3 lines · Oleg Nesterov

fastpath_timer_check()->thread_group_cputimer() is racy and unneeded.

Just in case... this fix doesn't depend on other patches I sent.

Quoted 37 lines

It is racy because another thread can clear ->running before thread_group_cputimer() takes cputimer->lock. In this case thread_group_cputimer() will set ->running = true again and call thread_group_cputime(). But since we do not hold tasklist or siglock, we can race with fork/exit and copy the wrong results into cputimer->cputime.

It is unneeded because if ->running == true we can just use the numbers in cputimer->cputime we already have.

Change fastpath_timer_check() to copy cputimer->cputime into the local variable under cputimer->lock. We do not re-check ->running under cputimer->lock, run_posix_cpu_timers() does this check later.

Note: we can add more optimizations on top of this change.

---

 kernel/posix-cpu-timers.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
kernel/posix-cpu-timers.c renamed from kernel/posix-cpu-timers.c~6_FPTC_DONT_SET_RUNNING +4 -1
@@ -1287,7 +1287,10 @@ static inline int fastpath_timer_check(s
 	if (sig->cputimer.running) {
 		struct task_cputime group_sample;
 
-		thread_group_cputimer(tsk, &group_sample);
+		spin_lock(&sig->cputimer.lock);
+		group_sample = sig->cputimer.cputime;
+		spin_unlock(&sig->cputimer.lock);
+
 		if (task_cputime_expired(&group_sample, &sig->cputime_expires))
 			return 1;
 	}
#3 lore raw

[tip:sched/core] sched: Fix the racy usage of thread_group_cputimer() in fastpath_timer_check()

In reply to Oleg Nesterov
Commit-ID:  8d1f431cbec115a780cd551ab1b4955c125f8d31
Gitweb:     http://git.kernel.org/tip/8d1f431cbec115a780cd551ab1b4955c125f8d31
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Fri, 11 Jun 2010 20:04:46 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Fri, 18 Jun 2010 10:46:57 +0200

sched: Fix the racy usage of thread_group_cputimer() in fastpath_timer_check()

fastpath_timer_check()->thread_group_cputimer() is racy and unneeded.

It is racy because another thread can clear ->running before thread_group_cputimer() takes cputimer->lock. In this case thread_group_cputimer() will set ->running = true again and call thread_group_cputime(). But since we do not hold tasklist or siglock, we can race with fork/exit and copy the wrong results into cputimer->cputime.

It is unneeded because if ->running == true we can just use the numbers in cputimer->cputime we already have.

Change fastpath_timer_check() to copy cputimer->cputime into the local variable under cputimer->lock. We do not re-check ->running under cputimer->lock, run_posix_cpu_timers() does this check later.

Note: we can add more optimizations on top of this change.

LKML-Reference: <[email protected]>

---
 kernel/posix-cpu-timers.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
kernel/posix-cpu-timers.c +4 -1
index d5dbef5..f66bdd3 100644
@@ -1287,7 +1287,10 @@ static inline int fastpath_timer_check(struct task_struct *tsk)
 	if (sig->cputimer.running) {
 		struct task_cputime group_sample;
 
-		thread_group_cputimer(tsk, &group_sample);
+		spin_lock(&sig->cputimer.lock);
+		group_sample = sig->cputimer.cputime;
+		spin_unlock(&sig->cputimer.lock);
+
 		if (task_cputime_expired(&group_sample, &sig->cputime_expires))
 			return 1;
 	}
#4 lore raw

[tip:sched/core] sched: run_posix_cpu_timers: Don't check ->exit_state, use lock_task_sighand()

In reply to Oleg Nesterov
Commit-ID:  0bdd2ed4138ec04e09b4f8165981efc99e439f55
Gitweb:     http://git.kernel.org/tip/0bdd2ed4138ec04e09b4f8165981efc99e439f55
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Fri, 11 Jun 2010 01:10:18 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Fri, 18 Jun 2010 10:46:57 +0200

sched: run_posix_cpu_timers: Don't check ->exit_state, use lock_task_sighand()

run_posix_cpu_timers() doesn't work if current has already passed exit_notify(). This was needed to prevent the races with do_wait().

Since ea6d290c ->signal is always valid and can't go away. We can remove the "tsk->exit_state == 0" in fastpath_timer_check() and convert run_posix_cpu_timers() to use lock_task_sighand().

Note: it makes sense to take group_leader's sighand instead, the sub-thread still uses CPU after release_task(). But we need more changes to do this.

LKML-Reference: <[email protected]>

---
 kernel/posix-cpu-timers.c |   10 ++++------
 1 files changed, 4 insertions(+), 6 deletions(-)
kernel/posix-cpu-timers.c +4 -6
index bf2a650..d5dbef5 100644
@@ -1272,10 +1272,6 @@ static inline int fastpath_timer_check(struct task_struct *tsk)
 {
 	struct signal_struct *sig;
 
-	/* tsk == current, ensure it is safe to use ->signal/sighand */
-	if (unlikely(tsk->exit_state))
-		return 0;
-
 	if (!task_cputime_zero(&tsk->cputime_expires)) {
 		struct task_cputime task_sample = {
 			.utime = tsk->utime,
@@ -1308,6 +1304,7 @@ void run_posix_cpu_timers(struct task_struct *tsk)
 {
 	LIST_HEAD(firing);
 	struct k_itimer *timer, *next;
+	unsigned long flags;
 
 	BUG_ON(!irqs_disabled());
 
@@ -1318,7 +1315,8 @@ void run_posix_cpu_timers(struct task_struct *tsk)
 	if (!fastpath_timer_check(tsk))
 		return;
 
-	spin_lock(&tsk->sighand->siglock);
+	if (!lock_task_sighand(tsk, &flags))
+		return;
 	/*
 	 * Here we take off tsk->signal->cpu_timers[N] and
 	 * tsk->cpu_timers[N] all the timers that are firing, and
@@ -1340,7 +1338,7 @@ void run_posix_cpu_timers(struct task_struct *tsk)
 	 * that gets the timer lock before we do will give it up and
 	 * spin until we've taken care of that timer below.
 	 */
-	spin_unlock(&tsk->sighand->siglock);
+	unlock_task_sighand(tsk, &flags);
 
 	/*
 	 * Now that all the timers on our list have the firing flag,