msm: event_timer: Fix potential race condition
There is a possible race condition which could happen in the following
scenario:
Core 0 PC: lock_hrtimer_base
lock_hrtimer_base
hrtimer_get_remaining
get_next_event_time
msm_pm_idle_prepare
msm_cpuidle_enter
Core 1 PC: generic_exec_single
generic_exec_single
smp_call_function_single
setup_event_timer
mdp_update_pm
If core0 idle thread runs before core1 has had the chance to make the
smp_call_function_single, then idle thread could end up spinning on
lock_hrtimer_base() since setup_event_timer adds an event to the event
queue but the smp cross call is the one that initializes the event timer.
So Core0 could spin on lock_hrtimer_base() since CPU1's SMP call won't be
handled as idle thread runs in noirq context.
To prevent this scenario make a change to the code flow to do the queue
operations and the timer operations on the same CPU. Meaning
setup_event_timer will only make an SMP cross call to re-organize the event
queue and reset the event timer all of which will be run on CPU0.
CRs-fixed: 425587
Change-Id: I53fc551d18f350c47e05a94976b56a5fd2e9050b
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
(cherry picked from commit e73869cb18ee79bd0b951fd7effd6565ce7ccfb4)
diff --git a/arch/arm/mach-msm/event_timer.c b/arch/arm/mach-msm/event_timer.c
index e06dad4..9f46f68 100644
--- a/arch/arm/mach-msm/event_timer.c
+++ b/arch/arm/mach-msm/event_timer.c
@@ -193,21 +193,7 @@
unsigned long flags;
struct event_timer_info *event =
(struct event_timer_info *)data;
-
- local_irq_save(flags);
- create_hrtimer(event->node.expires);
- local_irq_restore(flags);
-}
-
-/**
- * setup_timer() : Helper function to setup timer on primary
- * core during hrtimer callback.
- * @event: event handle causing the wakeup.
- */
-static void setup_event_hrtimer(struct event_timer_info *event)
-{
struct timerqueue_node *next;
- unsigned long flags;
spin_lock_irqsave(&event_timer_lock, flags);
if (is_event_active(event))
@@ -223,9 +209,18 @@
if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
pr_info("%s: Setting timer for %lu", __func__,
(unsigned long)ktime_to_ns(event->node.expires));
+ create_hrtimer(event->node.expires);
+ }
+}
- smp_call_function_single(0, create_timer_smp, event, 1);
- }
+/**
+ * setup_timer() : Helper function to setup timer on primary
+ * core during hrtimer callback.
+ * @event: event handle causing the wakeup.
+ */
+static void setup_event_hrtimer(struct event_timer_info *event)
+{
+ smp_call_function_single(0, create_timer_smp, event, 1);
}
/**