aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/uas.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2014-09-13 12:26:41 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-23 21:42:11 -0700
commit4c5481efb4346948ba7034432f86235a16ac9180 (patch)
treebacb19c8689964e38d98f7e6878b7dfbca7a667e /drivers/usb/storage/uas.c
parent616f0e6cab4698309ff9e48ee2a85b5eb78cf31a (diff)
downloadlinux-4c5481efb4346948ba7034432f86235a16ac9180.tar.gz
uas: Fix memleak of non-submitted urbs
Not all urbs we've allocated are necessarily also submitted, non-submitted urbs will not be free-ed by their completion handler. So we need to free them manually. There are 2 scenarios where this can happen: 1) We have failed to submit some urbs at abort / disconnect 2) When running over usb-2 we may have never tried to submit the data urbs when completing the scsi cmnd, because we never got a READ/WRITE_READY iu Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/storage/uas.c')
-rw-r--r--drivers/usb/storage/uas.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index b1a1acb434615..10a3dea041ed1 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -249,6 +249,25 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
(ci->state & IS_IN_WORK_LIST) ? " work" : "");
}
+static void uas_free_unsubmitted_urbs(struct scsi_cmnd *cmnd)
+{
+ struct uas_cmd_info *cmdinfo;
+
+ if (!cmnd)
+ return;
+
+ cmdinfo = (void *)&cmnd->SCp;
+
+ if (cmdinfo->state & SUBMIT_CMD_URB)
+ usb_free_urb(cmdinfo->cmd_urb);
+
+ /* data urbs may have never gotten their submit flag set */
+ if (!(cmdinfo->state & DATA_IN_URB_INFLIGHT))
+ usb_free_urb(cmdinfo->data_in_urb);
+ if (!(cmdinfo->state & DATA_OUT_URB_INFLIGHT))
+ usb_free_urb(cmdinfo->data_out_urb);
+}
+
static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
{
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
@@ -263,6 +282,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED);
cmdinfo->state |= COMMAND_COMPLETED;
devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL;
+ uas_free_unsubmitted_urbs(cmnd);
cmnd->scsi_done(cmnd);
return 0;
}
@@ -738,6 +758,8 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+ uas_free_unsubmitted_urbs(cmnd);
+
spin_unlock_irqrestore(&devinfo->lock, flags);
if (data_in_urb) {