summaryrefslogtreecommitdiff
path: root/kernel/posix-cpu-timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/posix-cpu-timers.c')
-rw-r--r--kernel/posix-cpu-timers.c119
1 files changed, 67 insertions, 52 deletions
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index b3f3edc475de..b15462b17a58 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -380,28 +380,31 @@ int posix_cpu_timer_create(struct k_itimer *new_timer)
int posix_cpu_timer_del(struct k_itimer *timer)
{
struct task_struct *p = timer->it.cpu.task;
+ int ret = 0;
- if (timer->it.cpu.firing)
- return TIMER_RETRY;
-
- if (unlikely(p == NULL))
- return 0;
+ if (likely(p != NULL)) {
+ read_lock(&tasklist_lock);
+ if (unlikely(p->signal == NULL)) {
+ /*
+ * We raced with the reaping of the task.
+ * The deletion should have cleared us off the list.
+ */
+ BUG_ON(!list_empty(&timer->it.cpu.entry));
+ } else {
+ spin_lock(&p->sighand->siglock);
+ if (timer->it.cpu.firing)
+ ret = TIMER_RETRY;
+ else
+ list_del(&timer->it.cpu.entry);
+ spin_unlock(&p->sighand->siglock);
+ }
+ read_unlock(&tasklist_lock);
- spin_lock(&p->sighand->siglock);
- if (!list_empty(&timer->it.cpu.entry)) {
- /*
- * Take us off the task's timer list. We don't need to
- * take tasklist_lock and check for the task being reaped.
- * If it was reaped, it already called posix_cpu_timers_exit
- * and posix_cpu_timers_exit_group to clear all the timers
- * that pointed to it.
- */
- list_del(&timer->it.cpu.entry);
- put_task_struct(p);
+ if (!ret)
+ put_task_struct(p);
}
- spin_unlock(&p->sighand->siglock);
- return 0;
+ return ret;
}
/*
@@ -418,8 +421,6 @@ static void cleanup_timers(struct list_head *head,
cputime_t ptime = cputime_add(utime, stime);
list_for_each_entry_safe(timer, next, head, entry) {
- put_task_struct(timer->task);
- timer->task = NULL;
list_del_init(&timer->entry);
if (cputime_lt(timer->expires.cpu, ptime)) {
timer->expires.cpu = cputime_zero;
@@ -431,8 +432,6 @@ static void cleanup_timers(struct list_head *head,
++head;
list_for_each_entry_safe(timer, next, head, entry) {
- put_task_struct(timer->task);
- timer->task = NULL;
list_del_init(&timer->entry);
if (cputime_lt(timer->expires.cpu, utime)) {
timer->expires.cpu = cputime_zero;
@@ -444,8 +443,6 @@ static void cleanup_timers(struct list_head *head,
++head;
list_for_each_entry_safe(timer, next, head, entry) {
- put_task_struct(timer->task);
- timer->task = NULL;
list_del_init(&timer->entry);
if (timer->expires.sched < sched_time) {
timer->expires.sched = 0;
@@ -489,6 +486,9 @@ static void process_timer_rebalance(struct task_struct *p,
struct task_struct *t = p;
unsigned int nthreads = atomic_read(&p->signal->live);
+ if (!nthreads)
+ return;
+
switch (clock_idx) {
default:
BUG();
@@ -730,9 +730,15 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,
* Disarm any old timer after extracting its expiry time.
*/
BUG_ON(!irqs_disabled());
+
+ ret = 0;
spin_lock(&p->sighand->siglock);
old_expires = timer->it.cpu.expires;
- list_del_init(&timer->it.cpu.entry);
+ if (unlikely(timer->it.cpu.firing)) {
+ timer->it.cpu.firing = -1;
+ ret = TIMER_RETRY;
+ } else
+ list_del_init(&timer->it.cpu.entry);
spin_unlock(&p->sighand->siglock);
/*
@@ -780,7 +786,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,
}
}
- if (unlikely(timer->it.cpu.firing)) {
+ if (unlikely(ret)) {
/*
* We are colliding with the timer actually firing.
* Punt after filling in the timer's old value, and
@@ -788,8 +794,6 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,
* it as an overrun (thanks to bump_cpu_timer above).
*/
read_unlock(&tasklist_lock);
- timer->it.cpu.firing = -1;
- ret = TIMER_RETRY;
goto out;
}
@@ -955,14 +959,16 @@ void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
static void check_thread_timers(struct task_struct *tsk,
struct list_head *firing)
{
+ int maxfire;
struct list_head *timers = tsk->cpu_timers;
+ maxfire = 20;
tsk->it_prof_expires = cputime_zero;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (cputime_lt(prof_ticks(tsk), t->expires.cpu)) {
+ if (!--maxfire || cputime_lt(prof_ticks(tsk), t->expires.cpu)) {
tsk->it_prof_expires = t->expires.cpu;
break;
}
@@ -971,12 +977,13 @@ static void check_thread_timers(struct task_struct *tsk,
}
++timers;
+ maxfire = 20;
tsk->it_virt_expires = cputime_zero;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (cputime_lt(virt_ticks(tsk), t->expires.cpu)) {
+ if (!--maxfire || cputime_lt(virt_ticks(tsk), t->expires.cpu)) {
tsk->it_virt_expires = t->expires.cpu;
break;
}
@@ -985,12 +992,13 @@ static void check_thread_timers(struct task_struct *tsk,
}
++timers;
+ maxfire = 20;
tsk->it_sched_expires = 0;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (tsk->sched_time < t->expires.sched) {
+ if (!--maxfire || tsk->sched_time < t->expires.sched) {
tsk->it_sched_expires = t->expires.sched;
break;
}
@@ -1007,6 +1015,7 @@ static void check_thread_timers(struct task_struct *tsk,
static void check_process_timers(struct task_struct *tsk,
struct list_head *firing)
{
+ int maxfire;
struct signal_struct *const sig = tsk->signal;
cputime_t utime, stime, ptime, virt_expires, prof_expires;
unsigned long long sched_time, sched_expires;
@@ -1039,12 +1048,13 @@ static void check_process_timers(struct task_struct *tsk,
} while (t != tsk);
ptime = cputime_add(utime, stime);
+ maxfire = 20;
prof_expires = cputime_zero;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (cputime_lt(ptime, t->expires.cpu)) {
+ if (!--maxfire || cputime_lt(ptime, t->expires.cpu)) {
prof_expires = t->expires.cpu;
break;
}
@@ -1053,12 +1063,13 @@ static void check_process_timers(struct task_struct *tsk,
}
++timers;
+ maxfire = 20;
virt_expires = cputime_zero;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (cputime_lt(utime, t->expires.cpu)) {
+ if (!--maxfire || cputime_lt(utime, t->expires.cpu)) {
virt_expires = t->expires.cpu;
break;
}
@@ -1067,12 +1078,13 @@ static void check_process_timers(struct task_struct *tsk,
}
++timers;
+ maxfire = 20;
sched_expires = 0;
while (!list_empty(timers)) {
struct cpu_timer_list *t = list_entry(timers->next,
struct cpu_timer_list,
entry);
- if (sched_time < t->expires.sched) {
+ if (!--maxfire || sched_time < t->expires.sched) {
sched_expires = t->expires.sched;
break;
}
@@ -1155,6 +1167,9 @@ static void check_process_timers(struct task_struct *tsk,
unsigned long long sched_left, sched;
const unsigned int nthreads = atomic_read(&sig->live);
+ if (!nthreads)
+ return;
+
prof_left = cputime_sub(prof_expires, utime);
prof_left = cputime_sub(prof_left, stime);
prof_left = cputime_div(prof_left, nthreads);
@@ -1280,30 +1295,30 @@ void run_posix_cpu_timers(struct task_struct *tsk)
#undef UNEXPIRED
- BUG_ON(tsk->exit_state);
-
/*
* Double-check with locks held.
*/
read_lock(&tasklist_lock);
- spin_lock(&tsk->sighand->siglock);
+ if (likely(tsk->signal != NULL)) {
+ spin_lock(&tsk->sighand->siglock);
- /*
- * Here we take off tsk->cpu_timers[N] and tsk->signal->cpu_timers[N]
- * all the timers that are firing, and put them on the firing list.
- */
- check_thread_timers(tsk, &firing);
- check_process_timers(tsk, &firing);
+ /*
+ * Here we take off tsk->cpu_timers[N] and tsk->signal->cpu_timers[N]
+ * all the timers that are firing, and put them on the firing list.
+ */
+ check_thread_timers(tsk, &firing);
+ check_process_timers(tsk, &firing);
- /*
- * We must release these locks before taking any timer's lock.
- * There is a potential race with timer deletion here, as the
- * siglock now protects our private firing list. We have set
- * the firing flag in each timer, so that a deletion attempt
- * 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);
+ /*
+ * We must release these locks before taking any timer's lock.
+ * There is a potential race with timer deletion here, as the
+ * siglock now protects our private firing list. We have set
+ * the firing flag in each timer, so that a deletion attempt
+ * 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);
+ }
read_unlock(&tasklist_lock);
/*