msm: bam_dmux: Fix race condition during SSR
BAM DMUX drivers set the global flags to identify the SSR during
SUBSYS_BEFORE_SHUTDOWN notification, but the flags change is not reflected
in some code paths like msm_bam_dmux_write().
Add the SSR flag check and SRCU locking to complete all pending bam dmux
writes before returning the control in SSR call back function for
SUBSYS_BEFORE_SHUTDOWN notification.
CRs-Fixed: 584805
Change-Id: I26647b485977943f272c44f7450ed6c4a4aa62f3
Signed-off-by: Arun Kumar Neelakantam <aneela@codeaurora.org>
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 83a1290..991ccef 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -29,6 +29,7 @@
#include <linux/kfifo.h>
#include <linux/of.h>
#include <mach/msm_ipc_logging.h>
+#include <linux/srcu.h>
#include <mach/sps.h>
#include <mach/bam_dmux.h>
#include <mach/msm_smsm.h>
@@ -221,6 +222,8 @@
static struct workqueue_struct *bam_mux_rx_workqueue;
static struct workqueue_struct *bam_mux_tx_workqueue;
+static struct srcu_struct bam_dmux_srcu;
+
/* A2 power collaspe */
#define UL_TIMEOUT_DELAY 1000 /* in ms */
#define ENABLE_DISCONNECT_ACK 0x1
@@ -738,6 +741,7 @@
struct sk_buff *new_skb = NULL;
dma_addr_t dma_address;
struct tx_pkt_info *pkt;
+ int rcu_id;
if (id >= BAM_DMUX_NUM_CHANNELS)
return -EINVAL;
@@ -746,11 +750,19 @@
if (!bam_mux_initialized)
return -ENODEV;
+ rcu_id = srcu_read_lock(&bam_dmux_srcu);
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: In SSR... ch_id[%d]\n", __func__, id);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return -EFAULT;
+ }
+
DBG("%s: writing to ch %d len %d\n", __func__, id, skb->len);
spin_lock_irqsave(&bam_ch[id].lock, flags);
if (!bam_ch_is_open(id)) {
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
return -ENODEV;
}
@@ -758,6 +770,7 @@
(bam_ch[id].num_tx_pkts >= HIGH_WATERMARK)) {
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
pr_err("%s: watermark exceeded: %d\n", __func__, id);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
return -EAGAIN;
}
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
@@ -766,8 +779,10 @@
if (!bam_is_connected) {
read_unlock(&ul_wakeup_lock);
ul_wakeup();
- if (unlikely(in_global_reset == 1))
+ if (unlikely(in_global_reset == 1)) {
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
return -EFAULT;
+ }
read_lock(&ul_wakeup_lock);
notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL));
}
@@ -845,6 +860,7 @@
}
ul_packet_written = 1;
read_unlock(&ul_wakeup_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
return rc;
write_fail3:
@@ -855,6 +871,7 @@
dev_kfree_skb_any(new_skb);
write_fail:
read_unlock(&ul_wakeup_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
return -ENOMEM;
}
@@ -1955,9 +1972,12 @@
* because a watchdog crash from a bus stall would likely occur.
*/
if (code == SUBSYS_BEFORE_SHUTDOWN) {
+ BAM_DMUX_LOG("%s: begin\n", __func__);
in_global_reset = 1;
in_ssr = 1;
- BAM_DMUX_LOG("%s: begin\n", __func__);
+ /* wait till all bam_dmux writes completes */
+ synchronize_srcu(&bam_dmux_srcu);
+ BAM_DMUX_LOG("%s: ssr signaling complete\n", __func__);
flush_workqueue(bam_mux_rx_workqueue);
}
if (code != SUBSYS_AFTER_SHUTDOWN)
@@ -2459,6 +2479,7 @@
INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout);
INIT_DELAYED_WORK(&queue_rx_work, queue_rx_work_func);
wake_lock_init(&bam_wakelock, WAKE_LOCK_SUSPEND, "bam_dmux_wakelock");
+ init_srcu_struct(&bam_dmux_srcu);
rc = bam_ops->smsm_state_cb_register_ptr(SMSM_MODEM_STATE,
SMSM_A2_POWER_CONTROL,