msm: smd: Fix race condition while processing SMSM callback
When SMSM interrupt is received, a snapshot of SMSM callback is stored
in a kfifo. The snapshot is stored as
a) SMSM state of each subsystem
b) Wakelock usage information.
Then a work item is scheduled in global workqueue to process the snapshot
and notify the smsm clients. The smsm snapshot is processed as
1) Lock the snapshot
2) Retrieve SMSM states from kfifo and process them
3) Unlock the snapshot
4) Retrieve Wakelock usage information from kfifo
When back-to-back smsm interrupts are received, multiple work items are
scheduled in multiple cores. The first work item performs steps 1, 2 &
3 and got pre-empted. In the meanwhile the second work item starts
with Wakelock usage information and reports wrong smsm state to the SMSM
clients.
So schedule the work items in a single thread workqueue and interchange
steps 3 & 4 while processing the SMSM snapshot.
CRs-Fixed: 353552
Change-Id: Ic08e242653cc8d1648ad7a70bf7c5f6b7c089c4e
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index c1e4118..6b42325 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -354,6 +354,7 @@
static void smd_fake_irq_handler(unsigned long arg);
static void smsm_cb_snapshot(uint32_t use_wakelock);
+static struct workqueue_struct *smsm_cb_wq;
static void notify_smsm_cb_clients_worker(struct work_struct *work);
static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
static DEFINE_MUTEX(smsm_lock);
@@ -2365,6 +2366,13 @@
return -ENOMEM;
}
+ smsm_cb_wq = create_singlethread_workqueue("smsm_cb_wq");
+ if (!smsm_cb_wq) {
+ pr_err("%s: smsm_cb_wq creation failed\n", __func__);
+ kfree(smsm_states);
+ return -EFAULT;
+ }
+
mutex_lock(&smsm_lock);
for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
state_info = &smsm_states[n];
@@ -2540,7 +2548,7 @@
goto restore_snapshot_count;
}
- schedule_work(&smsm_cb_work);
+ queue_work(smsm_cb_wq, &smsm_cb_work);
return;
restore_snapshot_count:
@@ -2832,7 +2840,6 @@
state_info->last_value = new_state;
}
}
- mutex_unlock(&smsm_lock);
/* read wakelock flag */
ret = kfifo_out(&smsm_snapshot_fifo, &use_wakelock,
@@ -2840,8 +2847,10 @@
if (ret != sizeof(use_wakelock)) {
pr_err("%s: snapshot underflow %d\n",
__func__, ret);
+ mutex_unlock(&smsm_lock);
return;
}
+ mutex_unlock(&smsm_lock);
if (use_wakelock) {
spin_lock_irqsave(&smsm_snapshot_count_lock, flags);