diff options
author | Hans de Goede <hdegoede@redhat.com> | 2013-10-29 05:51:00 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2014-03-04 18:38:20 -0500 |
commit | b83b86a352280cc8cbbf3760096c703986143b81 (patch) | |
tree | 170a36d2084123e9fdd4a019bb43db7c44d4edeb /drivers/usb/storage | |
parent | 70cf0fba7625987ef16085f458e3869c6e3043c1 (diff) |
uas: Don't allow more then one task to run at the same time
Since we use a fixed tag / stream for tasks we cannot allow more then one
to run at the same time. This could happen before this time if a task timed
out.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/uas.c | 39 |
1 files changed, 34 insertions, 5 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 9c6f9f9804fd..7fc4ad207752 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c | |||
@@ -53,6 +53,7 @@ struct uas_dev_info { | |||
53 | unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; | 53 | unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; |
54 | unsigned use_streams:1; | 54 | unsigned use_streams:1; |
55 | unsigned uas_sense_old:1; | 55 | unsigned uas_sense_old:1; |
56 | unsigned running_task:1; | ||
56 | struct scsi_cmnd *cmnd; | 57 | struct scsi_cmnd *cmnd; |
57 | spinlock_t lock; | 58 | spinlock_t lock; |
58 | struct work_struct work; | 59 | struct work_struct work; |
@@ -195,6 +196,7 @@ static void uas_zap_dead(struct uas_dev_info *devinfo) | |||
195 | DATA_OUT_URB_INFLIGHT); | 196 | DATA_OUT_URB_INFLIGHT); |
196 | uas_try_complete(cmnd, __func__); | 197 | uas_try_complete(cmnd, __func__); |
197 | } | 198 | } |
199 | devinfo->running_task = 0; | ||
198 | spin_unlock_irqrestore(&devinfo->lock, flags); | 200 | spin_unlock_irqrestore(&devinfo->lock, flags); |
199 | } | 201 | } |
200 | 202 | ||
@@ -340,6 +342,9 @@ static void uas_stat_cmplt(struct urb *urb) | |||
340 | 342 | ||
341 | if (!cmnd) { | 343 | if (!cmnd) { |
342 | if (iu->iu_id == IU_ID_RESPONSE) { | 344 | if (iu->iu_id == IU_ID_RESPONSE) { |
345 | if (!devinfo->running_task) | ||
346 | dev_warn(&urb->dev->dev, | ||
347 | "stat urb: recv unexpected response iu\n"); | ||
343 | /* store results for uas_eh_task_mgmt() */ | 348 | /* store results for uas_eh_task_mgmt() */ |
344 | memcpy(&devinfo->response, iu, sizeof(devinfo->response)); | 349 | memcpy(&devinfo->response, iu, sizeof(devinfo->response)); |
345 | } | 350 | } |
@@ -726,14 +731,26 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, | |||
726 | u16 tag = devinfo->qdepth; | 731 | u16 tag = devinfo->qdepth; |
727 | unsigned long flags; | 732 | unsigned long flags; |
728 | struct urb *sense_urb; | 733 | struct urb *sense_urb; |
734 | int result = SUCCESS; | ||
729 | 735 | ||
730 | spin_lock_irqsave(&devinfo->lock, flags); | 736 | spin_lock_irqsave(&devinfo->lock, flags); |
737 | |||
738 | if (devinfo->running_task) { | ||
739 | shost_printk(KERN_INFO, shost, | ||
740 | "%s: %s: error already running a task\n", | ||
741 | __func__, fname); | ||
742 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
743 | return FAILED; | ||
744 | } | ||
745 | |||
746 | devinfo->running_task = 1; | ||
731 | memset(&devinfo->response, 0, sizeof(devinfo->response)); | 747 | memset(&devinfo->response, 0, sizeof(devinfo->response)); |
732 | sense_urb = uas_submit_sense_urb(shost, GFP_ATOMIC, tag); | 748 | sense_urb = uas_submit_sense_urb(shost, GFP_ATOMIC, tag); |
733 | if (!sense_urb) { | 749 | if (!sense_urb) { |
734 | shost_printk(KERN_INFO, shost, | 750 | shost_printk(KERN_INFO, shost, |
735 | "%s: %s: submit sense urb failed\n", | 751 | "%s: %s: submit sense urb failed\n", |
736 | __func__, fname); | 752 | __func__, fname); |
753 | devinfo->running_task = 0; | ||
737 | spin_unlock_irqrestore(&devinfo->lock, flags); | 754 | spin_unlock_irqrestore(&devinfo->lock, flags); |
738 | return FAILED; | 755 | return FAILED; |
739 | } | 756 | } |
@@ -741,6 +758,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, | |||
741 | shost_printk(KERN_INFO, shost, | 758 | shost_printk(KERN_INFO, shost, |
742 | "%s: %s: submit task mgmt urb failed\n", | 759 | "%s: %s: submit task mgmt urb failed\n", |
743 | __func__, fname); | 760 | __func__, fname); |
761 | devinfo->running_task = 0; | ||
744 | spin_unlock_irqrestore(&devinfo->lock, flags); | 762 | spin_unlock_irqrestore(&devinfo->lock, flags); |
745 | usb_kill_urb(sense_urb); | 763 | usb_kill_urb(sense_urb); |
746 | return FAILED; | 764 | return FAILED; |
@@ -748,23 +766,33 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, | |||
748 | spin_unlock_irqrestore(&devinfo->lock, flags); | 766 | spin_unlock_irqrestore(&devinfo->lock, flags); |
749 | 767 | ||
750 | if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { | 768 | if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { |
769 | /* | ||
770 | * Note we deliberately do not clear running_task here. If we | ||
771 | * allow new tasks to be submitted, there is no way to figure | ||
772 | * out if a received response_iu is for the failed task or for | ||
773 | * the new one. A bus-reset will eventually clear running_task. | ||
774 | */ | ||
751 | shost_printk(KERN_INFO, shost, | 775 | shost_printk(KERN_INFO, shost, |
752 | "%s: %s timed out\n", __func__, fname); | 776 | "%s: %s timed out\n", __func__, fname); |
753 | return FAILED; | 777 | return FAILED; |
754 | } | 778 | } |
779 | |||
780 | spin_lock_irqsave(&devinfo->lock, flags); | ||
781 | devinfo->running_task = 0; | ||
755 | if (be16_to_cpu(devinfo->response.tag) != tag) { | 782 | if (be16_to_cpu(devinfo->response.tag) != tag) { |
756 | shost_printk(KERN_INFO, shost, | 783 | shost_printk(KERN_INFO, shost, |
757 | "%s: %s failed (wrong tag %d/%d)\n", __func__, | 784 | "%s: %s failed (wrong tag %d/%d)\n", __func__, |
758 | fname, be16_to_cpu(devinfo->response.tag), tag); | 785 | fname, be16_to_cpu(devinfo->response.tag), tag); |
759 | return FAILED; | 786 | result = FAILED; |
760 | } | 787 | } else if (devinfo->response.response_code != RC_TMF_COMPLETE) { |
761 | if (devinfo->response.response_code != RC_TMF_COMPLETE) { | ||
762 | shost_printk(KERN_INFO, shost, | 788 | shost_printk(KERN_INFO, shost, |
763 | "%s: %s failed (rc 0x%x)\n", __func__, | 789 | "%s: %s failed (rc 0x%x)\n", __func__, |
764 | fname, devinfo->response.response_code); | 790 | fname, devinfo->response.response_code); |
765 | return FAILED; | 791 | result = FAILED; |
766 | } | 792 | } |
767 | return SUCCESS; | 793 | spin_unlock_irqrestore(&devinfo->lock, flags); |
794 | |||
795 | return result; | ||
768 | } | 796 | } |
769 | 797 | ||
770 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) | 798 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) |
@@ -982,6 +1010,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) | |||
982 | devinfo->intf = intf; | 1010 | devinfo->intf = intf; |
983 | devinfo->udev = udev; | 1011 | devinfo->udev = udev; |
984 | devinfo->resetting = 0; | 1012 | devinfo->resetting = 0; |
1013 | devinfo->running_task = 0; | ||
985 | init_usb_anchor(&devinfo->cmd_urbs); | 1014 | init_usb_anchor(&devinfo->cmd_urbs); |
986 | init_usb_anchor(&devinfo->sense_urbs); | 1015 | init_usb_anchor(&devinfo->sense_urbs); |
987 | init_usb_anchor(&devinfo->data_urbs); | 1016 | init_usb_anchor(&devinfo->data_urbs); |