]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/hrtimer.c
V4L/DVB (9613): tvaudio: fix a memory leak
[linux-2.6-omap-h63xx.git] / kernel / hrtimer.c
index 51ee90bca2dedcc8b882f96130c3486fe3111ce5..95d3949f2ae51135b181906f632b8de7f5e3f8e7 100644 (file)
@@ -946,7 +946,7 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
 }
 
 /**
- * hrtimer_start_range_ns - (re)start an relative timer on the current CPU
+ * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU
  * @timer:     the timer to be added
  * @tim:       expiry time
  * @delta_ns:  "slack" range for the timer
@@ -1022,7 +1022,7 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n
 EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
 
 /**
- * hrtimer_start - (re)start an relative timer on the current CPU
+ * hrtimer_start - (re)start an hrtimer on the current CPU
  * @timer:     the timer to be added
  * @tim:       expiry time
  * @mode:      expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL)
@@ -1209,6 +1209,7 @@ static void run_hrtimer_pending(struct hrtimer_cpu_base *cpu_base)
                enum hrtimer_restart (*fn)(struct hrtimer *);
                struct hrtimer *timer;
                int restart;
+               int emulate_hardirq_ctx = 0;
 
                timer = list_entry(cpu_base->cb_pending.next,
                                   struct hrtimer, cb_entry);
@@ -1217,10 +1218,24 @@ static void run_hrtimer_pending(struct hrtimer_cpu_base *cpu_base)
                timer_stats_account_hrtimer(timer);
 
                fn = timer->function;
+               /*
+                * A timer might have been added to the cb_pending list
+                * when it was migrated during a cpu-offline operation.
+                * Emulate hardirq context for such timers.
+                */
+               if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU ||
+                   timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED)
+                       emulate_hardirq_ctx = 1;
+
                __remove_hrtimer(timer, timer->base, HRTIMER_STATE_CALLBACK, 0);
                spin_unlock_irq(&cpu_base->lock);
 
-               restart = fn(timer);
+               if (unlikely(emulate_hardirq_ctx)) {
+                       local_irq_disable();
+                       restart = fn(timer);
+                       local_irq_enable();
+               } else
+                       restart = fn(timer);
 
                spin_lock_irq(&cpu_base->lock);
 
@@ -1394,22 +1409,16 @@ void hrtimer_interrupt(struct clock_event_device *dev)
  */
 void hrtimer_peek_ahead_timers(void)
 {
-       unsigned long flags;
        struct tick_device *td;
-       struct clock_event_device *dev;
+       unsigned long flags;
 
        if (!hrtimer_hres_active())
                return;
 
        local_irq_save(flags);
        td = &__get_cpu_var(tick_cpu_device);
-       if (!td)
-               goto out;
-       dev = td->evtdev;
-       if (!dev)
-               goto out;
-       hrtimer_interrupt(dev);
-out:
+       if (td && td->evtdev)
+               hrtimer_interrupt(td->evtdev);
        local_irq_restore(flags);
 }
 
@@ -1467,9 +1476,7 @@ void hrtimer_run_queues(void)
                if (!base->first)
                        continue;
 
-               if (base->get_softirq_time)
-                       base->softirq_time = base->get_softirq_time();
-               else if (gettime) {
+               if (gettime) {
                        hrtimer_get_softirq_time(cpu_base);
                        gettime = 0;
                }
@@ -1758,9 +1765,11 @@ static void migrate_hrtimers(int cpu)
        new_base = &get_cpu_var(hrtimer_bases);
 
        tick_cancel_sched_timer(cpu);
-
-       local_irq_disable();
-       spin_lock(&new_base->lock);
+       /*
+        * The caller is globally serialized and nobody else
+        * takes two locks at once, deadlock is not possible.
+        */
+       spin_lock_irq(&new_base->lock);
        spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
 
        for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
@@ -1773,8 +1782,7 @@ static void migrate_hrtimers(int cpu)
                raise = 1;
 
        spin_unlock(&old_base->lock);
-       spin_unlock(&new_base->lock);
-       local_irq_enable();
+       spin_unlock_irq(&new_base->lock);
        put_cpu_var(hrtimer_bases);
 
        if (raise)