msm: ipc: Add support for subsystem restart in MSM_IPC Router

When a remote processor restarts, the transport corresponding to that
node notifies the MSM_IPC Router of that event. MSM_IPC Router cleans
up the data structures corresponding to that transport and notifies
the local clients/servers of removal of all the concerned remote
servers/clients respectively. When the remote node comes up and
broadcasts the server information, local clients will be notified of
the arrival of the server so that they can continue communication with
the server.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index 1e2b576..5987752 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -54,6 +54,9 @@
 static struct rr_packet *in_pkt;
 static int is_partial_in_pkt;
 
+static DEFINE_SPINLOCK(modem_reset_lock);
+static int modem_reset;
+
 static int msm_ipc_router_smd_remote_write_avail(void)
 {
 	return smd_write_avail(smd_remote_xprt.channel);
@@ -67,6 +70,8 @@
 	struct sk_buff *ipc_rtr_pkt;
 	int align_sz, align_data = 0;
 	int offset, sz_written = 0;
+	int ret, num_retries = 0;
+	unsigned long flags;
 
 	if (!pkt)
 		return -EINVAL;
@@ -75,8 +80,22 @@
 		return -EINVAL;
 
 	align_sz = ALIGN_SIZE(pkt->length);
-	while (smd_write_start(smd_remote_xprt.channel, (len + align_sz)) < 0)
+	while ((ret = smd_write_start(smd_remote_xprt.channel,
+				      (len + align_sz))) < 0) {
+		spin_lock_irqsave(&modem_reset_lock, flags);
+		if (modem_reset) {
+			spin_unlock_irqrestore(&modem_reset_lock, flags);
+			pr_err("%s: Modem reset\n", __func__);
+			return -ENETRESET;
+		}
+		spin_unlock_irqrestore(&modem_reset_lock, flags);
+		if (num_retries >= 5) {
+			pr_err("%s: Error %d @ smd_write_start\n",
+				__func__, ret);
+			return ret;
+		}
 		msleep(50);
+	}
 
 	D("%s: Ready to write\n", __func__);
 	skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
@@ -85,10 +104,18 @@
 			if (!smd_write_avail(smd_remote_xprt.channel))
 				smd_enable_read_intr(smd_remote_xprt.channel);
 
-			wait_event_interruptible_timeout(write_avail_wait_q,
-				smd_write_avail(smd_remote_xprt.channel),
-				msecs_to_jiffies(50));
+			wait_event(write_avail_wait_q,
+				(smd_write_avail(smd_remote_xprt.channel) ||
+				modem_reset));
 			smd_disable_read_intr(smd_remote_xprt.channel);
+			spin_lock_irqsave(&modem_reset_lock, flags);
+			if (modem_reset) {
+				spin_unlock_irqrestore(&modem_reset_lock,
+							flags);
+				pr_err("%s: Modem reset\n", __func__);
+				return -ENETRESET;
+			}
+			spin_unlock_irqrestore(&modem_reset_lock, flags);
 
 			sz_written = smd_write_segment(smd_remote_xprt.channel,
 					  ipc_rtr_pkt->data + offset,
@@ -103,10 +130,17 @@
 		if (smd_write_avail(smd_remote_xprt.channel) < align_sz)
 			smd_enable_read_intr(smd_remote_xprt.channel);
 
-		wait_event_interruptible_timeout(write_avail_wait_q,
-			(smd_write_avail(smd_remote_xprt.channel) >=
-			 align_sz), msecs_to_jiffies(50));
+		wait_event(write_avail_wait_q,
+			((smd_write_avail(smd_remote_xprt.channel) >=
+			 align_sz) || modem_reset));
 		smd_disable_read_intr(smd_remote_xprt.channel);
+		spin_lock_irqsave(&modem_reset_lock, flags);
+		if (modem_reset) {
+			spin_unlock_irqrestore(&modem_reset_lock, flags);
+			pr_err("%s: Modem reset\n", __func__);
+			return -ENETRESET;
+		}
+		spin_unlock_irqrestore(&modem_reset_lock, flags);
 
 		smd_write_segment(smd_remote_xprt.channel,
 				  &align_data, align_sz, 0);
@@ -128,6 +162,17 @@
 	int pkt_size, sz_read, sz;
 	struct sk_buff *ipc_rtr_pkt;
 	void *data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&modem_reset_lock, flags);
+	if (modem_reset) {
+		spin_unlock_irqrestore(&modem_reset_lock, flags);
+		release_pkt(in_pkt);
+		is_partial_in_pkt = 0;
+		pr_err("%s: Modem reset\n", __func__);
+		return;
+	}
+	spin_unlock_irqrestore(&modem_reset_lock, flags);
 
 	D("%s pkt_size: %d, read_avail: %d\n", __func__,
 		smd_cur_packet_size(smd_remote_xprt.channel),
@@ -206,12 +251,35 @@
 
 static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event)
 {
-	if (event == SMD_EVENT_DATA) {
+	unsigned long flags;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
 		if (smd_read_avail(smd_remote_xprt.channel))
 			queue_delayed_work(smd_xprt_workqueue,
 					   &work_read_data, 0);
 		if (smd_write_avail(smd_remote_xprt.channel))
 			wake_up(&write_avail_wait_q);
+		break;
+
+	case SMD_EVENT_OPEN:
+		spin_lock_irqsave(&modem_reset_lock, flags);
+		modem_reset = 0;
+		spin_unlock_irqrestore(&modem_reset_lock, flags);
+		msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
+					IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
+		D("%s: Notified IPC Router of OPEN Event\n", __func__);
+		break;
+
+	case SMD_EVENT_CLOSE:
+		spin_lock_irqsave(&modem_reset_lock, flags);
+		modem_reset = 1;
+		spin_unlock_irqrestore(&modem_reset_lock, flags);
+		wake_up(&write_avail_wait_q);
+		msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
+					IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
+		D("%s: Notified IPC Router of CLOSE Event\n", __func__);
+		break;
 	}
 }
 
@@ -244,11 +312,6 @@
 
 	smd_disable_read_intr(smd_remote_xprt.channel);
 
-	msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
-				  IPC_ROUTER_XPRT_EVENT_OPEN,
-				  NULL);
-	D("%s: Notified IPC Router of OPEN Event\n", __func__);
-
 	smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT);
 
 	return 0;