[RFC 5/9] UAS: Introduce an URB anchor for command cancellation.

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

 



When the SCSI layer wants to cancel a command, the UAS driver needs to
be able to cancel the two to four URBs that make up the command:
command, data in (optional), data out (optional), and status URBs.  The
easiest way to do that is to create a usb_anchor per SCSI command tag,
and kill the anchored URBs when the SCSI core wants to cancel a command.
See Documentation/usb/anchors.txt for more info.

SCSI commands have a zero-based tag index (confirmed by James
Bottomley), but the xHCI driver has a one-based stream ID, so the anchor
with an index of 0 will have a tag of 0 and a stream ID of 1.  Untagged
SCSI commands have a request tag of -1, but the UAS driver submits them
on stream 1 (and ensures only one of them is active at a time), so they
end up in the zeroth entry in the anchor array.

Ideally, the usb_anchor structure would be in the uas_cmd_info
structure, along with the URB pointers.  Unfortunately, putting the
whole structure in uas_cmd_info makes it bigger than the scsi_pointer
structure that uas_cmd_info overrides.  There's enough room to add a
pointer to the usb_anchor, but then we run into race conditions with the
SCSI core canceling commands (where we need to use the anchor) and the
final status URB completing (where we need to free the anchor).  So we
create an array of usb_anchors in uas_probe once we know the SCSI queue
depth.

I had to reorder the probe function a bit to make sure we could allocate
the anchor array with the right size before adding the scsi_host, so
please double check this.

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

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index eda5f1d..2598cbf 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -98,6 +98,8 @@ struct uas_dev_info {
 	unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
 	unsigned use_streams:1;
 	unsigned uas_sense_old:1;
+	/* Array of anchors, one for each tag. */
+	struct usb_anchor *anchors;
 };
 
 enum {
@@ -387,6 +389,12 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
 					struct uas_dev_info *devinfo, gfp_t gfp)
 {
 	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+	struct usb_anchor *anchor;
+
+	if (blk_rq_tagged(cmnd->request))
+		anchor = &devinfo->anchors[cmnd->request->tag];
+	else
+		anchor = &devinfo->anchors[0];
 
 	if (cmdinfo->state & ALLOC_STATUS_URB) {
 		cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
@@ -397,9 +405,11 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
 	}
 
 	if (cmdinfo->state & SUBMIT_STATUS_URB) {
+		usb_anchor_urb(cmdinfo->status_urb, anchor);
 		if (usb_submit_urb(cmdinfo->status_urb, gfp)) {
 			scmd_printk(KERN_INFO, cmnd,
 					"sense urb submission failure\n");
+			usb_unanchor_urb(cmdinfo->status_urb);
 			return SCSI_MLQUEUE_DEVICE_BUSY;
 		}
 		cmdinfo->state &= ~SUBMIT_STATUS_URB;
@@ -415,9 +425,11 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
 	}
 
 	if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
+		usb_anchor_urb(cmdinfo->data_in_urb, anchor);
 		if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) {
 			scmd_printk(KERN_INFO, cmnd,
 					"data in urb submission failure\n");
+			usb_unanchor_urb(cmdinfo->data_in_urb);
 			return SCSI_MLQUEUE_DEVICE_BUSY;
 		}
 		cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
@@ -433,9 +445,11 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
 	}
 
 	if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
+		usb_anchor_urb(cmdinfo->data_out_urb, anchor);
 		if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) {
 			scmd_printk(KERN_INFO, cmnd,
 					"data out urb submission failure\n");
+			usb_unanchor_urb(cmdinfo->data_out_urb);
 			return SCSI_MLQUEUE_DEVICE_BUSY;
 		}
 		cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
@@ -450,9 +464,11 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
 	}
 
 	if (cmdinfo->state & SUBMIT_CMD_URB) {
+		usb_anchor_urb(cmdinfo->cmd_urb, anchor);
 		if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) {
 			scmd_printk(KERN_INFO, cmnd,
 					"cmd urb submission failure\n");
+			usb_unanchor_urb(cmdinfo->cmd_urb);
 			return SCSI_MLQUEUE_DEVICE_BUSY;
 		}
 		cmdinfo->state &= ~SUBMIT_CMD_URB;
@@ -705,7 +721,7 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
  */
 static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-	int result;
+	int result, i;
 	struct Scsi_Host *shost;
 	struct uas_dev_info *devinfo;
 	struct usb_device *udev = interface_to_usbdev(intf);
@@ -726,19 +742,28 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	shost->max_id = 1;
 	shost->sg_tablesize = udev->bus->sg_tablesize;
 
-	result = scsi_add_host(shost, &intf->dev);
-	if (result)
-		goto free;
-	shost->hostdata[0] = (unsigned long)devinfo;
-
 	devinfo->intf = intf;
 	devinfo->udev = udev;
 	uas_configure_endpoints(devinfo);
+	devinfo->anchors = kmalloc(sizeof(*devinfo->anchors)*devinfo->qdepth,
+			GFP_KERNEL);
+	if (!devinfo->anchors)
+		goto free;
+	for (i = 0; i < devinfo->qdepth; i++)
+		init_usb_anchor(&devinfo->anchors[i]);
+
+	result = scsi_add_host(shost, &intf->dev);
+	if (result)
+		goto free_anchors;
+	shost->hostdata[0] = (unsigned long)devinfo;
 
 	scsi_scan_host(shost);
 	usb_set_intfdata(intf, shost);
 	return result;
- free:
+
+free_anchors:
+	kfree(devinfo->anchors);
+free:
 	kfree(devinfo);
 	if (shost)
 		scsi_host_put(shost);
@@ -771,6 +796,7 @@ static void uas_disconnect(struct usb_interface *intf)
 	eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
 	usb_free_streams(intf, eps, 3, GFP_KERNEL);
 
+	kfree(devinfo->anchors);
 	kfree(devinfo);
 }
 
-- 
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