diag: Close the read when buffer is already marked free

Currently there is a possibility of reading the data for
read queued by md session which is already closed and buffer
is marked free. The patch adds check for buffer busy status
and close the read thread if buffer is marked free. The patch
also modify the checks for keeping buffer busy for multi logging
mode at starting of session.

CRs-Fixed: 2203013
Change-Id: If97cf3c64fa6cbc4a496b6ca1ee4471e4e336012
Signed-off-by: Hardik Arya <harya@codeaurora.org>
diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c
index e9683e0..59e45a7 100644
--- a/drivers/char/diag/diagfwd_glink.c
+++ b/drivers/char/diag/diagfwd_glink.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -257,7 +257,8 @@
 static void diag_state_open_glink(void *ctxt);
 static void diag_state_close_glink(void *ctxt);
 static int diag_glink_write(void *ctxt, unsigned char *buf, int len);
-static int diag_glink_read(void *ctxt, unsigned char *buf, int buf_len);
+static int diag_glink_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf);
 static void diag_glink_queue_read(void *ctxt);
 
 static struct diag_peripheral_ops glink_ops = {
@@ -320,7 +321,8 @@
 	return (int)(atomic_read(&info->diag_state));
 }
 
-static int diag_glink_read(void *ctxt, unsigned char *buf, int buf_len)
+static int diag_glink_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf)
 {
 	struct diag_glink_info *glink_info =  NULL;
 	int ret_val = 0;
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 7908b82..f53d9d5 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -1286,11 +1286,10 @@
 	 * Logging mode here is reflecting previous mode
 	 * status and will be updated to new mode later.
 	 *
-	 * Keeping the buffers busy for Memory Device Mode.
+	 * Keeping the buffers busy for Memory Device and Multi Mode.
 	 */
 
-	if ((driver->logging_mode != DIAG_USB_MODE) ||
-		driver->usb_connected) {
+	if (driver->logging_mode != DIAG_USB_MODE) {
 		if (fwd_info->buf_1) {
 			atomic_set(&fwd_info->buf_1->in_busy, 0);
 			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
@@ -1701,7 +1700,8 @@
 
 	DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "issued a read p: %d t: %d buf: %pK\n",
 		 fwd_info->peripheral, fwd_info->type, read_buf);
-	err = fwd_info->p_ops->read(fwd_info->ctxt, read_buf, read_len);
+	err = fwd_info->p_ops->read(fwd_info->ctxt, read_buf, read_len,
+			temp_buf);
 	if (err)
 		goto fail_return;
 
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index 4124b17..d8e0b68 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -65,7 +65,8 @@
 	void (*open)(void *ctxt);
 	void (*close)(void *ctxt);
 	int (*write)(void *ctxt, unsigned char *buf, int len);
-	int (*read)(void *ctxt, unsigned char *buf, int len);
+	int (*read)(void *ctxt, unsigned char *buf, int len,
+			struct diagfwd_buf_t *fwd_buf);
 	void (*queue_read)(void *ctxt);
 };
 
diff --git a/drivers/char/diag/diagfwd_smd.c b/drivers/char/diag/diagfwd_smd.c
index 27433a7..d530204 100644
--- a/drivers/char/diag/diagfwd_smd.c
+++ b/drivers/char/diag/diagfwd_smd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -198,7 +198,8 @@
 static void diag_state_close_smd(void *ctxt);
 static void smd_notify(void *ctxt, unsigned int event);
 static int diag_smd_write(void *ctxt, unsigned char *buf, int len);
-static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len);
+static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf);
 static void diag_smd_queue_read(void *ctxt);
 
 static struct diag_peripheral_ops smd_ops = {
@@ -780,7 +781,8 @@
 	return 0;
 }
 
