msm: ipc: Serialize SMD XPRT CLOSE and OPEN events

When a remote subsystem resets, IPC Router's SMD XPRT Layer queues a CLOSE
event. When the concerned subsytem powers up and the channel is reopened,
SMD XPRT Layer queues a OPEN event. When the remote subsytem resets and
powers up quickly, the following race condition exists:
a) IPC Router is scheduled out while handling the CLOSE event, leaving a
   stale XPRT
b) IPC Router's SMD XPRT Layer queues a OPEN event
c) IPC Router's SMD XPRT Layer reads a HELLO message, finds a stale
   XPRT and queues a DATA event on the stale XPRT
This race condition causes a kernel panic.

Fix this race condition by not handling any other SMD XPRT event until
the CLOSE event is complete.

CRs-Fixed: 488331
Change-Id: I2283e163413d1896bd6842530b8a6a2b537e22de
Signed-off-by: Zaheerulla Meer <zmeer@codeaurora.org>
Signed-off-by: Sridhar Gujje <sgujje@codeaurora.org>
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 2c91371..a86ed4c 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -189,8 +189,6 @@
 
 static uint32_t next_port_id;
 static DEFINE_MUTEX(next_port_id_lock);
-static atomic_t pending_close_count = ATOMIC_INIT(0);
-static wait_queue_head_t subsystem_restart_wait;
 static struct workqueue_struct *msm_ipc_router_workqueue;
 
 enum {
@@ -2610,10 +2608,7 @@
 
 	modem_reset_cleanup(xprt_work->xprt->priv);
 	msm_ipc_router_remove_xprt(xprt_work->xprt);
-
-	if (atomic_dec_return(&pending_close_count) == 0)
-		wake_up(&subsystem_restart_wait);
-
+	xprt_work->xprt->sft_close_done(xprt_work->xprt);
 	kfree(xprt_work);
 }
 
@@ -2647,7 +2642,6 @@
 
 	case IPC_ROUTER_XPRT_EVENT_CLOSE:
 		D("close event for '%s'\n", xprt->name);
-		atomic_inc(&pending_close_count);
 		xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
 				GFP_ATOMIC);
 		xprt_work->xprt = xprt;
@@ -2675,45 +2669,6 @@
 	queue_work(xprt_info->workqueue, &xprt_info->read_data);
 }
 
-static int modem_restart_notifier_cb(struct notifier_block *this,
-				unsigned long code,
-				void *data);
-static struct notifier_block msm_ipc_router_nb = {
-	.notifier_call = modem_restart_notifier_cb,
-};
-
-static int modem_restart_notifier_cb(struct notifier_block *this,
-				unsigned long code,
-				void *data)
-{
-	switch (code) {
-	case SUBSYS_BEFORE_SHUTDOWN:
-		D("%s: SUBSYS_BEFORE_SHUTDOWN\n", __func__);
-		break;
-
-	case SUBSYS_BEFORE_POWERUP:
-		D("%s: waiting for RPC restart to complete\n", __func__);
-		wait_event(subsystem_restart_wait,
-			atomic_read(&pending_close_count) == 0);
-		D("%s: finished restart wait\n", __func__);
-		break;
-
-	default:
-		break;
-	}
-
-	return NOTIFY_DONE;
-}
-
-static void *restart_notifier_handle;
-static __init int msm_ipc_router_modem_restart_late_init(void)
-{
-	restart_notifier_handle = subsys_notif_register_notifier("modem",
-							&msm_ipc_router_nb);
-	return 0;
-}
-late_initcall(msm_ipc_router_modem_restart_late_init);
-
 static int __init msm_ipc_router_init(void)
 {
 	int i, ret;
@@ -2743,7 +2698,6 @@
 	mutex_unlock(&routing_table_lock);
 
 	init_waitqueue_head(&newserver_wait);
-	init_waitqueue_head(&subsystem_restart_wait);
 	ret = msm_ipc_router_init_sockets();
 	if (ret < 0)
 		pr_err("%s: Init sockets failed\n", __func__);
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index d56c0dd..6f6cf64 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -170,6 +170,7 @@
 	int (*write)(void *data, uint32_t len,
 		     struct msm_ipc_router_xprt *xprt);
 	int (*close)(struct msm_ipc_router_xprt *xprt);
+	void (*sft_close_done)(struct msm_ipc_router_xprt *xprt);
 };
 
 extern struct completion msm_ipc_remote_router_up;
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index c2f738e..e7ca58b 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -54,6 +54,7 @@
 	spinlock_t ss_reset_lock;	/*Subsystem reset lock*/
 	int ss_reset;
 	void *pil;
+	struct completion sft_close_complete;
 };
 
 struct msm_ipc_router_smd_xprt_work {
@@ -216,6 +217,14 @@
 	return rc;
 }
 
+static void smd_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt)
+{
+	struct msm_ipc_router_smd_xprt *smd_xprtp =
+		container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
+
+	complete_all(&smd_xprtp->sft_close_complete);
+}
+
 static void smd_xprt_read_data(struct work_struct *work)
 {
 	int pkt_size, sz_read, sz;
@@ -321,7 +330,14 @@
 {
 	struct msm_ipc_router_smd_xprt_work *xprt_work =
 		container_of(work, struct msm_ipc_router_smd_xprt_work, work);
+	struct msm_ipc_router_smd_xprt *smd_xprtp =
+		container_of(xprt_work->xprt,
+			     struct msm_ipc_router_smd_xprt, xprt);
+	unsigned long flags;
 
+	spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
+	smd_xprtp->ss_reset = 0;
+	spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
 	msm_ipc_router_xprt_notify(xprt_work->xprt,
 				IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
 	D("%s: Notified IPC Router of %s OPEN\n",
@@ -333,11 +349,16 @@
 {
 	struct msm_ipc_router_smd_xprt_work *xprt_work =
 		container_of(work, struct msm_ipc_router_smd_xprt_work, work);
+	struct msm_ipc_router_smd_xprt *smd_xprtp =
+		container_of(xprt_work->xprt,
+			     struct msm_ipc_router_smd_xprt, xprt);
 
+	init_completion(&smd_xprtp->sft_close_complete);
 	msm_ipc_router_xprt_notify(xprt_work->xprt,
 				IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
 	D("%s: Notified IPC Router of %s CLOSE\n",
 	   __func__, xprt_work->xprt->name);
+	wait_for_completion(&smd_xprtp->sft_close_complete);
 	kfree(xprt_work);
 }
 
@@ -361,9 +382,6 @@
 		break;
 
 	case SMD_EVENT_OPEN:
-		spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
-		smd_xprtp->ss_reset = 0;
-		spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
 		xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work),
 				    GFP_ATOMIC);
 		if (!xprt_work) {
@@ -440,6 +458,7 @@
 		msm_ipc_router_smd_remote_write_avail;
 	smd_remote_xprt[id].xprt.write = msm_ipc_router_smd_remote_write;
 	smd_remote_xprt[id].xprt.close = msm_ipc_router_smd_remote_close;
+	smd_remote_xprt[id].xprt.sft_close_done = smd_xprt_sft_close_done;
 	smd_remote_xprt[id].xprt.priv = NULL;
 
 	init_waitqueue_head(&smd_remote_xprt[id].write_avail_wait_q);