From 8d2c272d6ec11c71da5dc5d1f9ffe3ecbb3cd465 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Thu, 14 Jun 2012 19:00:31 -0700 Subject: [PATCH] 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 --- drivers/char/diag/diagfwd_hsic.c | 10 ++++++---- drivers/usb/misc/diag_bridge.c | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c index d54d3dc14ec..fedcf031643 100644 --- a/drivers/char/diag/diagfwd_hsic.c +++ b/drivers/char/diag/diagfwd_hsic.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ static void diag_read_hsic_work_fn(struct work_struct *work) 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 @@ static void diag_read_hsic_work_fn(struct work_struct *work) * 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 @@ static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr) 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 8e7bacfb901..8e80348191b 100644 --- a/drivers/usb/misc/diag_bridge.c +++ b/drivers/usb/misc/diag_bridge.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -97,7 +98,7 @@ static void diag_bridge_read_cb(struct urb *urb) 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 @@ int diag_bridge_read(char *data, int size) /* if there was a previous unrecoverable error, just quit */ if (dev->err) - return -ESHUTDOWN; + return -ENODEV; kref_get(&dev->kref); @@ -152,8 +153,8 @@ int diag_bridge_read(char *data, int size) } 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 @@ int diag_bridge_read(char *data, int size) 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 @@ static void diag_bridge_write_cb(struct urb *urb) 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 @@ int diag_bridge_write(char *data, int size) /* if there was a previous unrecoverable error, just quit */ if (dev->err) - return -ESHUTDOWN; + return -ENODEV; kref_get(&dev->kref); @@ -246,8 +247,8 @@ int diag_bridge_write(char *data, int size) } 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 @@ int diag_bridge_write(char *data, int size) 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);