msm: smd_pkt: Avoid race condition between close and read/write operations

If the close operation is performed by a different thread, then there
is a possibility of read/write operations getting stuck or accessing
some stale resources. The close operation is blocked if there is a read/
write operation is going on. The read/write operations check for
appropriate flags to see if a close operation is performed on a device.

Change-Id: I05ecce88b020c8da1658fe673be6e3ac001ae772
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/smd_pkt.c b/arch/arm/mach-msm/smd_pkt.c
index 430a4c0..912e129 100644
--- a/arch/arm/mach-msm/smd_pkt.c
+++ b/arch/arm/mach-msm/smd_pkt.c
@@ -287,7 +287,6 @@
 	int bytes_read;
 	int pkt_size;
 	struct smd_pkt_dev *smd_pkt_devp;
-	struct smd_channel *chl;
 	unsigned long flags;
 
 	smd_pkt_devp = file->private_data;
@@ -312,20 +311,30 @@
 	D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
 		__func__, smd_pkt_devp->i, count);
 
-	chl = smd_pkt_devp->ch;
 wait_for_packet:
 	r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
-				     (smd_cur_packet_size(chl) > 0 &&
-				      smd_read_avail(chl)) ||
+				     !smd_pkt_devp->ch ||
+				     (smd_cur_packet_size(smd_pkt_devp->ch) > 0
+				      && smd_read_avail(smd_pkt_devp->ch)) ||
 				     smd_pkt_devp->has_reset);
 
+	mutex_lock(&smd_pkt_devp->rx_lock);
 	if (smd_pkt_devp->has_reset) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
 		pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
 			__func__, smd_pkt_devp->i);
 		return notify_reset(smd_pkt_devp);
 	}
 
+	if (!smd_pkt_devp->ch) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		pr_err("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return -EINVAL;
+	}
+
 	if (r < 0) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
 		/* qualify error message */
 		if (r != -ERESTARTSYS) {
 			/* we get this anytime a signal comes in */
@@ -337,8 +346,6 @@
 	}
 
 	/* Here we have a whole packet waiting for us */
-
-	mutex_lock(&smd_pkt_devp->rx_lock);
 	pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
 
 	if (!pkt_size) {
@@ -485,6 +492,9 @@
 						__func__, smd_pkt_devp->i);
 					return notify_reset(smd_pkt_devp);
 				}
+				pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
+					__func__, smd_pkt_devp->i, r);
+				return r;
 			}
 			bytes_written += r;
 		}
@@ -864,6 +874,8 @@
 	clean_and_signal(smd_pkt_devp);
 
 	mutex_lock(&smd_pkt_devp->ch_lock);
+	mutex_lock(&smd_pkt_devp->rx_lock);
+	mutex_lock(&smd_pkt_devp->tx_lock);
 	if (smd_pkt_devp->ch != 0) {
 		r = smd_close(smd_pkt_devp->ch);
 		smd_pkt_devp->ch = 0;
@@ -873,6 +885,8 @@
 		if (smd_pkt_devp->pil)
 			pil_put(smd_pkt_devp->pil);
 	}
+	mutex_unlock(&smd_pkt_devp->tx_lock);
+	mutex_unlock(&smd_pkt_devp->rx_lock);
 	mutex_unlock(&smd_pkt_devp->ch_lock);
 
 	smd_pkt_devp->has_reset = 0;