-static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len)
+static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf)
 {
 	int pkt_len = 0;
 	int err = 0;
@@ -791,7 +793,7 @@
 	uint32_t read_len = 0;
 	struct diag_smd_info *smd_info = NULL;
 
-	if (!ctxt || !buf || buf_len <= 0)
+	if (!ctxt || !buf || buf_len <= 0 || !fwd_buf)
 		return -EIO;
 
 	smd_info = (struct diag_smd_info *)ctxt;
@@ -810,7 +812,15 @@
 		diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
 		return -ERESTARTSYS;
 	}
-
+	if (atomic_read(&fwd_buf->in_busy) == 0) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"%s closing read thread. Buffer is already marked freed p: %d t: %d buf_num: %d\n",
+			 smd_info->name, GET_BUF_PERIPHERAL(fwd_buf->ctxt),
+			 GET_BUF_TYPE(fwd_buf->ctxt),
+			 GET_BUF_NUM(fwd_buf->ctxt));
+		diag_ws_release();
+		return 0;
+	}
 	/*
 	 * Reset the buffers. Also release the wake source hold earlier.
 	 */
diff --git a/drivers/char/diag/diagfwd_socket.c b/drivers/char/diag/diagfwd_socket.c
index f3c587d..401dbb0f 100644
--- a/drivers/char/diag/diagfwd_socket.c
+++ b/drivers/char/diag/diagfwd_socket.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -224,7 +224,8 @@
 static void diag_state_open_socket(void *ctxt);
 static void diag_state_close_socket(void *ctxt);
 static int diag_socket_write(void *ctxt, unsigned char *buf, int len);
-static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len);
+static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf);
 static void diag_socket_queue_read(void *ctxt);
 static void socket_init_work_fn(struct work_struct *work);
 static int socket_ready_notify(struct notifier_block *nb,
@@ -1062,7 +1063,8 @@
 	}
 }
 
-static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len)
+static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len,
+			struct diagfwd_buf_t *fwd_buf)
 {
 	int err = 0;
 	int pkt_len = 0;
@@ -1082,7 +1084,7 @@
 	if (!info)
 		return -ENODEV;
 
-	if (!buf || !ctxt || buf_len <= 0)
+	if (!buf || !ctxt || buf_len <= 0 || !fwd_buf)
 		return -EINVAL;
 
 	temp = buf;
@@ -1091,13 +1093,17 @@
 	err = wait_event_interruptible(info->read_wait_q,
 				      (info->data_ready > 0) || (!info->hdl) ||
 				      (atomic_read(&info->diag_state) == 0));
-	if (err) {
-		mutex_lock(&driver->diagfwd_channel_mutex[info->peripheral]);
-		diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
-		mutex_unlock(&driver->diagfwd_channel_mutex[info->peripheral]);
-		return -ERESTARTSYS;
+	if (err)
+		goto fail;
+	if (atomic_read(&fwd_buf->in_busy) == 0) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"%s closing read thread. Buffer is already marked freed p: %d t: %d buf_num: %d\n",
+			 info->name, GET_BUF_PERIPHERAL(fwd_buf->ctxt),
+			 GET_BUF_TYPE(fwd_buf->ctxt),
+			 GET_BUF_NUM(fwd_buf->ctxt));
+		diag_ws_release();
+		return 0;
 	}
-
 	/*
 	 * There is no need to continue reading over peripheral in this case.
 	 * Release the wake source hold earlier.
@@ -1106,10 +1112,7 @@
 		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
 			 "%s closing read thread. diag state is closed\n",
 			 info->name);
-		mutex_lock(&driver->diagfwd_channel_mutex[info->peripheral]);
-		diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
-		mutex_unlock(&driver->diagfwd_channel_mutex[info->peripheral]);
-		return 0;
+		goto fail;
 	}
 
 	if (!info->hdl) {
@@ -1211,7 +1214,7 @@
 	mutex_lock(&driver->diagfwd_channel_mutex[info->peripheral]);
 	diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
 	mutex_unlock(&driver->diagfwd_channel_mutex[info->peripheral]);
-	return -EIO;
+	return 0;
 }
 
 static int diag_socket_write(void *ctxt, unsigned char *buf, int len)