usb: diag_bridge: Fix error handling during disconnect

usb_autopm_get_interface() returns an error if runtime PM is disabled,
yet this should not impede the ability to submit URBs. This can happen
just as the device is removed but the interface driver's disconnect()
routine has not been called. Thus any client wishing to call read or
write may errorneously continue to call and might eventually flood the
console with excessive printks which could cause a watchdog timeout. So
treat -EAGAIN and -EACCES as okay and only bail on other error values.

Also, rate-limit the printks associated with reading/writing on the
bridge to further avoid the possibility of excessive logging during
similar corner cases.

Finally, use -ENODEV as an indicator that read/write should halt. This
will handle the case when an URB fails to submit late in the device
disconnect stage, and the client should not attempt any more submissions.

CRs-fixed: 370694
Change-Id: Ia5f4a4bd7ed620eedd2cd00c117066b8c7eb4f8c
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index d54d3dc..fedcf03 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -16,6 +16,7 @@
 #include <linux/diagchar.h>
 #include <linux/sched.h>
 #include <linux/err.h>
+#include <linux/ratelimit.h>
 #include <linux/workqueue.h>
 #include <linux/pm_runtime.h>
 #include <linux/platform_device.h>
@@ -55,7 +56,7 @@
 		err = diag_bridge_read((char *)driver->buf_in_hsic,
 					IN_BUF_SIZE);
 		if (err) {
-			pr_err("DIAG: Error initiating HSIC read, err: %d\n",
+			pr_err_ratelimited("DIAG: Error initiating HSIC read, err: %d\n",
 				err);
 			/*
 			 * If the error is recoverable, then clear
@@ -63,7 +64,7 @@
 			 * read on the next frame.  Otherwise, don't
 			 * resubmit a read on the next frame.
 			 */
-			if ((-ESHUTDOWN) != err)
+			if ((-ENODEV) != err)
 				driver->in_busy_hsic_read = 0;
 		}
 	}
@@ -373,14 +374,15 @@
 		err = diag_bridge_write(driver->usb_buf_mdm_out,
 					driver->read_len_mdm);
 		if (err) {
-			pr_err("DIAG: mdm data on hsic write err: %d\n", err);
+			pr_err_ratelimited("DIAG: mdm data on hsic write err: %d\n",
+					err);
 			/*
 			 * If the error is recoverable, then clear
 			 * the write flag, so we will resubmit a
 			 * write on the next frame.  Otherwise, don't
 			 * resubmit a write on the next frame.
 			 */
-			if ((-ESHUTDOWN) != err)
+			if ((-ENODEV) != err)
 				driver->in_busy_hsic_write = 0;
 		}
 	}
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 8e7bacf..8e80348 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/kref.h>
 #include <linux/platform_device.h>
+#include <linux/ratelimit.h>
 #include <linux/uaccess.h>
 #include <linux/usb.h>
 #include <linux/debugfs.h>
@@ -97,7 +98,7 @@
 
 	if (urb->status == -EPROTO) {
 		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
-		/* save error so that subsequent read/write returns ESHUTDOWN */
+		/* save error so that subsequent read/write returns ENODEV */
 		dev->err = urb->status;
 		kref_put(&dev->kref, diag_bridge_delete);
 		return;
@@ -140,7 +141,7 @@
 
 	/* if there was a previous unrecoverable error, just quit */
 	if (dev->err)
-		return -ESHUTDOWN;
+		return -ENODEV;
 
 	kref_get(&dev->kref);
 
@@ -152,8 +153,8 @@
 	}
 
 	ret = usb_autopm_get_interface(dev->ifc);
-	if (ret < 0) {
-		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+	if (ret < 0 && ret != -EAGAIN && ret != -EACCES) {
+		pr_err_ratelimited("read: autopm_get failed:%d", ret);
 		goto free_error;
 	}
 
@@ -165,7 +166,7 @@
 
 	ret = usb_submit_urb(urb, GFP_KERNEL);
 	if (ret) {
-		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		pr_err_ratelimited("submitting urb failed err:%d", ret);
 		dev->pending_reads--;
 		usb_unanchor_urb(urb);
 	}
@@ -191,7 +192,7 @@
 
 	if (urb->status == -EPROTO) {
 		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
-		/* save error so that subsequent read/write returns ESHUTDOWN */
+		/* save error so that subsequent read/write returns ENODEV */
 		dev->err = urb->status;
 		kref_put(&dev->kref, diag_bridge_delete);
 		return;
@@ -234,7 +235,7 @@
 
 	/* if there was a previous unrecoverable error, just quit */
 	if (dev->err)
-		return -ESHUTDOWN;
+		return -ENODEV;
 
 	kref_get(&dev->kref);
 
@@ -246,8 +247,8 @@
 	}
 
 	ret = usb_autopm_get_interface(dev->ifc);
-	if (ret < 0) {
-		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+	if (ret < 0 && ret != -EAGAIN && ret != -EACCES) {
+		pr_err_ratelimited("write: autopm_get failed:%d", ret);
 		goto free_error;
 	}
 
@@ -259,7 +260,7 @@
 
 	ret = usb_submit_urb(urb, GFP_KERNEL);
 	if (ret) {
-		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		pr_err_ratelimited("submitting urb failed err:%d", ret);
 		dev->pending_writes--;
 		usb_unanchor_urb(urb);
 		usb_autopm_put_interface(dev->ifc);