[RFC 6/9] UAS: Kill anchored URBs for canceled SCSI commands.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If any of the four URBs that represent a SCSI command fail to be
submitted, they will be queued to the UAS work queue.  If the SCSI layer
wants to cancel that command later because it timed out, we need to be
able remove that command from the work queue, potentially out of the list
that the work queue function is currently walking across.

Make the work queue function take the work queue lock before it
manipulates any part of the work list, and make the command abort
handler take that lock before attempting to remove the item off the work
queue.  Make sure to initialize the list_head in the command during
allocation, and remove the item from the work queue if it was
successfully submitted.

Once the command is off the work queue, kill any anchored URBs (waiting
for them to complete), and free any URBs that were allocated but not
submitted.

Return FAILED for now, until we issue the ABORT TASK command to the
device (in the next patch).

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Cc: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/storage/uas.c |   43 ++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 2598cbf..f61de1f 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -144,19 +144,19 @@ static void uas_do_work(struct work_struct *work)
 	list_replace_init(&uas_work_list, &list);
 	spin_unlock_irq(&uas_work_lock);
 
+	spin_lock_irq(&uas_work_lock);
 	list_for_each_entry_safe(cmdinfo, temp, &list, list) {
 		struct scsi_pointer *scp = (void *)cmdinfo;
 		struct scsi_cmnd *cmnd = container_of(scp,
 							struct scsi_cmnd, SCp);
 		err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
+		list_del_init(&cmdinfo->list);
 		if (err) {
-			list_del(&cmdinfo->list);
-			spin_lock_irq(&uas_work_lock);
 			list_add_tail(&cmdinfo->list, &uas_work_list);
-			spin_unlock_irq(&uas_work_lock);
 			schedule_work(&uas_work);
 		}
 	}
+	spin_unlock_irq(&uas_work_lock);
 }
 
 static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
@@ -501,6 +501,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
 	cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB |
 			ALLOC_CMD_URB | SUBMIT_CMD_URB;
+	INIT_LIST_HEAD(&cmdinfo->list);
 
 	switch (cmnd->sc_data_direction) {
 	case DMA_FROM_DEVICE:
@@ -537,11 +538,47 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
 static DEF_SCSI_QCMD(uas_queuecommand)
 
+static void uas_kill_tagged_urbs(struct usb_anchor *anchor,
+		struct uas_cmd_info *cmdinfo)
+{
+	usb_kill_anchored_urbs(anchor);
+
+	/* Free any URBs that weren't submitted. */
+	if (cmdinfo->state & SUBMIT_STATUS_URB) {
+		kfree(cmdinfo->status_urb->transfer_buffer);
+		usb_free_urb(cmdinfo->status_urb);
+	}
+	if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
+		kfree(cmdinfo->data_in_urb->transfer_buffer);
+		usb_free_urb(cmdinfo->data_in_urb);
+	}
+	if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
+		kfree(cmdinfo->data_out_urb->transfer_buffer);
+		usb_free_urb(cmdinfo->data_out_urb);
+	}
+	if (cmdinfo->state & SUBMIT_CMD_URB) {
+		kfree(cmdinfo->cmd_urb->transfer_buffer);
+		usb_free_urb(cmdinfo->cmd_urb);
+	}
+}
+
 static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
 {
 	struct scsi_device *sdev = cmnd->device;
+	struct uas_dev_info *devinfo = sdev->hostdata;
+	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
 	sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
 							cmnd->request->tag);
+	spin_lock_irq(&uas_work_lock);
+	if (!list_empty(&cmdinfo->list))
+		list_del_init(&cmdinfo->list);
+	spin_unlock_irq(&uas_work_lock);
+
+	if (blk_rq_tagged(cmnd->request))
+		uas_kill_tagged_urbs(&devinfo->anchors[cmnd->request->tag], cmdinfo);
+	else
+		uas_kill_tagged_urbs(&devinfo->anchors[0], cmdinfo);
 
 /* XXX: Send ABORT TASK Task Management command */
 	return FAILED;
-- 
1.7.5.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux