[PATCH] posix-timers: fix posix_cpu_timer_set() vs run_posix_cpu_timers() race
[pandora-kernel.git] / kernel / posix-cpu-timers.c
index d30b304..b15462b 100644 (file)
@@ -380,14 +380,9 @@ 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 (!list_empty(&timer->it.cpu.entry)) {
+       if (likely(p != NULL)) {
                read_lock(&tasklist_lock);
                if (unlikely(p->signal == NULL)) {
                        /*
@@ -396,18 +391,20 @@ int posix_cpu_timer_del(struct k_itimer *timer)
                         */
                        BUG_ON(!list_empty(&timer->it.cpu.entry));
                } else {
-                       /*
-                        * Take us off the task's timer list.
-                        */
                        spin_lock(&p->sighand->siglock);
-                       list_del(&timer->it.cpu.entry);
+                       if (timer->it.cpu.firing)
+                               ret = TIMER_RETRY;
+                       else
+                               list_del(&timer->it.cpu.entry);
                        spin_unlock(&p->sighand->siglock);
                }
                read_unlock(&tasklist_lock);
+
+               if (!ret)
+                       put_task_struct(p);
        }
-       put_task_struct(p);
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -424,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;
@@ -437,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;
@@ -450,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;
@@ -495,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();
@@ -736,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);
 
        /*
@@ -786,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
@@ -794,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;
        }
 
@@ -1169,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);
@@ -1294,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);
 
        /*