2010-11-06 04:46:56

by Luben Tuikov

[permalink] [raw]
Subject: [PATCH] [USB] UAS: Achitecture; TMF; more

* Fix sense IU layout

* Command delivery to the service delivery subsystem should
be an atomic transaction. Fix this in this driver by
removing the work thread which retried allocating and
sending urbs. First allocate the urbs we'll need up
front; second recycle them (sense<-RRIU in High-Speed
UAS); third if any operation with the SDS fails, report
this to the application client.

* Rename uas_dev_info->uas_tport_info to more correctly
portray the capabilities of the target port. Decouple
that from the sdev structure as it now has a uas_lu_info
(new) which assists in TMF processing and error
recovery. So now uas_cmd_info lives in scsi_cmd;
uas_lu_info lives in scsi_device and uas_tport_info lives
in scsi_host.

* Redo uas_sense. Make it more general and print a more
informative message if the sense data were short.

* uas_xfer_data for High-Speed doesn't retry but fails if
the SDS is down. This means that we cannot reach the
target port, so we let error recovery kick in.

* Add common urb initializers/fillers for the command and
status pipe. This generalizes output transactions on the
command pipe to always free the urb and the buffer, and
input transactions on the status pipe to always have a
buffer holding the max size of struct sense_iu and
complete in the same place, uas_stat_cmplt, in order to
accommodate both High-Speed devices and SuperSpeed
(USB3.0) devices.

* Gracefully free the urbs if the SDS is down or we have no
memory for all urbs, at queuecommand entry.

* Fix a shortcoming of SCSI Core where some commands coming
into the driver are NOT tagged and some are tagged. The
SDS (UAS) needs everything to be tagged and tagged
correctly. Use tag 1 for untagged commands and tags 2 to
FFEFh for tagged commands. In practice we use much less
and target ports will report much less than that.

* If the target port is SuperSpeed request a macro defined
number of streams (128) for this I_T nexus. Target ports
will report less. If usb_alloc_streams complains, we know
we can handle at least one stream. Else if the port is
High-Speed, set the number of tags we use to some sane
value (32). Else the desired number of tags is set.

* Implement TMF. All TMFs are supported. As we're not in
control of the tags, nor can we, at the time of this
commit, request a free tag from the block layer, we
allocate tags for TMFs with range [130, 1153]
(130+1024-1), as an increasing sequence.

Signed-off-by: Luben Tuikov <[email protected]>
---

This patch changes 86% of the driver. More is to come, but this is
a self-contained patch so it can be applied.

drivers/usb/storage/uas.c | 970 ++++++++++++++++++++++++++++++---------------
1 files changed, 656 insertions(+), 314 deletions(-)

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index ef6e707..ed1a82f 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -4,6 +4,7 @@
*
* Copyright Matthew Wilcox for Intel Corp, 2010
* Copyright Sarah Sharp for Intel Corp, 2010
+ * Copyright Luben Tuikov, 2010
*
* Distributed under the terms of the GNU GPL, version two.
*/
@@ -13,6 +14,7 @@
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/storage.h>
+#include <linux/completion.h>

#include <scsi/scsi.h>
#include <scsi/scsi_dbg.h>
@@ -29,12 +31,18 @@
#define UAS_DPRINTK(fmt, ...)
#endif

+/* Define the number of streams to ask to allocate, essentially
+ * the maximum number of tags which we are asking to be able to send
+ * to the device. Devices may report much less.
+ */
+#define UAS_MAX_STREAMS 128
+
/* Common header for all IUs */
struct iu {
__u8 iu_id;
__u8 rsvd1;
__be16 tag;
-};
+} __packed;

enum {
IU_ID_COMMAND = 0x01,
@@ -55,7 +63,7 @@ struct command_iu {
__u8 rsvd7;
struct scsi_lun lun;
__u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */
-};
+} __packed;

struct sense_iu {
__u8 iu_id;
@@ -63,11 +71,12 @@ struct sense_iu {
__be16 tag;
__be16 status_qual;
__u8 status;
- __u8 service_response;
- __u8 rsvd8[6];
+ __u8 rsvd8[7];
__be16 len;
__u8 sense[SCSI_SENSE_BUFFERSIZE];
-};
+} __packed;
+
+#define SENSE_IU_SIZE sizeof(struct sense_iu)

/*
* The r00-r01c specs define this version of the SENSE IU data structure.
@@ -81,6 +90,56 @@ struct sense_iu_old {
__u8 status;
__u8 service_response;
__u8 sense[SCSI_SENSE_BUFFERSIZE];
+} __packed;
+
+struct tmf_iu {
+ __u8 iu_id;
+ __u8 rsvd1;
+ __be16 tag;
+ __u8 tmf;
+ __u8 rsvd5;
+ __u16 ttbm;
+ struct scsi_lun lun;
+} __packed;
+
+enum {
+ TMF_ABORT_TASK = 1,
+ TMF_ABORT_TASK_SET = 2,
+ TMF_CLEAR_TASK_SET = 4,
+ TMF_LU_RESET = 8,
+ TMF_IT_NEXUS_RESET = 0x10,
+ TMF_CLEAR_ACA = 0x40,
+ TMF_QUERY_TASK = 0x80,
+ TMF_QUERY_TASK_SET = 0x81,
+ TMF_QUERY_ASYNC_EVENT = 0x82,
+};
+
+struct resp_iu {
+ __u8 iu_id;
+ __u8 rsvd1;
+ __be16 tag;
+ __be32 resp;
+} __packed;
+
+#define TMR_RESPONSE_CODE_MASK 0xFF
+#define TMR_RESPONSE_CODE_SHIFT 0
+#define TMR_RESPONSE_INFO_MASK 0xFFFFFF00
+#define TMR_RESPONSE_INFO_SHIFT 8
+
+#define TMR_RESPONSE_CODE(__Val) \
+ (((__Val) & TMR_RESPONSE_CODE_MASK) >> TMR_RESPONSE_CODE_SHIFT)
+
+#define TMR_RESPONSE_INFO(__Val) \
+ (((__Val) & TMR_RESPONSE_INFO_MASK) >> TMR_RESPONSE_INFO_SHIFT)
+
+enum tmf_resp_code {
+ TMR_COMPLETE = 0,
+ TMR_IIU = 2,
+ TMR_UNSUPP = 4,
+ TMR_FAILED = 5,
+ TMR_SUCC = 8,
+ TMR_ILUN = 9,
+ TMR_OLAP = 0xA,
};

enum {
@@ -95,89 +154,73 @@ enum {
UAS_ACA = 4,
};

-struct uas_dev_info {
+/* Lives in the SCSI host, hostdata points to it.
+ */
+struct uas_tport_info {
struct usb_interface *intf;
struct usb_device *udev;
- int qdepth;
+ int num_tags;
unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
unsigned use_streams:1;
unsigned uas_sense_old:1;
};

-enum {
- ALLOC_STATUS_URB = (1 << 0),
- SUBMIT_STATUS_URB = (1 << 1),
- ALLOC_DATA_IN_URB = (1 << 2),
- SUBMIT_DATA_IN_URB = (1 << 3),
- ALLOC_DATA_OUT_URB = (1 << 4),
- SUBMIT_DATA_OUT_URB = (1 << 5),
- ALLOC_CMD_URB = (1 << 6),
- SUBMIT_CMD_URB = (1 << 7),
+/* Lives in the scsi device, hostdata points to it.
+ */
+struct uas_lu_info {
+ struct completion tmf_completion;
+ struct resp_iu resp;
+ struct urb *freed_urb;
};

-/* Overrides scsi_pointer */
+/* Lives in the scsi command, overrides SCp.
+ */
struct uas_cmd_info {
- unsigned int state;
- unsigned int stream;
+ int tag;
struct urb *cmd_urb;
struct urb *status_urb;
struct urb *data_in_urb;
struct urb *data_out_urb;
- struct list_head list;
};

-static int uas_submit_urbs(struct scsi_cmnd *cmnd,
- struct uas_dev_info *devinfo, gfp_t gfp);
+#define UAS_CMD_INFO(__Scmd) ((struct uas_cmd_info *)(&(__Scmd)->SCp))
+#define UAS_TPORT_INFO(__Scmd) \
+ ((struct uas_tport_info *)((__Scmd)->device->host->hostdata[0]))
+#define SDEV_TPORT_INFO(__Sdev) \
+ ((struct uas_tport_info *)((__Sdev)->host->hostdata[0]))

-static DEFINE_SPINLOCK(uas_work_lock);
-static LIST_HEAD(uas_work_list);
+#define SDEV_LU_INFO(__Sdev) ((struct uas_lu_info *)((__Sdev)->hostdata))

-static void uas_do_work(struct work_struct *work)
-{
- struct uas_cmd_info *cmdinfo;
- struct list_head list;
- int res;
-
- spin_lock_irq(&uas_work_lock);
- list_replace_init(&uas_work_list, &list);
- spin_unlock_irq(&uas_work_lock);
-
- list_for_each_entry(cmdinfo, &list, list) {
- struct scsi_pointer *scp = (void *)cmdinfo;
- struct scsi_cmnd *cmnd = container_of(scp,
- struct scsi_cmnd, SCp);
- res = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_KERNEL);
- UAS_DPRINTK("%s: cmd:%p, res:%d, state:0x%x\n", __FUNCTION__,
- cmnd, res, cmdinfo->state);
- }
-}
-
-static DECLARE_WORK(uas_work, uas_do_work);
+/* ---------- IU processors ---------- */

-static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
+static void uas_sense(struct urb *urb, struct scsi_cmnd *cmd)
{
struct sense_iu *sense_iu = urb->transfer_buffer;
- struct scsi_device *sdev = cmnd->device;
+ struct scsi_device *sdev = cmd->device;
+
+ cmd->result = sense_iu->status;

if (urb->actual_length > 16) {
- unsigned len = be16_to_cpup(&sense_iu->len);
- if (len + 16 != urb->actual_length) {
- int newlen = min(len + 16, urb->actual_length) - 16;
- if (newlen < 0)
- newlen = 0;
- sdev_printk(KERN_INFO, sdev, "%s: urb length %d "
- "disagrees with IU sense data length %d, "
- "using %d bytes of sense data\n", __func__,
- urb->actual_length, len, newlen);
- len = newlen;
+ unsigned slen = be16_to_cpup(&sense_iu->len);
+
+ if (urb->actual_length >= 16 + slen) {
+ slen = min(slen, (unsigned) SCSI_SENSE_BUFFERSIZE);
+ } else {
+ unsigned dlen = slen;
+
+ slen = min(urb->actual_length - 16,
+ (unsigned) SCSI_SENSE_BUFFERSIZE);
+
+ sdev_printk(KERN_INFO, sdev,
+ "%s: short SENSE IU by %d bytes, "
+ "using %d/%d bytes of sense data\n",
+ __func__, 16 + slen - urb->actual_length,
+ slen, dlen);
}
- memcpy(cmnd->sense_buffer, sense_iu->sense, len);
+ memcpy(cmd->sense_buffer, sense_iu->sense, slen);
}
-
- cmnd->result = sense_iu->status;
- if (sdev->current_cmnd)
- sdev->current_cmnd = NULL;
- cmnd->scsi_done(cmnd);
+ sdev->current_cmnd = NULL;
+ cmd->scsi_done(cmd);
usb_free_urb(urb);
}

@@ -200,73 +243,105 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
}
memcpy(cmnd->sense_buffer, sense_iu->sense, len);
}
-
cmnd->result = sense_iu->status;
- if (sdev->current_cmnd)
- sdev->current_cmnd = NULL;
+ sdev->current_cmnd = NULL;
cmnd->scsi_done(cmnd);
usb_free_urb(urb);
}

-static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
- unsigned direction)
+/* Let High-Speed devices fail here instead of accommodating EAGAIN
+ * and racing with the error handler, if EAGAIN takes longer than the
+ * command completion timeout. SuperSpeed devices never get here.
+ */
+static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmd,
+ struct uas_tport_info *tpinfo,
+ enum dma_data_direction dir, int tag)
{
- struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
- int err;
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ int res = 0;
+
+ res = usb_submit_urb(urb, GFP_ATOMIC);

- cmdinfo->state = direction | SUBMIT_STATUS_URB;
- err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
- UAS_DPRINTK("%s: cmd:%p, err:%d, state:0x%x\n", __FUNCTION__,
- cmnd, err, cmdinfo->state);
- if (err) {
- spin_lock(&uas_work_lock);
- list_add_tail(&cmdinfo->list, &uas_work_list);
- spin_unlock(&uas_work_lock);
- schedule_work(&uas_work);
+ if (res == 0) {
+ if (dir == DMA_FROM_DEVICE)
+ res = usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC);
+ else
+ res = usb_submit_urb(cmdinfo->data_out_urb,GFP_ATOMIC);
}
+
+ UAS_DPRINTK("%s: cmd:%p res:%d tag:%d\n", __func__, cmd, res,
+ cmdinfo->tag);
}

+/**
+ * uas_stat_cmplt -- Status pipe urb completion
+ * @urb: the URB that completed
+ *
+ * Anything we expect to come back on the status pipe
+ * comes here.
+ */
static void uas_stat_cmplt(struct urb *urb)
{
struct iu *iu = urb->transfer_buffer;
struct scsi_device *sdev = urb->context;
- struct uas_dev_info *devinfo = sdev->hostdata;
+ struct uas_tport_info *tpinfo = SDEV_TPORT_INFO(sdev);
struct scsi_cmnd *cmnd;
- u16 tag;
+ int tag;
+
+ UAS_DPRINTK("%s: IU ID:%d tag:%d\n", __func__, iu->iu_id,
+ be16_to_cpup(&iu->tag));

if (urb->status) {
dev_err(&urb->dev->dev, "%s: URB BAD STATUS %d\n",
- __FUNCTION__, urb->status);
+ __func__, urb->status);
+ usb_free_urb(urb);
+ return;
+ }
+
+ if (iu->iu_id == IU_ID_RESPONSE) {
+ struct uas_lu_info *luinfo = SDEV_LU_INFO(sdev);
+
+ memcpy(&luinfo->resp, urb->transfer_buffer,
+ sizeof(struct resp_iu));
+ complete(&luinfo->tmf_completion);
+ luinfo->freed_urb = urb;
usb_free_urb(urb);
return;
}

- tag = be16_to_cpup(&iu->tag) - 1;
+ tag = be16_to_cpup(&iu->tag);
if (sdev->current_cmnd)
cmnd = sdev->current_cmnd;
else
- cmnd = scsi_find_tag(sdev, tag);
- if (!cmnd)
+ cmnd = scsi_find_tag(sdev, tag-2);
+
+ if (!cmnd) {
+ UAS_DPRINTK("%s: No command!?\n", __func__);
+ usb_free_urb(urb);
return;
+ }

switch (iu->iu_id) {
case IU_ID_STATUS:
- if (urb->actual_length < 16)
- devinfo->uas_sense_old = 1;
- if (devinfo->uas_sense_old)
+ if (unlikely(urb->actual_length < 16))
+ tpinfo->uas_sense_old = 1;
+ if (unlikely(tpinfo->uas_sense_old))
uas_sense_old(urb, cmnd);
else
uas_sense(urb, cmnd);
break;
case IU_ID_READ_READY:
- uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
+ uas_xfer_data(urb, cmnd, tpinfo, DMA_FROM_DEVICE, tag);
break;
case IU_ID_WRITE_READY:
- uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
+ uas_xfer_data(urb, cmnd, tpinfo, DMA_TO_DEVICE, tag);
break;
default:
scmd_printk(KERN_ERR, cmnd,
- "Bogus IU (%d) received on status pipe\n", iu->iu_id);
+ "Unknown IU ID %d received on the status pipe\n",
+ iu->iu_id);
+ usb_free_urb(urb);
+ break;
}
}

@@ -274,333 +349,594 @@ static void uas_data_cmplt(struct urb *urb)
{
struct scsi_data_buffer *sdb = urb->context;

- if (urb->status) {
+ if (!urb->status)
+ sdb->resid = sdb->length - urb->actual_length;
+ else
dev_err(&urb->dev->dev, "%s: URB BAD STATUS %d\n",
- __FUNCTION__, urb->status);
- usb_free_urb(urb);
- return;
- }
-
- sdb->resid = sdb->length - urb->actual_length;
+ __func__, urb->status);
+
usb_free_urb(urb);
}

-static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
- unsigned int pipe, u16 stream_id,
- struct scsi_data_buffer *sdb,
- enum dma_data_direction dir)
+/* ---------- URB allocators and submission ---------- */
+
+/**
+ * uas_fill_cmdp_urb -- Fill in a command pipe urb
+ * @urb: the urb
+ * @tpinfo: the UAS device info struct
+ * @transfer_buffer: the IU
+ * @buffer_length: the size of the IU
+ *
+ * A unified place to initialize a command pipe urb so that
+ * all command pipe urbs are freed in the same manner.
+ */
+static void uas_fill_cmdp_urb(struct urb *urb, struct uas_tport_info *tpinfo,
+ void *transfer_buffer, int buffer_length)
+{
+ usb_fill_bulk_urb(urb, tpinfo->udev, tpinfo->cmd_pipe,
+ transfer_buffer, buffer_length,
+ usb_free_urb, NULL);
+ urb->transfer_flags |= URB_FREE_BUFFER;
+}
+
+/**
+ * uas_fill_statp_urb -- Fill in a status pipe urb
+ * @urb: the urb
+ * @dev: the scsi device
+ * @transfer_buffer: the transfer buffer of size SENSE_IU_SIZE
+ * @tag: the tag
+ *
+ * The callback is responsible to free/recycle the urb and the
+ * transfer buffer.
+ */
+static void uas_fill_statp_urb(struct urb *urb, struct scsi_device *sdev,
+ void *transfer_buffer, int tag)
+{
+ struct uas_tport_info *tpinfo = SDEV_TPORT_INFO(sdev);
+
+ usb_fill_bulk_urb(urb, tpinfo->udev, tpinfo->status_pipe,
+ transfer_buffer, SENSE_IU_SIZE,
+ uas_stat_cmplt, sdev);
+ urb->transfer_flags |= URB_FREE_BUFFER;
+ if (tpinfo->use_streams)
+ urb->stream_id = tag;
+}
+
+static struct urb *uas_alloc_data_urb(struct uas_tport_info *tpinfo, gfp_t gfp,
+ unsigned int data_pipe, u16 tag,
+ struct scsi_data_buffer *sdb)
{
- struct usb_device *udev = devinfo->udev;
+ struct usb_device *udev = tpinfo->udev;
struct urb *urb = usb_alloc_urb(0, gfp);

if (!urb)
- goto out;
- usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt,
- sdb);
- if (devinfo->use_streams)
- urb->stream_id = stream_id;
+ goto Out;
+
+ usb_fill_bulk_urb(urb, udev, data_pipe, NULL, sdb->length,
+ uas_data_cmplt, sdb);
+ if (tpinfo->use_streams)
+ urb->stream_id = tag;
urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
urb->sg = sdb->table.sgl;
- out:
+ Out:
return urb;
}

-static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
- struct scsi_cmnd *cmnd, u16 stream_id)
+static struct urb *uas_alloc_status_urb(struct scsi_cmnd *cmd, gfp_t gfp)
+
{
- struct usb_device *udev = devinfo->udev;
- struct urb *urb = usb_alloc_urb(0, gfp);
- struct sense_iu *iu;
+ struct sense_iu *siu;
+ struct urb *urb;

+ urb = usb_alloc_urb(0, gfp);
if (!urb)
- goto out;
+ return NULL;

- iu = kzalloc(sizeof(*iu), gfp);
- if (!iu)
- goto free;
+ siu = kzalloc(SENSE_IU_SIZE, gfp);
+ if (!siu)
+ goto Out_free;
+
+ uas_fill_statp_urb(urb, cmd->device, siu, UAS_CMD_INFO(cmd)->tag);

- usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu),
- uas_stat_cmplt, cmnd->device);
- urb->stream_id = stream_id;
- urb->transfer_flags |= URB_FREE_BUFFER;
- out:
return urb;
- free:
+ Out_free:
usb_free_urb(urb);
return NULL;
}

-static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
- struct scsi_cmnd *cmnd, u16 stream_id)
+static struct urb *uas_alloc_cmd_urb(struct scsi_cmnd *cmd, gfp_t gfp)
{
- struct usb_device *udev = devinfo->udev;
- struct scsi_device *sdev = cmnd->device;
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ struct uas_tport_info *tpinfo = UAS_TPORT_INFO(cmd);
+ struct usb_device *udev = tpinfo->udev;
+ struct scsi_device *sdev = cmd->device;
struct urb *urb = usb_alloc_urb(0, gfp);
- struct command_iu *iu;
+ struct command_iu *ciu;
int len;

if (!urb)
- goto out;
+ return NULL;

- len = cmnd->cmd_len - 16;
+ len = cmd->cmd_len - 16;
if (len < 0)
len = 0;
- len = ALIGN(len, 4);
- iu = kzalloc(sizeof(*iu) + len, gfp);
- if (!iu)
+ else
+ len = ALIGN(len, 4);
+
+ ciu = kzalloc(sizeof(*ciu) + len, gfp);
+ if (!ciu)
goto free;

- iu->iu_id = IU_ID_COMMAND;
- iu->tag = cpu_to_be16(stream_id);
- if (sdev->ordered_tags && (cmnd->request->cmd_flags & REQ_HARDBARRIER))
- iu->prio_attr = UAS_ORDERED_TAG;
+ ciu->iu_id = IU_ID_COMMAND;
+ ciu->tag = cpu_to_be16(cmdinfo->tag);
+ if (sdev->ordered_tags && (cmd->request->cmd_flags & REQ_HARDBARRIER))
+ ciu->prio_attr = UAS_ORDERED_TAG;
else
- iu->prio_attr = UAS_SIMPLE_TAG;
- iu->len = len;
- int_to_scsilun(sdev->lun, &iu->lun);
- memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);
+ ciu->prio_attr = UAS_SIMPLE_TAG;
+ ciu->len = len;
+ int_to_scsilun(sdev->lun, &ciu->lun);
+ memcpy(ciu->cdb, cmd->cmnd, cmd->cmd_len);

- usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len,
+ usb_fill_bulk_urb(urb, udev, tpinfo->cmd_pipe, ciu, sizeof(*ciu) + len,
usb_free_urb, NULL);
urb->transfer_flags |= URB_FREE_BUFFER;
- out:
return urb;
free:
usb_free_urb(urb);
return NULL;
}

-/*
- * Why should I request the Status IU before sending the Command IU? Spec
- * says to, but also says the device may receive them in any order. Seems
- * daft to me.
- */
-
-static int uas_submit_urbs(struct scsi_cmnd *cmnd,
- struct uas_dev_info *devinfo, gfp_t gfp)
+static int uas_alloc_urbs(struct scsi_cmnd *cmd, gfp_t gfp)
{
- struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
- int res;
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ struct uas_tport_info *tpinfo = UAS_TPORT_INFO(cmd);

- if (cmdinfo->state & ALLOC_STATUS_URB) {
- cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
- cmdinfo->stream);
- if (!cmdinfo->status_urb)
- return -ENOMEM;
- cmdinfo->state &= ~ALLOC_STATUS_URB;
- }
+ cmdinfo->status_urb = NULL;
+ cmdinfo->data_in_urb = NULL;
+ cmdinfo->data_out_urb = NULL;
+ cmdinfo->cmd_urb = NULL;

- if (cmdinfo->state & SUBMIT_STATUS_URB) {
- res = usb_submit_urb(cmdinfo->status_urb, gfp);
- if (res) {
- scmd_printk(KERN_INFO, cmnd,
- "sense urb submission failure (%d)\n",
- res);
- return res;
+ cmdinfo->status_urb = uas_alloc_status_urb(cmd, gfp);
+ cmdinfo->cmd_urb = uas_alloc_cmd_urb(cmd, gfp);
+ if (!cmdinfo->cmd_urb || !cmdinfo->status_urb)
+ goto Out_err1;
+
+ switch (cmd->sc_data_direction) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_TO_DEVICE:
+ cmdinfo->data_out_urb =
+ uas_alloc_data_urb(tpinfo, gfp,
+ tpinfo->data_out_pipe,
+ cmdinfo->tag,
+ scsi_out(cmd));
+ if (!cmdinfo->data_out_urb)
+ goto Out_err2;
+ if (cmd->sc_data_direction != DMA_BIDIRECTIONAL)
+ break;
+ else {
+ case DMA_FROM_DEVICE:
+ cmdinfo->data_in_urb =
+ uas_alloc_data_urb(tpinfo, gfp,
+ tpinfo->data_in_pipe,
+ cmdinfo->tag,
+ scsi_in(cmd));
+ if (!cmdinfo->data_in_urb)
+ goto Out_err2;
}
- cmdinfo->state &= ~SUBMIT_STATUS_URB;
+ break;
+ case DMA_NONE:
+ break;
}

- if (cmdinfo->state & ALLOC_DATA_IN_URB) {
- cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
- devinfo->data_in_pipe, cmdinfo->stream,
- scsi_in(cmnd), DMA_FROM_DEVICE);
- if (!cmdinfo->data_in_urb)
- return -ENOMEM;
- cmdinfo->state &= ~ALLOC_DATA_IN_URB;
+ return 0;
+
+ Out_err2:
+ usb_free_urb(cmdinfo->data_in_urb);
+ usb_free_urb(cmdinfo->data_out_urb);
+ Out_err1:
+ usb_free_urb(cmdinfo->cmd_urb);
+ return -ENOMEM;
+}
+
+static int uas_submit_urbs(struct scsi_cmnd *cmd, gfp_t gfp)
+{
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ struct uas_tport_info *tpinfo = UAS_TPORT_INFO(cmd);
+ int res;
+
+ UAS_DPRINTK("%s: cmd:%p (0x%02x) tag:%d\n", __func__,
+ cmd, cmd->cmnd[0], cmdinfo->tag);
+
+ res = usb_submit_urb(cmdinfo->status_urb, gfp);
+ if (res) {
+ scmd_printk(KERN_INFO, cmd,
+ "sense urb submission failure (%d)\n", res);
+ return res;
}

- if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
+ if (cmdinfo->data_in_urb && tpinfo->use_streams) {
res = usb_submit_urb(cmdinfo->data_in_urb, gfp);
if (res) {
- scmd_printk(KERN_INFO, cmnd,
+ scmd_printk(KERN_INFO, cmd,
"data in urb submission failure (%d)\n",
res);
return res;
}
- cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
}

- if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
- cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
- devinfo->data_out_pipe, cmdinfo->stream,
- scsi_out(cmnd), DMA_TO_DEVICE);
- if (!cmdinfo->data_out_urb)
- return -ENOMEM;
- cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
- }
-
- if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
+ if (cmdinfo->data_out_urb && tpinfo->use_streams) {
res = usb_submit_urb(cmdinfo->data_out_urb, gfp);
if (res) {
- scmd_printk(KERN_INFO, cmnd,
+ scmd_printk(KERN_INFO, cmd,
"data out urb submission failure (%d)\n",
res);
return res;
}
- cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
- }
-
- if (cmdinfo->state & ALLOC_CMD_URB) {
- cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
- cmdinfo->stream);
- if (!cmdinfo->cmd_urb)
- return -ENOMEM;
- cmdinfo->state &= ~ALLOC_CMD_URB;
}

- if (cmdinfo->state & SUBMIT_CMD_URB) {
- res = usb_submit_urb(cmdinfo->cmd_urb, gfp);
- if (res) {
- scmd_printk(KERN_INFO, cmnd,
- "cmd urb submission failure (%d)\n", res);
- return res;
- }
- cmdinfo->state &= ~SUBMIT_CMD_URB;
+ res = usb_submit_urb(cmdinfo->cmd_urb, gfp);
+ if (res) {
+ scmd_printk(KERN_INFO, cmd,
+ "cmd urb submission failure (%d)\n", res);
+ return res;
}

return 0;
}

-static int uas_queuecommand(struct scsi_cmnd *cmnd,
+static void uas_free_urbs(struct scsi_cmnd *cmd)
+{
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ int res;
+
+ if (cmdinfo->cmd_urb) {
+ res = usb_unlink_urb(cmdinfo->cmd_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(cmdinfo->cmd_urb);
+ }
+ if (cmdinfo->status_urb) {
+ res = usb_unlink_urb(cmdinfo->status_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(cmdinfo->status_urb);
+ }
+ if (cmdinfo->data_in_urb) {
+ res = usb_unlink_urb(cmdinfo->data_in_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(cmdinfo->data_in_urb);
+ }
+ if (cmdinfo->data_out_urb) {
+ res = usb_unlink_urb(cmdinfo->data_out_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(cmdinfo->data_out_urb);
+ }
+}
+
+static int uas_queuecommand(struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *))
{
- struct scsi_device *sdev = cmnd->device;
- struct uas_dev_info *devinfo = sdev->hostdata;
- struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ struct scsi_device *sdev = cmd->device;
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
int res;

BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));

- if (!cmdinfo->status_urb && sdev->current_cmnd)
- return SCSI_MLQUEUE_DEVICE_BUSY;
-
- if (blk_rq_tagged(cmnd->request)) {
- cmdinfo->stream = cmnd->request->tag + 1;
+ /* If LLDD are NOT to maintain their own tags, (but the block
+ * layer would), THEN ANY AND ALL scsi_cmnds passed to the
+ * queuecommand entry of a LLDD MUST HAVE A VALID,
+ * REVERSE-MAPPABLE tag, REGARDLESS of where the command came
+ * from, regardless of whether the device supports tags, and
+ * regardless of how many tags it supports.
+ */
+ if (blk_rq_tagged(cmd->request)) {
+ cmdinfo->tag = cmd->request->tag + 2;
+ } else if (sdev->current_cmnd == NULL) {
+ sdev->current_cmnd = cmd; /* Hilarious, isn't it?! */
+ cmdinfo->tag = 1;
} else {
- sdev->current_cmnd = cmnd;
- cmdinfo->stream = 1;
+ cmd->result = DID_ABORT << 16;
+ goto Out_err;
}

- cmnd->scsi_done = done;
-
- cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB |
- ALLOC_CMD_URB | SUBMIT_CMD_URB;
+ cmd->scsi_done = done;

- switch (cmnd->sc_data_direction) {
- case DMA_FROM_DEVICE:
- cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB;
- break;
- case DMA_BIDIRECTIONAL:
- cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB;
- case DMA_TO_DEVICE:
- cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB;
- case DMA_NONE:
- break;
+ res = uas_alloc_urbs(cmd, GFP_ATOMIC);
+ if (res) {
+ cmd->result = DID_ABORT << 16;
+ goto Out_err;
}

- if (!devinfo->use_streams) {
- cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
- cmdinfo->stream = 0;
+ res = uas_submit_urbs(cmd, GFP_ATOMIC);
+ if (res) {
+ cmd->result = DID_NO_CONNECT << 16;
+ uas_free_urbs(cmd);
+ goto Out_err;
}
+
+ UAS_DPRINTK("%s: cmd:%p (0x%02x) res:%d tag:%d\n",
+ __func__, cmd, cmd->cmnd[0], res, cmdinfo->tag);

- res = uas_submit_urbs(cmnd, devinfo, GFP_ATOMIC);
- UAS_DPRINTK("%s: cmd:%p (0x%02x), err:%d, state:0x%x\n", __FUNCTION__,
- cmnd, cmnd->cmnd[0], res, cmdinfo->state);
- if (res) {
- usb_unlink_urb(cmdinfo->status_urb);
- usb_unlink_urb(cmdinfo->data_in_urb);
- usb_unlink_urb(cmdinfo->data_out_urb);
- usb_unlink_urb(cmdinfo->cmd_urb);
+ return 0;
+ Out_err:
+ sdev->current_cmnd = NULL;
+ done(cmd);
+
+ return 0;
+}

- sdev->current_cmnd = NULL;
+/* ---------- Error Recovery ---------- */

- cmnd->result = DID_NO_CONNECT << 16;
- done(cmnd);
- }
+/* [1, UAS_MAX_STREAMS+1] belong to commands.
+ * [UAS_MAX_STREAMS+2, UAS_MAX_STREAMS+2+TMF_MAX_TAGS) belong to TMFs.
+ */
+#define TMF_TAG_FIRST (UAS_MAX_STREAMS + 2)
+#define TMF_MAX_TAGS 1024
+
+static int uas_get_tmf_tag(void)
+{
+ static int tmf_tag = 0;
+ int res;
+
+ /* [TMF_TAG_FIRST, TMF_MAX_TAGS + TMF_MAX_TAGS) */
+ res = tmf_tag + TMF_TAG_FIRST;
+
+ tmf_tag += 1;
+ tmf_tag %= TMF_MAX_TAGS; /* [0,TMF_MAX_TAGS) */
+
+ return res;
+}
+
+static int uas_alloc_tmf_urb(struct urb **urb, struct uas_tport_info *tpinfo,
+ u8 tmf, u16 ttbm, struct scsi_lun *lun)
+{
+ struct tmf_iu *tmf_iu;
+
+ *urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!*urb)
+ return -ENOMEM;
+ tmf_iu = kzalloc(sizeof(*tmf_iu), GFP_KERNEL);
+ if (!tmf_iu)
+ goto Out_err;
+
+ /* If LLDD are NOT to maintain their own tags, (but the block
+ * layer would), THEN LLDDs must be able to call a function of
+ * some sort and reserve a tag from the same pool and obtain
+ * it for their own use, as well as being able to free it back
+ * later.
+ */
+ tmf_iu->iu_id = IU_ID_TASK_MGMT;
+ tmf_iu->tag = cpu_to_be16(uas_get_tmf_tag());
+ tmf_iu->tmf = tmf;
+ tmf_iu->ttbm = cpu_to_be16(ttbm);
+ tmf_iu->lun = *lun;
+
+ uas_fill_cmdp_urb(*urb, tpinfo, tmf_iu, sizeof(*tmf_iu));

return 0;
+ Out_err:
+ usb_free_urb(*urb);
+ return -ENOMEM;
}

-static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
+static int uas_alloc_resp_urb(struct urb **urb, struct scsi_device *sdev,
+ struct resp_iu *resp, int tag)
{
- struct scsi_device *sdev = cmnd->device;
- sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
- cmnd->request->tag);
+ *urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!*urb)
+ return -ENOMEM;

-/* XXX: Send ABORT TASK Task Management command */
- return FAILED;
+ uas_fill_statp_urb(*urb, sdev, resp, tag);
+
+ return 0;
}

-static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd)
+#define UAS_TMF_TIMEOUT (5*HZ)
+
+/**
+ * uas_do_tmf -- Execute the desired TMF
+ * @sdev: the device to which we should send the TMF
+ * @tmf: the task management function to execute
+ * @ttbm: the tag of task to be managed
+ *
+ * This function returns a negative value on error (-ENOMEM, etc), or
+ * an integer with bytes 3, 2 and 1 being the high to low byte of the
+ * Additional Response Information field and byte 0 being the Response
+ * code of the Response IU.
+ *
+ * If the response code is 0xFF, then the TMF timed out.
+ */
+static int uas_do_tmf(struct scsi_cmnd *cmd, u8 tmf, u8 ttbm)
{
- struct scsi_device *sdev = cmnd->device;
- sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
- cmnd->request->tag);
+ struct scsi_device *sdev = cmd->device;
+ struct uas_tport_info *tpinfo = SDEV_TPORT_INFO(sdev);
+ struct uas_lu_info *luinfo= SDEV_LU_INFO(sdev);
+ struct uas_cmd_info *cmdinfo = UAS_CMD_INFO(cmd);
+ struct urb *tmf_urb = NULL;
+ struct urb *resp_urb = NULL;
+ struct scsi_lun lun;
+ long timeout;
+ int res;

-/* XXX: Send LOGICAL UNIT RESET Task Management command */
- return FAILED;
+ /* scsi_dev should contain u8[8] for a LUN, not an unsigned int!
+ */
+ int_to_scsilun(sdev->lun, &lun);
+ res = uas_alloc_tmf_urb(&tmf_urb, tpinfo, tmf, ttbm, &lun);
+ if (res)
+ return -ENOMEM;
+
+ /* If we're not using streams, we'll get the Response IU via
+ * the sense urb we previosly submitted, so there is no need
+ * to allocate a response urb.
+ * If we're using streams, we need a response urb.
+ * Or we need a response urb if we've previosly executed a TMF
+ * which used up the sense urb as a response urb.
+ */
+ if (tpinfo->use_streams || luinfo->freed_urb == cmdinfo->status_urb) {
+ struct tmf_iu *tmf_iu;
+ struct resp_iu *resp = NULL;
+
+ resp = kzalloc(SENSE_IU_SIZE, GFP_KERNEL);
+ if (!resp) {
+ usb_free_urb(tmf_urb);
+ return -ENOMEM;
+ }
+
+ tmf_iu = tmf_urb->transfer_buffer;
+ res = uas_alloc_resp_urb(&resp_urb, sdev, resp,
+ be16_to_cpu(tmf_iu->tag));
+ if (res) {
+ usb_free_urb(tmf_urb);
+ kfree(resp);
+ return -ENOMEM;
+ }
+ }
+
+ /* Now submit them */
+
+ init_completion(&luinfo->tmf_completion);
+ luinfo->resp.resp = 0xFFFFFFFF;
+
+ if (tpinfo->use_streams || luinfo->freed_urb == cmdinfo->status_urb) {
+ res = usb_submit_urb(resp_urb, GFP_KERNEL);
+ if (res) {
+ complete(&luinfo->tmf_completion);
+ usb_free_urb(tmf_urb);
+ res = usb_unlink_urb(resp_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(resp_urb);
+ return res;
+ }
+ }
+
+ res = usb_submit_urb(tmf_urb, GFP_KERNEL);
+ if (res) {
+ complete(&luinfo->tmf_completion);
+ res = usb_unlink_urb(tmf_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(tmf_urb);
+ if (tpinfo->use_streams ||
+ luinfo->freed_urb == cmdinfo->status_urb) {
+ res = usb_unlink_urb(resp_urb);
+ if (res != -EINPROGRESS)
+ usb_free_urb(resp_urb);
+ return res;
+ }
+ }
+
+ timeout = wait_for_completion_timeout(&luinfo->tmf_completion,
+ UAS_TMF_TIMEOUT);
+ if (timeout && luinfo->resp.iu_id == IU_ID_RESPONSE &&
+ be16_to_cpup(&luinfo->resp.tag) >= TMF_TAG_FIRST) {
+ res = be32_to_cpup(&luinfo->resp.resp);
+ } else {
+ res = 0xFF;
+ }
+ return res;
}

-static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd)
+static int uas_er_tmf(struct scsi_cmnd *cmd, u8 tmf)
{
- struct scsi_device *sdev = cmnd->device;
- sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
- cmnd->request->tag);
+ struct scsi_device *sdev = cmd->device;
+ int tag;
+ int res;

-/* XXX: Can we reset just the one USB interface?
- * Would calling usb_set_interface() have the right effect?
- */
- return FAILED;
+ if (sdev->current_cmnd == cmd)
+ tag = 1;
+ else
+ tag = cmd->request->tag + 2;
+
+ res = uas_do_tmf(cmd, tmf, tag);
+
+ UAS_DPRINTK("%s: cmd:%p (0x%02x) tag:%d tmf:0x%02x resp:0x%08x\n",
+ __func__, cmd, cmd->cmnd[0], tag, tmf, res);
+
+ switch (TMR_RESPONSE_CODE(res)) {
+ case TMR_COMPLETE:
+ case TMR_SUCC:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
}

-static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
+static int uas_eh_abort_handler(struct scsi_cmnd *cmd)
{
- struct scsi_device *sdev = cmnd->device;
- struct uas_dev_info *devinfo = sdev->hostdata;
- struct usb_device *udev = devinfo->udev;
+ return uas_er_tmf(cmd, TMF_ABORT_TASK);
+}
+
+static int uas_eh_device_reset_handler(struct scsi_cmnd *cmd)
+{
+ return uas_er_tmf(cmd, TMF_LU_RESET);
+}

- sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
- cmnd->request->tag);
+static int uas_eh_target_reset_handler(struct scsi_cmnd *cmd)
+{
+ return uas_er_tmf(cmd, TMF_IT_NEXUS_RESET);
+}
+
+static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmd)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct uas_tport_info *tpinfo = SDEV_TPORT_INFO(sdev);
+ struct usb_device *udev = tpinfo->udev;
+ int res;
+
+ res = usb_reset_device(udev);

- if (usb_reset_device(udev))
+ UAS_DPRINTK("%s: cmd:%p (0x%02x) res:0x%08x\n",
+ __func__, cmd, cmd->cmnd[0], res);
+
+ if (res)
return SUCCESS;

return FAILED;
}

+/* ---------- SCSI Host support ---------- */
+
static int uas_slave_alloc(struct scsi_device *sdev)
{
- sdev->hostdata = (void *)sdev->host->hostdata[0];
+ sdev->hostdata = kzalloc(sizeof(struct uas_lu_info), GFP_KERNEL);
+ if (sdev->hostdata == NULL)
+ return -ENOMEM;
return 0;
}

static int uas_slave_configure(struct scsi_device *sdev)
{
- struct uas_dev_info *devinfo = sdev->hostdata;
+ struct uas_tport_info *tpinfo = SDEV_TPORT_INFO(sdev);
+
scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
- scsi_activate_tcq(sdev, devinfo->qdepth - 1);
+ scsi_activate_tcq(sdev, tpinfo->num_tags);
+
return 0;
}

+static void uas_slave_destroy(struct scsi_device *sdev)
+{
+ kfree(sdev->hostdata);
+}
+
static struct scsi_host_template uas_host_template = {
.module = THIS_MODULE,
.name = "uas",
.queuecommand = uas_queuecommand,
.slave_alloc = uas_slave_alloc,
.slave_configure = uas_slave_configure,
+ .slave_destroy = uas_slave_destroy,
.eh_abort_handler = uas_eh_abort_handler,
.eh_device_reset_handler = uas_eh_device_reset_handler,
.eh_target_reset_handler = uas_eh_target_reset_handler,
.eh_bus_reset_handler = uas_eh_bus_reset_handler,
- .can_queue = 65536, /* Is there a limit on the _host_ ? */
+ .can_queue = 0xFFEF, /* [1, 0xFFF0) */
.this_id = -1,
.sg_tablesize = SG_NONE,
- .cmd_per_lun = 1, /* until we override it */
+ .cmd_per_lun = 1, /* until we override it */
.skip_settle_delay = 1,
.ordered_tag = 1,
};

+/* ---------- USB related ---------- */
+
static struct usb_device_id uas_usb_ids[] = {
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) },
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) },
@@ -610,15 +946,15 @@ static struct usb_device_id uas_usb_ids[] = {
};
MODULE_DEVICE_TABLE(usb, uas_usb_ids);

-static void uas_configure_endpoints(struct uas_dev_info *devinfo)
+static void uas_configure_endpoints(struct uas_tport_info *tpinfo)
{
struct usb_host_endpoint *eps[4] = { };
- struct usb_interface *intf = devinfo->intf;
- struct usb_device *udev = devinfo->udev;
+ struct usb_interface *intf = tpinfo->intf;
+ struct usb_device *udev = tpinfo->udev;
struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint;
unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;

- devinfo->uas_sense_old = 0;
+ tpinfo->uas_sense_old = 0;

for (i = 0; i < n_endpoints; i++) {
unsigned char *extra = endpoint[i].extra;
@@ -641,32 +977,38 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
* this.
*/
if (!eps[0]) {
- devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1);
- devinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
- devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
- devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
-
- eps[1] = usb_pipe_endpoint(udev, devinfo->status_pipe);
- eps[2] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
- eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
+ tpinfo->cmd_pipe = usb_sndbulkpipe(udev, 1);
+ tpinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
+ tpinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
+ tpinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
+
+ eps[1] = usb_pipe_endpoint(udev, tpinfo->status_pipe);
+ eps[2] = usb_pipe_endpoint(udev, tpinfo->data_in_pipe);
+ eps[3] = usb_pipe_endpoint(udev, tpinfo->data_out_pipe);
} else {
- devinfo->cmd_pipe = usb_sndbulkpipe(udev,
+ tpinfo->cmd_pipe = usb_sndbulkpipe(udev,
eps[0]->desc.bEndpointAddress);
- devinfo->status_pipe = usb_rcvbulkpipe(udev,
+ tpinfo->status_pipe = usb_rcvbulkpipe(udev,
eps[1]->desc.bEndpointAddress);
- devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
+ tpinfo->data_in_pipe = usb_rcvbulkpipe(udev,
eps[2]->desc.bEndpointAddress);
- devinfo->data_out_pipe = usb_sndbulkpipe(udev,
+ tpinfo->data_out_pipe = usb_sndbulkpipe(udev,
eps[3]->desc.bEndpointAddress);
}

- devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256,
- GFP_KERNEL);
- if (devinfo->qdepth < 0) {
- devinfo->qdepth = 256;
- devinfo->use_streams = 0;
+ if (udev->speed == USB_SPEED_SUPER) {
+ tpinfo->use_streams = 1;
+ tpinfo->num_tags = usb_alloc_streams(tpinfo->intf,
+ eps + 1, 3,
+ UAS_MAX_STREAMS,
+ GFP_KERNEL);
+
+ if (tpinfo->num_tags <= 0)
+ tpinfo->num_tags = 1;
} else {
- devinfo->use_streams = 1;
+ /* Be conservative */
+ tpinfo->num_tags = 32;
+ tpinfo->use_streams = 0;
}
}

@@ -680,7 +1022,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
int result;
struct Scsi_Host *shost;
- struct uas_dev_info *devinfo;
+ struct uas_tport_info *tpinfo;
struct usb_device *udev = interface_to_usbdev(intf);

if (id->bInterfaceProtocol == 0x50) {
@@ -691,8 +1033,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
return -ENODEV;
}

- devinfo = kzalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
- if (!devinfo)
+ tpinfo = kzalloc(sizeof(struct uas_tport_info), GFP_KERNEL);
+ if (!tpinfo)
return -ENOMEM;

result = -ENOMEM;
@@ -707,17 +1049,17 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
result = scsi_add_host(shost, &intf->dev);
if (result)
goto free;
- shost->hostdata[0] = (unsigned long)devinfo;
+ shost->hostdata[0] = (unsigned long)tpinfo;

- devinfo->intf = intf;
- devinfo->udev = udev;
- uas_configure_endpoints(devinfo);
+ tpinfo->intf = intf;
+ tpinfo->udev = udev;
+ uas_configure_endpoints(tpinfo);

scsi_scan_host(shost);
usb_set_intfdata(intf, shost);
return result;
free:
- kfree(devinfo);
+ kfree(tpinfo);
if (shost)
scsi_host_put(shost);
return result;
@@ -740,16 +1082,16 @@ static void uas_disconnect(struct usb_interface *intf)
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_endpoint *eps[3];
struct Scsi_Host *shost = usb_get_intfdata(intf);
- struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+ struct uas_tport_info *tpinfo = (void *)shost->hostdata[0];

scsi_remove_host(shost);

- eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe);
- eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
- eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
+ eps[0] = usb_pipe_endpoint(udev, tpinfo->status_pipe);
+ eps[1] = usb_pipe_endpoint(udev, tpinfo->data_in_pipe);
+ eps[2] = usb_pipe_endpoint(udev, tpinfo->data_out_pipe);
usb_free_streams(intf, eps, 3, GFP_KERNEL);

- kfree(devinfo);
+ kfree(tpinfo);
}

/*
--
1.7.0.1


2010-11-06 05:14:50

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

On Fri, Nov 05, 2010 at 09:46:49PM -0700, Luben Tuikov wrote:
> This patch changes 86% of the driver. More is to come, but this is
> a self-contained patch so it can be applied.

No it can not at all, sorry.

Remember, patches do one single thing at a time. This patch is
equivalant to deleting the file and replacing it with a totally
different driver, which is unacceptable.

Please break all of these changes up into individual patches and
resubmit them. That's the only way we can take them. I also suggest
you go re-read Documentation/development_process/ as well.

thanks,

greg k-h

2010-11-06 06:52:05

by Luben Tuikov

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

--- On Fri, 11/5/10, Greg KH <[email protected]> wrote:
> > This patch changes 86% of the driver. More is to come,
> but this is
> > a self-contained patch so it can be applied.
>
> No it can not at all, sorry.

Greg, would you accept a brand new driver, say uastoo.c (just like
8139too.c came about, remember?), or would you say "This is 14% the same
as uas.c, and thus cannot go in"?

Is 14% difference too much or too little? Surely, the sum of
small tiny individual patches, *would* replace 86% of the driver. BTW,
there are more things to fix even on top of that.

(I wonder how much similar 8139too.c was to rtl8139.c... when 8139too.c
got in back about 10 years ago? I bet it was more than 14%. Hey, I'm building precedent here.)

Or would you say "only individual patches even if it changes 100% of the
code".

> Remember, patches do one single thing at a time.

I wholeheartedly agree. One thing at a time. Self-contained, well defined.

I really did want to do that with uas.c but it was really hard. I was
like "where to begin?" There was no one single change to say "here it
is" without changing something else.

> This patch is equivalant to deleting the file and replacing it with a
> totally different driver, which is unacceptable.

I did want to submit them one at a time. I really did. But one change
necessitated another in order to keep things consistent, and it ended
up like this, one single commit in my git tree.

I really do have one single commit as this "patch". I really did want
to submit it as individual one at a time. But there was no well defined "one at a time".

> Please break all of these changes up into individual
> patches and resubmit them.? That's the only way we can take
> them.

That, or a new driver perhaps?
The net effect of individual patches would be the same, leaving only
14% of the code the same and maybe less with the more work pending.
Perhaps a new driver acknowledging it is based on this one FWIW?

> I also suggest you go re-read Documentation/development_process/ as well.

Did Willy reply to your query about the previous patches? I didn't see
his reply.

2010-11-06 10:38:15

by Alan

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

> I did want to submit them one at a time. I really did. But one change
> necessitated another in order to keep things consistent, and it ended
> up like this, one single commit in my git tree.

You can still dice it up into small bits.

Having done this before a few times when I've ended up in the same
situation (either by my own lack of planning or by being handed a blob by
and told my job is to sort it out) a few tips.

First start with the trivial bits - for example you can easily pull out
patches like

1. Add packed to dats structures that match the wire format
2. Correct the layout of sense iu
3. Add common initialisers
4. Clean up URB freeing

And while you are at it you can also lose changes by removing gratiutious
stuff like

-struct uas_dev_info {
+/* Lives in the SCSI host, hostdata points to it.
+ */
+struct uas_tport_info {


Now even if you hate the name or the qdepth field name you can deal with
it later once it is all nicely working. That would clean up the patch
noise a lot in itself

As you do that the rest becomes clearer and you can then recommit stuff
step by step. The patches need to tell a story which is "How we got from
A to B in small clearly logical steps without breaking anything on the
way"

The tagging mix should possibly also be discussed with the scsi list,
that may be the better place to fix it.

Alan

2010-11-06 16:35:49

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

On Fri, Nov 05, 2010 at 11:52:00PM -0700, Luben Tuikov wrote:
> --- On Fri, 11/5/10, Greg KH <[email protected]> wrote:
> > > This patch changes 86% of the driver. More is to come,
> > but this is
> > > a self-contained patch so it can be applied.
> >
> > No it can not at all, sorry.
>
> Greg, would you accept a brand new driver, say uastoo.c (just like
> 8139too.c came about, remember?), or would you say "This is 14% the same
> as uas.c, and thus cannot go in"?

I will say that as you and Mathew need to work together here.

> Did Willy reply to your query about the previous patches? I didn't see
> his reply.

If he did, I did not see his reply either.

I now have a UAS device so I will test your other patches out next week,
and if Matthew is still absent, will have to determine to apply them or
not.

thanks,

greg k-h

2010-11-08 17:36:56

by Luben Tuikov

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

--- On Sat, 11/6/10, Greg KH <[email protected]> wrote:
> I now have a UAS device so I will test your other patches
> out next week,
> and if Matthew is still absent, will have to determine to
> apply them or
> not.

Do you refer to /all/ other patches I've posted or only to the "[USB]" ones?

The reason I'm asking this is because I've not seen the
"[SCSI] Retrieve the Caching mode page" make it in yet, and I was
wondering if I should re-post it or if you'll take it?

2010-11-08 17:58:56

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

On Mon, Nov 08, 2010 at 09:36:53AM -0800, Luben Tuikov wrote:
> --- On Sat, 11/6/10, Greg KH <[email protected]> wrote:
> > I now have a UAS device so I will test your other patches
> > out next week,
> > and if Matthew is still absent, will have to determine to
> > apply them or
> > not.
>
> Do you refer to /all/ other patches I've posted or only to the "[USB]" ones?

USB ones.

> The reason I'm asking this is because I've not seen the
> "[SCSI] Retrieve the Caching mode page" make it in yet, and I was
> wondering if I should re-post it or if you'll take it?

That is for the scsi maintainer to decide, not me.

thanks,

greg k-h

2010-11-20 01:55:35

by Sarah Sharp

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

On Fri, Nov 05, 2010 at 09:46:49PM -0700, Luben Tuikov wrote:
> * Fix sense IU layout
>
> * Command delivery to the service delivery subsystem should
> be an atomic transaction. Fix this in this driver by
> removing the work thread which retried allocating and
> sending urbs. First allocate the urbs we'll need up
> front; second recycle them (sense<-RRIU in High-Speed
> UAS); third if any operation with the SDS fails, report
> this to the application client.
>
> * Rename uas_dev_info->uas_tport_info to more correctly
> portray the capabilities of the target port. Decouple
> that from the sdev structure as it now has a uas_lu_info
> (new) which assists in TMF processing and error
> recovery. So now uas_cmd_info lives in scsi_cmd;
> uas_lu_info lives in scsi_device and uas_tport_info lives
> in scsi_host.
>
> * Redo uas_sense. Make it more general and print a more
> informative message if the sense data were short.
>
> * uas_xfer_data for High-Speed doesn't retry but fails if
> the SDS is down. This means that we cannot reach the
> target port, so we let error recovery kick in.
>
> * Add common urb initializers/fillers for the command and
> status pipe. This generalizes output transactions on the
> command pipe to always free the urb and the buffer, and
> input transactions on the status pipe to always have a
> buffer holding the max size of struct sense_iu and
> complete in the same place, uas_stat_cmplt, in order to
> accommodate both High-Speed devices and SuperSpeed
> (USB3.0) devices.
>
> * Gracefully free the urbs if the SDS is down or we have no
> memory for all urbs, at queuecommand entry.
>
> * Fix a shortcoming of SCSI Core where some commands coming
> into the driver are NOT tagged and some are tagged. The
> SDS (UAS) needs everything to be tagged and tagged
> correctly. Use tag 1 for untagged commands and tags 2 to
> FFEFh for tagged commands. In practice we use much less
> and target ports will report much less than that.
>
> * If the target port is SuperSpeed request a macro defined
> number of streams (128) for this I_T nexus. Target ports
> will report less. If usb_alloc_streams complains, we know
> we can handle at least one stream. Else if the port is
> High-Speed, set the number of tags we use to some sane
> value (32). Else the desired number of tags is set.
>
> * Implement TMF. All TMFs are supported. As we're not in
> control of the tags, nor can we, at the time of this
> commit, request a free tag from the block layer, we
> allocate tags for TMFs with range [130, 1153]
> (130+1024-1), as an increasing sequence.
>
> Signed-off-by: Luben Tuikov <[email protected]>
> ---
>
> This patch changes 86% of the driver. More is to come, but this is
> a self-contained patch so it can be applied.
>
> drivers/usb/storage/uas.c | 970 ++++++++++++++++++++++++++++++---------------
> 1 files changed, 656 insertions(+), 314 deletions(-)
>
> diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
> index ef6e707..ed1a82f 100644
> --- a/drivers/usb/storage/uas.c
> +++ b/drivers/usb/storage/uas.c
> @@ -4,6 +4,7 @@
> *
> * Copyright Matthew Wilcox for Intel Corp, 2010
> * Copyright Sarah Sharp for Intel Corp, 2010
> + * Copyright Luben Tuikov, 2010
> *
> * Distributed under the terms of the GNU GPL, version two.
> */
> @@ -13,6 +14,7 @@
> #include <linux/types.h>
> #include <linux/usb.h>
> #include <linux/usb/storage.h>
> +#include <linux/completion.h>
>
> #include <scsi/scsi.h>
> #include <scsi/scsi_dbg.h>
> @@ -29,12 +31,18 @@
> #define UAS_DPRINTK(fmt, ...)
> #endif
>
> +/* Define the number of streams to ask to allocate, essentially
> + * the maximum number of tags which we are asking to be able to send
> + * to the device. Devices may report much less.
> + */
> +#define UAS_MAX_STREAMS 128
> +

Why do you limit the number of streams this way? USB 3.0 devices can
support up to 65533 streams, so why place an arbitrary limit like this?
Just use the MaxStreams field in the SuperSpeed Endpoint Companion
descriptor in the call to usb_alloc_streams(). I don't see how limiting
the number of streams you allocate to 128 when the original driver tried
to allocate 256 is an improvement.

<snip>

> -struct uas_dev_info {
> +/* Lives in the SCSI host, hostdata points to it.
> + */
> +struct uas_tport_info {
> struct usb_interface *intf;
> struct usb_device *udev;
> - int qdepth;
> + int num_tags;
> unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
> unsigned use_streams:1;
> unsigned uas_sense_old:1;
> };

Couldn't the renaming code could have made it into a separate
patch? It also makes it hard to wade through the changes. You should
have two patches at least: one to rename uas_dev_info, and one to rename
qdepth. There's places in the code where you've changed how you use
qdepth, and it's hard to see because the rename and change are in one
patch.

Matthew and you can dicker over the name for uas_dev_info, but I really
don't understand your reasoning for renaming it to uas_tport_info. What
type of "ports" are you talking about? USB ports or some other type of
ports? To me, uas_dev_info made sense because it stored information
about the particular UAS device, not the USB port it was attached to...

>
> -enum {
> - ALLOC_STATUS_URB = (1 << 0),
> - SUBMIT_STATUS_URB = (1 << 1),
> - ALLOC_DATA_IN_URB = (1 << 2),
> - SUBMIT_DATA_IN_URB = (1 << 3),
> - ALLOC_DATA_OUT_URB = (1 << 4),
> - SUBMIT_DATA_OUT_URB = (1 << 5),
> - ALLOC_CMD_URB = (1 << 6),
> - SUBMIT_CMD_URB = (1 << 7),
> +/* Lives in the scsi device, hostdata points to it.
> + */
> +struct uas_lu_info {
> + struct completion tmf_completion;
> + struct resp_iu resp;
> + struct urb *freed_urb;
> };
>
> -/* Overrides scsi_pointer */
> +/* Lives in the scsi command, overrides SCp.
> + */
> struct uas_cmd_info {
> - unsigned int state;
> - unsigned int stream;
> + int tag;

Tag ID or stream ID is the same, I suppose. But stream IDs are always
positive integers, so why should tag be signed?

> struct urb *cmd_urb;
> struct urb *status_urb;
> struct urb *data_in_urb;
> struct urb *data_out_urb;
> - struct list_head list;
> };

<snip>

> - sdev->current_cmnd = NULL;
> +/* ---------- Error Recovery ---------- */
>
> - cmnd->result = DID_NO_CONNECT << 16;
> - done(cmnd);
> - }
> +/* [1, UAS_MAX_STREAMS+1] belong to commands.
> + * [UAS_MAX_STREAMS+2, UAS_MAX_STREAMS+2+TMF_MAX_TAGS) belong to TMFs.

I'm a bit confused by this comment, perhaps because you didn't explain
what TMFs are in your comment or your commit message.

However, if you're telling the xHCI driver to allocate UAS_MAX_STREAMS,
the UAS driver gets to put stream ID 1 to stream ID UAS_MAX_STREAMS in
urb->stream_id. If you submit an URB with urb->stream_id greater than
UAS_MAX_STREAMS, the xHCI driver is going to reject that submission.

It looks like from the comment you expect to be able to use a stream ID
as big as UAS_MAX_STREAMS+2+TMF_MAX_TAGS, is that what you were trying
to do?

Sarah Sharp

2010-12-10 10:52:31

by Luben Tuikov

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

Patch retracted for this still-born driver. Instead please use superior uasp.c which was recently submitted.


--- On Fri, 11/5/10, Luben Tuikov <[email protected]> wrote:

> From: Luben Tuikov <[email protected]>
> Subject: [PATCH] [USB] UAS: Achitecture; TMF; more
> To: "Greg KH" <[email protected]>, [email protected], [email protected], "Matthew Wilcox" <[email protected]>
> Date: Friday, November 5, 2010, 9:46 PM
> * Fix sense IU layout
>
> * Command delivery to the service delivery subsystem
> should
> ???be an atomic transaction. Fix this in
> this driver by
> ???removing the work thread which retried
> allocating and
> ???sending urbs. First allocate the urbs
> we'll need up
> ???front; second recycle them
> (sense<-RRIU in High-Speed
> ???UAS); third if any operation with the SDS
> fails, report
> ???this to the application client.
>
> * Rename uas_dev_info->uas_tport_info to more
> correctly
> ???portray the capabilities of the target
> port. Decouple
> ???that from the sdev structure as it now
> has a uas_lu_info
> ???(new) which assists in TMF processing and
> error
> ???recovery. So now uas_cmd_info lives in
> scsi_cmd;
> ???uas_lu_info lives in scsi_device and
> uas_tport_info lives
> ???in scsi_host.
>
> * Redo uas_sense. Make it more general and print a more
> ???informative message if the sense data
> were short.
>
> * uas_xfer_data for High-Speed doesn't retry but fails if
> ???the SDS is down. This means that we
> cannot reach the
> ???target port, so we let error recovery
> kick in.
>
> * Add common urb initializers/fillers for the command and
> ???status pipe. This generalizes output
> transactions on the
> ???command pipe to always free the urb and
> the buffer, and
> ???input transactions on the status pipe to
> always have a
> ???buffer holding the max size of struct
> sense_iu and
> ???complete in the same place,
> uas_stat_cmplt, in order to
> ???accommodate both High-Speed devices and
> SuperSpeed
> ???(USB3.0) devices.
>
> * Gracefully free the urbs if the SDS is down or we have
> no
> ???memory for all urbs, at queuecommand
> entry.
>
> * Fix a shortcoming of SCSI Core where some commands
> coming
> ???into the driver are NOT tagged and some
> are tagged. The
> ???SDS (UAS) needs everything to be tagged
> and tagged
> ???correctly. Use tag 1 for untagged
> commands and tags 2 to
> ???FFEFh for tagged commands. In practice we
> use much less
> ???and target ports will report much less
> than that.
>
> * If the target port is SuperSpeed request a macro
> defined
> ???number of streams (128) for this I_T
> nexus. Target ports
> ???will report less. If usb_alloc_streams
> complains, we know
> ???we can handle at least one stream. Else
> if the port is
> ???High-Speed, set the number of tags we use
> to some sane
> ???value (32). Else the desired number of
> tags is set.
>
> * Implement TMF. All TMFs are supported. As we're not in
> ???control of the tags, nor can we, at the
> time of this
> ???commit, request a free tag from the block
> layer, we
> ???allocate tags for TMFs with range [130,
> 1153]
> ???(130+1024-1), as an increasing sequence.
>
> Signed-off-by: Luben Tuikov <[email protected]>
> ---
>
> This patch changes 86% of the driver. More is to come, but
> this is
> a self-contained patch so it can be applied.
>
> drivers/usb/storage/uas.c |? 970
> ++++++++++++++++++++++++++++++---------------
> 1 files changed, 656 insertions(+), 314 deletions(-)
>
> diff --git a/drivers/usb/storage/uas.c
> b/drivers/usb/storage/uas.c
> index ef6e707..ed1a82f 100644
> --- a/drivers/usb/storage/uas.c
> +++ b/drivers/usb/storage/uas.c
> @@ -4,6 +4,7 @@
> ? *
> ? * Copyright Matthew Wilcox for Intel Corp, 2010
> ? * Copyright Sarah Sharp for Intel Corp, 2010
> + * Copyright Luben Tuikov, 2010
> ? *
> ? * Distributed under the terms of the GNU GPL,
> version two.
> ? */
> @@ -13,6 +14,7 @@
> #include <linux/types.h>
> #include <linux/usb.h>
> #include <linux/usb/storage.h>
> +#include <linux/completion.h>
>
> #include <scsi/scsi.h>
> #include <scsi/scsi_dbg.h>
> @@ -29,12 +31,18 @@
> #define UAS_DPRINTK(fmt, ...)
> #endif
>
> +/* Define the number of streams to ask to allocate,
> essentially
> + * the maximum number of tags which we are asking to be
> able to send
> + * to the device.? Devices may report much less.
> + */
> +#define UAS_MAX_STREAMS??? 128
> +
> /* Common header for all IUs */
> struct iu {
> ??? __u8 iu_id;
> ??? __u8 rsvd1;
> ??? __be16 tag;
> -};
> +} __packed;
>
> enum {
> ??? IU_ID_COMMAND???
> ??? = 0x01,
> @@ -55,7 +63,7 @@ struct command_iu {
> ??? __u8 rsvd7;
> ??? struct scsi_lun lun;
> ??? __u8 cdb[16];??? /* XXX:
> Overflow-checking tools may misunderstand */
> -};
> +} __packed;
>
> struct sense_iu {
> ??? __u8 iu_id;
> @@ -63,11 +71,12 @@ struct sense_iu {
> ??? __be16 tag;
> ??? __be16 status_qual;
> ??? __u8 status;
> -??? __u8 service_response;
> -??? __u8 rsvd8[6];
> +??? __u8 rsvd8[7];
> ??? __be16 len;
> ??? __u8 sense[SCSI_SENSE_BUFFERSIZE];
> -};
> +} __packed;
> +
> +#define SENSE_IU_SIZE??? sizeof(struct
> sense_iu)
>
> /*
> ? * The r00-r01c specs define this version of the
> SENSE IU data structure.
> @@ -81,6 +90,56 @@ struct sense_iu_old {
> ??? __u8 status;
> ??? __u8 service_response;
> ??? __u8 sense[SCSI_SENSE_BUFFERSIZE];
> +} __packed;
> +
> +struct tmf_iu {
> +??? __u8??? iu_id;
> +??? __u8? ? rsvd1;
> +??? __be16??? tag;
> +??? __u8??? tmf;
> +??? __u8??? rsvd5;
> +??? __u16??? ttbm;
> +??? struct scsi_lun lun;
> +} __packed;
> +
> +enum {
> +??? TMF_ABORT_TASK = 1,
> +??? TMF_ABORT_TASK_SET = 2,
> +??? TMF_CLEAR_TASK_SET = 4,
> +??? TMF_LU_RESET = 8,
> +??? TMF_IT_NEXUS_RESET = 0x10,
> +??? TMF_CLEAR_ACA = 0x40,
> +??? TMF_QUERY_TASK = 0x80,
> +??? TMF_QUERY_TASK_SET = 0x81,
> +??? TMF_QUERY_ASYNC_EVENT = 0x82,
> +};
> +
> +struct resp_iu {
> +??? __u8??? iu_id;
> +??? __u8? ? rsvd1;
> +??? __be16??? tag;
> +??? __be32??? resp;
> +} __packed;
> +
> +#define TMR_RESPONSE_CODE_MASK??? 0xFF
> +#define TMR_RESPONSE_CODE_SHIFT 0
> +#define TMR_RESPONSE_INFO_MASK? 0xFFFFFF00
> +#define TMR_RESPONSE_INFO_SHIFT 8
> +
> +#define TMR_RESPONSE_CODE(__Val)???
> ??? ??? ???
> ??? \
> +??? (((__Val) & TMR_RESPONSE_CODE_MASK)
> >> TMR_RESPONSE_CODE_SHIFT)
> +
> +#define TMR_RESPONSE_INFO(__Val)???
> ??? ??? ???
> ??? \
> +??? (((__Val) & TMR_RESPONSE_INFO_MASK)
> >> TMR_RESPONSE_INFO_SHIFT)
> +
> +enum tmf_resp_code {
> +??? TMR_COMPLETE??? = 0,
> +??? TMR_IIU???
> ??? = 2,
> +??? TMR_UNSUPP??? = 4,
> +??? TMR_FAILED??? = 5,
> +??? TMR_SUCC??? = 8,
> +??? TMR_ILUN??? = 9,
> +??? TMR_OLAP??? = 0xA,
> };
>
> enum {
> @@ -95,89 +154,73 @@ enum {
> ??? UAS_ACA???
> ??? ??? = 4,
> };
>
> -struct uas_dev_info {
> +/* Lives in the SCSI host, hostdata points to it.
> + */
> +struct uas_tport_info {
> ??? struct usb_interface *intf;
> ??? struct usb_device *udev;
> -??? int qdepth;
> +??? int num_tags;
> ??? unsigned cmd_pipe, status_pipe,
> data_in_pipe, data_out_pipe;
> ??? unsigned use_streams:1;
> ??? unsigned uas_sense_old:1;
> };
>
> -enum {
> -??? ALLOC_STATUS_URB??? = (1
> << 0),
> -??? SUBMIT_STATUS_URB??? =
> (1 << 1),
> -??? ALLOC_DATA_IN_URB??? =
> (1 << 2),
> -??? SUBMIT_DATA_IN_URB??? =
> (1 << 3),
> -??? ALLOC_DATA_OUT_URB??? =
> (1 << 4),
> -??? SUBMIT_DATA_OUT_URB??? =
> (1 << 5),
> -??? ALLOC_CMD_URB???
> ??? = (1 << 6),
> -??? SUBMIT_CMD_URB???
> ??? = (1 << 7),
> +/* Lives in the scsi device, hostdata points to it.
> + */
> +struct uas_lu_info {
> +??? struct completion tmf_completion;
> +??? struct resp_iu resp;
> +??? struct urb *freed_urb;
> };
>
> -/* Overrides scsi_pointer */
> +/* Lives in the scsi command, overrides SCp.
> + */
> struct uas_cmd_info {
> -??? unsigned int state;
> -??? unsigned int stream;
> +??? int tag;
> ??? struct urb *cmd_urb;
> ??? struct urb *status_urb;
> ??? struct urb *data_in_urb;
> ??? struct urb *data_out_urb;
> -??? struct list_head list;
> };
>
> -static int uas_submit_urbs(struct scsi_cmnd *cmnd,
> -??? ??? ???
> ??? struct uas_dev_info *devinfo, gfp_t
> gfp);
> +#define UAS_CMD_INFO(__Scmd) ((struct uas_cmd_info
> *)(&(__Scmd)->SCp))
> +#define UAS_TPORT_INFO(__Scmd) \
> +??? ((struct uas_tport_info
> *)((__Scmd)->device->host->hostdata[0]))
> +#define SDEV_TPORT_INFO(__Sdev)? \
> +??? ((struct uas_tport_info
> *)((__Sdev)->host->hostdata[0]))
>
> -static DEFINE_SPINLOCK(uas_work_lock);
> -static LIST_HEAD(uas_work_list);
> +#define SDEV_LU_INFO(__Sdev) ((struct uas_lu_info
> *)((__Sdev)->hostdata))
>
> -static void uas_do_work(struct work_struct *work)
> -{
> -??? struct uas_cmd_info *cmdinfo;
> -??? struct list_head list;
> -??? int res;
> -
> -??? spin_lock_irq(&uas_work_lock);
> -??? list_replace_init(&uas_work_list,
> &list);
> -??? spin_unlock_irq(&uas_work_lock);
> -
> -??? list_for_each_entry(cmdinfo, &list,
> list) {
> -??? ??? struct scsi_pointer
> *scp = (void *)cmdinfo;
> -??? ??? struct scsi_cmnd
> *cmnd = container_of(scp,
> -??? ??? ???
> ??? ??? ???
> ? ? ? struct scsi_cmnd, SCp);
> -??? ??? res =
> uas_submit_urbs(cmnd, cmnd->device->hostdata,
> GFP_KERNEL);
> -??? ??? UAS_DPRINTK("%s:
> cmd:%p, res:%d, state:0x%x\n", __FUNCTION__,
> -??? ??? ???
> ? ? cmnd, res, cmdinfo->state);
> -??? }
> -}
> -
> -static DECLARE_WORK(uas_work, uas_do_work);
> +/* ---------- IU processors ---------- */
>
> -static void uas_sense(struct urb *urb, struct scsi_cmnd
> *cmnd)
> +static void uas_sense(struct urb *urb, struct scsi_cmnd
> *cmd)
> {
> ??? struct sense_iu *sense_iu =
> urb->transfer_buffer;
> -??? struct scsi_device *sdev =
> cmnd->device;
> +??? struct scsi_device *sdev =
> cmd->device;
> +
> +??? cmd->result = sense_iu->status;
>
> ??? if (urb->actual_length > 16) {
> -??? ??? unsigned len =
> be16_to_cpup(&sense_iu->len);
> -??? ??? if (len + 16 !=
> urb->actual_length) {
> -??? ??? ???
> int newlen = min(len + 16, urb->actual_length) - 16;
> -??? ??? ???
> if (newlen < 0)
> -??? ??? ???
> ??? newlen = 0;
> -??? ??? ???
> sdev_printk(KERN_INFO, sdev, "%s: urb length %d "
> -??? ??? ???
> ??? "disagrees with IU sense data length %d,
> "
> -??? ??? ???
> ??? "using %d bytes of sense data\n",
> __func__,
> -??? ??? ???
> ??? ??? urb->actual_length,
> len, newlen);
> -??? ??? ???
> len = newlen;
> +??? ??? unsigned slen =
> be16_to_cpup(&sense_iu->len);
> +
> +??? ??? if
> (urb->actual_length >= 16 + slen) {
> +??? ??? ???
> slen = min(slen, (unsigned) SCSI_SENSE_BUFFERSIZE);
> +??? ??? } else {
> +??? ??? ???
> unsigned dlen = slen;
> +
> +??? ??? ???
> slen = min(urb->actual_length - 16,
> +??? ??? ???
> ??? ???(unsigned)
> SCSI_SENSE_BUFFERSIZE);
> +
> +??? ??? ???
> sdev_printk(KERN_INFO, sdev,
> +??? ??? ???
> ??? ? ? "%s: short SENSE IU by %d
> bytes, "
> +??? ??? ???
> ??? ? ? "using %d/%d bytes of sense
> data\n",
> +??? ??? ???
> ??? ? ? __func__, 16 + slen -
> urb->actual_length,
> +??? ??? ???
> ??? ? ? slen, dlen);
> ??? ??? }
> -??? ???
> memcpy(cmnd->sense_buffer, sense_iu->sense, len);
> +??? ???
> memcpy(cmd->sense_buffer, sense_iu->sense, slen);
> ??? }
> -
> -??? cmnd->result = sense_iu->status;
> -??? if (sdev->current_cmnd)
> -??? ???
> sdev->current_cmnd = NULL;
> -??? cmnd->scsi_done(cmnd);
> +??? sdev->current_cmnd = NULL;
> +??? cmd->scsi_done(cmd);
> ??? usb_free_urb(urb);
> }
>
> @@ -200,73 +243,105 @@ static void uas_sense_old(struct urb
> *urb, struct scsi_cmnd *cmnd)
> ??? ??? }
> ??? ???
> memcpy(cmnd->sense_buffer, sense_iu->sense, len);
> ??? }
> -
> ??? cmnd->result = sense_iu->status;
> -??? if (sdev->current_cmnd)
> -??? ???
> sdev->current_cmnd = NULL;
> +??? sdev->current_cmnd = NULL;
> ??? cmnd->scsi_done(cmnd);
> ??? usb_free_urb(urb);
> }
>
> -static void uas_xfer_data(struct urb *urb, struct
> scsi_cmnd *cmnd,
> -??? ??? ???
> ? unsigned direction)
> +/* Let High-Speed devices fail here instead of
> accommodating EAGAIN
> + * and racing with the error handler, if EAGAIN takes
> longer than the
> + * command completion timeout.? SuperSpeed devices
> never get here.
> + */
> +static void uas_xfer_data(struct urb *urb, struct
> scsi_cmnd *cmd,
> +??? ??? ???
> ? struct uas_tport_info *tpinfo,
> +??? ??? ???
> ? enum dma_data_direction dir, int tag)
> {
> -??? struct uas_cmd_info *cmdinfo = (void
> *)&cmnd->SCp;
> -??? int err;
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? int res = 0;
> +
> +??? res = usb_submit_urb(urb, GFP_ATOMIC);
>
> -??? cmdinfo->state = direction |
> SUBMIT_STATUS_URB;
> -??? err = uas_submit_urbs(cmnd,
> cmnd->device->hostdata, GFP_ATOMIC);
> -??? UAS_DPRINTK("%s: cmd:%p, err:%d,
> state:0x%x\n", __FUNCTION__,
> -??? ??? ? ? cmnd,
> err, cmdinfo->state);
> -??? if (err) {
> -??? ???
> spin_lock(&uas_work_lock);
> -??? ???
> list_add_tail(&cmdinfo->list, &uas_work_list);
> -??? ???
> spin_unlock(&uas_work_lock);
> -??? ???
> schedule_work(&uas_work);
> +??? if (res == 0) {
> +??? ??? if (dir ==
> DMA_FROM_DEVICE)
> +??? ??? ???
> res = usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC);
> +??? ??? else
> +??? ??? ???
> res = usb_submit_urb(cmdinfo->data_out_urb,GFP_ATOMIC);
> ??? }
> +
> +??? UAS_DPRINTK("%s: cmd:%p res:%d
> tag:%d\n", __func__, cmd, res,
> +??? ??? ? ?
> cmdinfo->tag);
> }
>
> +/**
> + * uas_stat_cmplt -- Status pipe urb completion
> + * @urb: the URB that completed
> + *
> + * Anything we expect to come back on the status pipe
> + * comes here.
> + */
> static void uas_stat_cmplt(struct urb *urb)
> {
> ??? struct iu *iu =
> urb->transfer_buffer;
> ??? struct scsi_device *sdev =
> urb->context;
> -??? struct uas_dev_info *devinfo =
> sdev->hostdata;
> +??? struct uas_tport_info *tpinfo =
> SDEV_TPORT_INFO(sdev);
> ??? struct scsi_cmnd *cmnd;
> -??? u16 tag;
> +??? int tag;
> +
> +??? UAS_DPRINTK("%s: IU ID:%d tag:%d\n",
> __func__, iu->iu_id,
> +??? ??? ? ?
> be16_to_cpup(&iu->tag));
>
> ??? if (urb->status) {
> ??? ???
> dev_err(&urb->dev->dev, "%s: URB BAD STATUS
> %d\n",
> -??? ??? ???
> __FUNCTION__, urb->status);
> +??? ??? ???
> __func__, urb->status);
> +??? ??? usb_free_urb(urb);
> +??? ??? return;
> +??? }
> +
> +??? if (iu->iu_id == IU_ID_RESPONSE) {
> +??? ??? struct uas_lu_info
> *luinfo = SDEV_LU_INFO(sdev);
> +
> +??? ???
> memcpy(&luinfo->resp, urb->transfer_buffer,
> +??? ??? ? ?
> ???sizeof(struct resp_iu));
> +??? ???
> complete(&luinfo->tmf_completion);
> +??? ??? luinfo->freed_urb
> = urb;
> ??? ??? usb_free_urb(urb);
> ??? ??? return;
> ??? }
>
> -??? tag = be16_to_cpup(&iu->tag) -
> 1;
> +??? tag = be16_to_cpup(&iu->tag);
> ??? if (sdev->current_cmnd)
> ??? ??? cmnd =
> sdev->current_cmnd;
> ??? else
> -??? ??? cmnd =
> scsi_find_tag(sdev, tag);
> -??? if (!cmnd)
> +??? ??? cmnd =
> scsi_find_tag(sdev, tag-2);
> +
> +??? if (!cmnd) {
> +??? ??? UAS_DPRINTK("%s: No
> command!?\n", __func__);
> +??? ??? usb_free_urb(urb);
> ??? ??? return;
> +??? }
>
> ??? switch (iu->iu_id) {
> ??? case IU_ID_STATUS:
> -??? ??? if
> (urb->actual_length < 16)
> -??? ??? ???
> devinfo->uas_sense_old = 1;
> -??? ??? if
> (devinfo->uas_sense_old)
> +??? ??? if
> (unlikely(urb->actual_length < 16))
> +??? ??? ???
> tpinfo->uas_sense_old = 1;
> +??? ??? if
> (unlikely(tpinfo->uas_sense_old))
> ??? ??? ???
> uas_sense_old(urb, cmnd);
> ??? ??? else
> ??? ??? ???
> uas_sense(urb, cmnd);
> ??? ??? break;
> ??? case IU_ID_READ_READY:
> -??? ??? uas_xfer_data(urb,
> cmnd, SUBMIT_DATA_IN_URB);
> +??? ??? uas_xfer_data(urb,
> cmnd, tpinfo, DMA_FROM_DEVICE, tag);
> ??? ??? break;
> ??? case IU_ID_WRITE_READY:
> -??? ??? uas_xfer_data(urb,
> cmnd, SUBMIT_DATA_OUT_URB);
> +??? ??? uas_xfer_data(urb,
> cmnd, tpinfo, DMA_TO_DEVICE, tag);
> ??? ??? break;
> ??? default:
> ??? ???
> scmd_printk(KERN_ERR, cmnd,
> -??? ??? ???
> "Bogus IU (%d) received on status pipe\n", iu->iu_id);
> +??? ??? ???
> ? ? "Unknown IU ID %d received on the status
> pipe\n",
> +??? ??? ???
> ? ? iu->iu_id);
> +??? ??? usb_free_urb(urb);
> +??? ??? break;
> ??? }
> }
>
> @@ -274,333 +349,594 @@ static void uas_data_cmplt(struct
> urb *urb)
> {
> ??? struct scsi_data_buffer *sdb =
> urb->context;
>
> -??? if (urb->status) {
> +??? if (!urb->status)
> +??? ??? sdb->resid =
> sdb->length - urb->actual_length;
> +??? else
> ??? ???
> dev_err(&urb->dev->dev, "%s: URB BAD STATUS
> %d\n",
> -??? ??? ???
> __FUNCTION__, urb->status);
> -??? ??? usb_free_urb(urb);
> -??? ??? return;
> -??? }
> -???
> -??? sdb->resid = sdb->length -
> urb->actual_length;
> +??? ??? ???
> __func__, urb->status);
> +
> ??? usb_free_urb(urb);
> }
>
> -static struct urb *uas_alloc_data_urb(struct uas_dev_info
> *devinfo, gfp_t gfp,
> -??? ??? ???
> ??? unsigned int pipe, u16 stream_id,
> -??? ??? ???
> ??? struct scsi_data_buffer *sdb,
> -??? ??? ???
> ??? enum dma_data_direction dir)
> +/* ---------- URB allocators and submission ---------- */
> +
> +/**
> + * uas_fill_cmdp_urb -- Fill in a command pipe urb
> + * @urb: the urb
> + * @tpinfo: the UAS device info struct
> + * @transfer_buffer: the IU
> + * @buffer_length: the size of the IU
> + *
> + * A unified place to initialize a command pipe urb so
> that
> + * all command pipe urbs are freed in the same manner.
> + */
> +static void uas_fill_cmdp_urb(struct urb *urb, struct
> uas_tport_info *tpinfo,
> +??? ??? ???
> ? ? ? void *transfer_buffer, int
> buffer_length)
> +{
> +??? usb_fill_bulk_urb(urb, tpinfo->udev,
> tpinfo->cmd_pipe,
> +??? ??? ???
> ? transfer_buffer, buffer_length,
> +??? ??? ???
> ? usb_free_urb, NULL);
> +??? urb->transfer_flags |=
> URB_FREE_BUFFER;
> +}
> +
> +/**
> + * uas_fill_statp_urb -- Fill in a status pipe urb
> + * @urb: the urb
> + * @dev: the scsi device
> + * @transfer_buffer: the transfer buffer of size
> SENSE_IU_SIZE
> + * @tag: the tag
> + *
> + * The callback is responsible to free/recycle the urb and
> the
> + * transfer buffer.
> + */
> +static void uas_fill_statp_urb(struct urb *urb, struct
> scsi_device *sdev,
> +??? ??? ???
> ? ? ???void *transfer_buffer, int
> tag)
> +{
> +??? struct uas_tport_info *tpinfo =
> SDEV_TPORT_INFO(sdev);
> +
> +??? usb_fill_bulk_urb(urb, tpinfo->udev,
> tpinfo->status_pipe,
> +??? ??? ???
> ? transfer_buffer, SENSE_IU_SIZE,
> +??? ??? ???
> ? uas_stat_cmplt, sdev);
> +??? urb->transfer_flags |=
> URB_FREE_BUFFER;
> +??? if (tpinfo->use_streams)
> +??? ??? urb->stream_id =
> tag;
> +}
> +
> +static struct urb *uas_alloc_data_urb(struct
> uas_tport_info *tpinfo, gfp_t gfp,
> +??? ??? ???
> ??? ? ? ? unsigned int
> data_pipe, u16 tag,
> +??? ??? ???
> ??? ? ? ? struct
> scsi_data_buffer *sdb)
> {
> -??? struct usb_device *udev =
> devinfo->udev;
> +??? struct usb_device *udev =
> tpinfo->udev;
> ??? struct urb *urb = usb_alloc_urb(0,
> gfp);
>
> ??? if (!urb)
> -??? ??? goto out;
> -??? usb_fill_bulk_urb(urb, udev, pipe,
> NULL, sdb->length, uas_data_cmplt,
> -??? ??? ???
> ??? ??? ???
> ??? ??? ???
> sdb);
> -??? if (devinfo->use_streams)
> -??? ??? urb->stream_id =
> stream_id;
> +??? ??? goto Out;
> +
> +??? usb_fill_bulk_urb(urb, udev, data_pipe,
> NULL, sdb->length,
> +??? ??? ???
> ? uas_data_cmplt, sdb);
> +??? if (tpinfo->use_streams)
> +??? ??? urb->stream_id =
> tag;
> ??? urb->num_sgs =
> udev->bus->sg_tablesize ? sdb->table.nents : 0;
> ??? urb->sg = sdb->table.sgl;
> - out:
> + Out:
> ??? return urb;
> }
>
> -static struct urb *uas_alloc_sense_urb(struct uas_dev_info
> *devinfo, gfp_t gfp,
> -??? ??? ???
> ??? ??? struct scsi_cmnd
> *cmnd, u16 stream_id)
> +static struct urb *uas_alloc_status_urb(struct scsi_cmnd
> *cmd, gfp_t gfp)
> +??? ??? ???
> ??? ???
> {
> -??? struct usb_device *udev =
> devinfo->udev;
> -??? struct urb *urb = usb_alloc_urb(0,
> gfp);
> -??? struct sense_iu *iu;
> +??? struct sense_iu *siu;
> +??? struct urb *urb;
>
> +??? urb = usb_alloc_urb(0, gfp);
> ??? if (!urb)
> -??? ??? goto out;
> +??? ??? return NULL;
>
> -??? iu = kzalloc(sizeof(*iu), gfp);
> -??? if (!iu)
> -??? ??? goto free;
> +??? siu = kzalloc(SENSE_IU_SIZE, gfp);
> +??? if (!siu)
> +??? ??? goto Out_free;
> +
> +??? uas_fill_statp_urb(urb, cmd->device,
> siu, UAS_CMD_INFO(cmd)->tag);
>
> -??? usb_fill_bulk_urb(urb, udev,
> devinfo->status_pipe, iu, sizeof(*iu),
> -??? ??? ???
> ??? ??? ???
> uas_stat_cmplt, cmnd->device);
> -??? urb->stream_id = stream_id;
> -??? urb->transfer_flags |=
> URB_FREE_BUFFER;
> - out:
> ??? return urb;
> - free:
> + Out_free:
> ??? usb_free_urb(urb);
> ??? return NULL;
> }
>
> -static struct urb *uas_alloc_cmd_urb(struct uas_dev_info
> *devinfo, gfp_t gfp,
> -??? ??? ???
> ??? ??? struct scsi_cmnd
> *cmnd, u16 stream_id)
> +static struct urb *uas_alloc_cmd_urb(struct scsi_cmnd
> *cmd, gfp_t gfp)
> {
> -??? struct usb_device *udev =
> devinfo->udev;
> -??? struct scsi_device *sdev =
> cmnd->device;
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? struct uas_tport_info *tpinfo =
> UAS_TPORT_INFO(cmd);
> +??? struct usb_device *udev =
> tpinfo->udev;
> +??? struct scsi_device *sdev =
> cmd->device;
> ??? struct urb *urb = usb_alloc_urb(0,
> gfp);
> -??? struct command_iu *iu;
> +??? struct command_iu *ciu;
> ??? int len;
>
> ??? if (!urb)
> -??? ??? goto out;
> +??? ??? return NULL;
>
> -??? len = cmnd->cmd_len - 16;
> +??? len = cmd->cmd_len - 16;
> ??? if (len < 0)
> ??? ??? len = 0;
> -??? len = ALIGN(len, 4);
> -??? iu = kzalloc(sizeof(*iu) + len, gfp);
> -??? if (!iu)
> +??? else
> +??? ??? len = ALIGN(len,
> 4);
> +
> +??? ciu = kzalloc(sizeof(*ciu) + len,
> gfp);
> +??? if (!ciu)
> ??? ??? goto free;
>
> -??? iu->iu_id = IU_ID_COMMAND;
> -??? iu->tag = cpu_to_be16(stream_id);
> -??? if (sdev->ordered_tags &&
> (cmnd->request->cmd_flags & REQ_HARDBARRIER))
> -??? ??? iu->prio_attr =
> UAS_ORDERED_TAG;
> +??? ciu->iu_id = IU_ID_COMMAND;
> +??? ciu->tag =
> cpu_to_be16(cmdinfo->tag);
> +??? if (sdev->ordered_tags &&
> (cmd->request->cmd_flags & REQ_HARDBARRIER))
> +??? ??? ciu->prio_attr =
> UAS_ORDERED_TAG;
> ??? else
> -??? ??? iu->prio_attr =
> UAS_SIMPLE_TAG;
> -??? iu->len = len;
> -??? int_to_scsilun(sdev->lun,
> &iu->lun);
> -??? memcpy(iu->cdb, cmnd->cmnd,
> cmnd->cmd_len);
> +??? ??? ciu->prio_attr =
> UAS_SIMPLE_TAG;
> +??? ciu->len = len;
> +??? int_to_scsilun(sdev->lun,
> &ciu->lun);
> +??? memcpy(ciu->cdb, cmd->cmnd,
> cmd->cmd_len);
>
> -??? usb_fill_bulk_urb(urb, udev,
> devinfo->cmd_pipe, iu, sizeof(*iu) + len,
> +??? usb_fill_bulk_urb(urb, udev,
> tpinfo->cmd_pipe, ciu, sizeof(*ciu) + len,
> ??? ??? ???
> ? usb_free_urb, NULL);
> ??? urb->transfer_flags |=
> URB_FREE_BUFFER;
> - out:
> ??? return urb;
> ? free:
> ??? usb_free_urb(urb);
> ??? return NULL;
> }
>
> -/*
> - * Why should I request the Status IU before sending the
> Command IU?? Spec
> - * says to, but also says the device may receive them in
> any order.? Seems
> - * daft to me.
> - */
> -
> -static int uas_submit_urbs(struct scsi_cmnd *cmnd,
> -??? ??? ???
> ???struct uas_dev_info *devinfo, gfp_t gfp)
> +static int uas_alloc_urbs(struct scsi_cmnd *cmd, gfp_t
> gfp)
> {
> -??? struct uas_cmd_info *cmdinfo = (void
> *)&cmnd->SCp;
> -??? int res;
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? struct uas_tport_info *tpinfo =
> UAS_TPORT_INFO(cmd);
>
> -??? if (cmdinfo->state &
> ALLOC_STATUS_URB) {
> -??? ???
> cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp,
> cmnd,
> -??? ??? ???
> ??? ??? ???
> ??? ? cmdinfo->stream);
> -??? ??? if
> (!cmdinfo->status_urb)
> -??? ??? ???
> return -ENOMEM;
> -??? ??? cmdinfo->state
> &= ~ALLOC_STATUS_URB;
> -??? }
> +??? cmdinfo->status_urb = NULL;
> +??? cmdinfo->data_in_urb = NULL;
> +??? cmdinfo->data_out_urb = NULL;
> +??? cmdinfo->cmd_urb = NULL;
>
> -??? if (cmdinfo->state &
> SUBMIT_STATUS_URB) {
> -??? ??? res =
> usb_submit_urb(cmdinfo->status_urb, gfp);
> -??? ??? if (res) {
> -??? ??? ???
> scmd_printk(KERN_INFO, cmnd,
> -??? ??? ???
> ??? ? ? "sense urb submission
> failure (%d)\n",
> -??? ??? ???
> ??? ? ? res);
> -??? ??? ???
> return res;
> +??? cmdinfo->status_urb =
> uas_alloc_status_urb(cmd, gfp);
> +??? cmdinfo->cmd_urb =
> uas_alloc_cmd_urb(cmd, gfp);
> +??? if (!cmdinfo->cmd_urb ||
> !cmdinfo->status_urb)
> +??? ??? goto Out_err1;
> +
> +??? switch (cmd->sc_data_direction) {
> +??? case DMA_BIDIRECTIONAL:
> +??? case DMA_TO_DEVICE:
> +??? ???
> cmdinfo->data_out_urb =
> +??? ??? ???
> uas_alloc_data_urb(tpinfo, gfp,
> +??? ??? ???
> ??? ???
> ???tpinfo->data_out_pipe,
> +??? ??? ???
> ??? ???
> ???cmdinfo->tag,
> +??? ??? ???
> ??? ???
> ???scsi_out(cmd));
> +??? ??? if
> (!cmdinfo->data_out_urb)
> +??? ??? ???
> goto Out_err2;
> +??? ??? if
> (cmd->sc_data_direction != DMA_BIDIRECTIONAL)
> +??? ??? ???
> break;
> +??? ??? else {
> +??? case DMA_FROM_DEVICE:
> +??? ???
> cmdinfo->data_in_urb =
> +??? ??? ???
> uas_alloc_data_urb(tpinfo, gfp,
> +??? ??? ???
> ??? ???
> ???tpinfo->data_in_pipe,
> +??? ??? ???
> ??? ???
> ???cmdinfo->tag,
> +??? ??? ???
> ??? ???
> ???scsi_in(cmd));
> +??? ??? if
> (!cmdinfo->data_in_urb)
> +??? ??? ???
> goto Out_err2;
> ??? ??? }
> -??? ??? cmdinfo->state
> &= ~SUBMIT_STATUS_URB;
> +??? ??? break;
> +??? case DMA_NONE:
> +??? ??? break;
> ??? }
>
> -??? if (cmdinfo->state &
> ALLOC_DATA_IN_URB) {
> -??? ???
> cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
> -??? ??? ???
> ??? ???
> devinfo->data_in_pipe, cmdinfo->stream,
> -??? ??? ???
> ??? ??? scsi_in(cmnd),
> DMA_FROM_DEVICE);
> -??? ??? if
> (!cmdinfo->data_in_urb)
> -??? ??? ???
> return -ENOMEM;
> -??? ??? cmdinfo->state
> &= ~ALLOC_DATA_IN_URB;
> +??? return 0;
> +???
> + Out_err2:
> +??? usb_free_urb(cmdinfo->data_in_urb);
> +???
> usb_free_urb(cmdinfo->data_out_urb);
> + Out_err1:
> +??? usb_free_urb(cmdinfo->cmd_urb);
> +??? return -ENOMEM;
> +}
> +
> +static int uas_submit_urbs(struct scsi_cmnd *cmd, gfp_t
> gfp)
> +{
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? struct uas_tport_info *tpinfo =
> UAS_TPORT_INFO(cmd);
> +??? int res;
> +
> +??? UAS_DPRINTK("%s: cmd:%p (0x%02x)
> tag:%d\n", __func__,
> +??? ??? ? ? cmd,
> cmd->cmnd[0], cmdinfo->tag);
> +
> +??? res =
> usb_submit_urb(cmdinfo->status_urb, gfp);
> +??? if (res) {
> +??? ???
> scmd_printk(KERN_INFO, cmd,
> +??? ??? ???
> ? ? "sense urb submission failure (%d)\n", res);
> +??? ??? return res;
> ??? }
>
> -??? if (cmdinfo->state &
> SUBMIT_DATA_IN_URB) {
> +??? if (cmdinfo->data_in_urb &&
> tpinfo->use_streams) {
> ??? ??? res =
> usb_submit_urb(cmdinfo->data_in_urb, gfp);
> ??? ??? if (res) {
> -??? ??? ???
> scmd_printk(KERN_INFO, cmnd,
> +??? ??? ???
> scmd_printk(KERN_INFO, cmd,
> ??? ??? ???
> ??? ? ? "data in urb submission
> failure (%d)\n",
> ??? ??? ???
> ??? ? ? res);
> ??? ??? ???
> return res;
> ??? ??? }
> -??? ??? cmdinfo->state
> &= ~SUBMIT_DATA_IN_URB;
> ??? }
>
> -??? if (cmdinfo->state &
> ALLOC_DATA_OUT_URB) {
> -??? ???
> cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
> -??? ??? ???
> ??? ???
> devinfo->data_out_pipe, cmdinfo->stream,
> -??? ??? ???
> ??? ??? scsi_out(cmnd),
> DMA_TO_DEVICE);
> -??? ??? if
> (!cmdinfo->data_out_urb)
> -??? ??? ???
> return -ENOMEM;
> -??? ??? cmdinfo->state
> &= ~ALLOC_DATA_OUT_URB;
> -??? }
> -
> -??? if (cmdinfo->state &
> SUBMIT_DATA_OUT_URB) {
> +??? if (cmdinfo->data_out_urb &&
> tpinfo->use_streams) {
> ??? ??? res =
> usb_submit_urb(cmdinfo->data_out_urb, gfp);
> ??? ??? if (res) {
> -??? ??? ???
> scmd_printk(KERN_INFO, cmnd,
> +??? ??? ???
> scmd_printk(KERN_INFO, cmd,
> ??? ??? ???
> ??? ? ? "data out urb submission
> failure (%d)\n",
> ??? ??? ???
> ??? ? ? res);
> ??? ??? ???
> return res;
> ??? ??? }
> -??? ??? cmdinfo->state
> &= ~SUBMIT_DATA_OUT_URB;
> -??? }
> -
> -??? if (cmdinfo->state &
> ALLOC_CMD_URB) {
> -??? ??? cmdinfo->cmd_urb
> = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
> -??? ??? ???
> ??? ??? ???
> ??? cmdinfo->stream);
> -??? ??? if
> (!cmdinfo->cmd_urb)
> -??? ??? ???
> return -ENOMEM;
> -??? ??? cmdinfo->state
> &= ~ALLOC_CMD_URB;
> ??? }
>
> -??? if (cmdinfo->state &
> SUBMIT_CMD_URB) {
> -??? ??? res =
> usb_submit_urb(cmdinfo->cmd_urb, gfp);
> -??? ??? if (res) {
> -??? ??? ???
> scmd_printk(KERN_INFO, cmnd,
> -??? ??? ???
> ??? ? ? "cmd urb submission failure
> (%d)\n", res);
> -??? ??? ???
> return res;
> -??? ??? }
> -??? ??? cmdinfo->state
> &= ~SUBMIT_CMD_URB;
> +??? res =
> usb_submit_urb(cmdinfo->cmd_urb, gfp);
> +??? if (res) {
> +??? ???
> scmd_printk(KERN_INFO, cmd,
> +??? ??? ???
> ? ? "cmd urb submission failure (%d)\n", res);
> +??? ??? return res;
> ??? }
>
> ??? return 0;
> }
>
> -static int uas_queuecommand(struct scsi_cmnd *cmnd,
> +static void uas_free_urbs(struct scsi_cmnd *cmd)
> +{
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? int res;
> +
> +??? if (cmdinfo->cmd_urb) {
> +??? ??? res =
> usb_unlink_urb(cmdinfo->cmd_urb);
> +??? ??? if (res !=
> -EINPROGRESS)
> +??? ??? ???
> usb_free_urb(cmdinfo->cmd_urb);
> +??? }
> +??? if (cmdinfo->status_urb) {
> +??? ??? res =
> usb_unlink_urb(cmdinfo->status_urb);
> +??? ??? if (res !=
> -EINPROGRESS)
> +??? ??? ???
> usb_free_urb(cmdinfo->status_urb);
> +??? }
> +??? if (cmdinfo->data_in_urb) {
> +??? ??? res =
> usb_unlink_urb(cmdinfo->data_in_urb);
> +??? ??? if (res !=
> -EINPROGRESS)
> +??? ??? ???
> usb_free_urb(cmdinfo->data_in_urb);
> +??? }
> +??? if (cmdinfo->data_out_urb) {
> +??? ??? res =
> usb_unlink_urb(cmdinfo->data_out_urb);
> +??? ??? if (res !=
> -EINPROGRESS)
> +??? ??? ???
> usb_free_urb(cmdinfo->data_out_urb);
> +??? }
> +}
> +
> +static int uas_queuecommand(struct scsi_cmnd *cmd,
> ??? ??? ???
> ? ? void (*done)(struct scsi_cmnd *))
> {
> -??? struct scsi_device *sdev =
> cmnd->device;
> -??? struct uas_dev_info *devinfo =
> sdev->hostdata;
> -??? struct uas_cmd_info *cmdinfo = (void
> *)&cmnd->SCp;
> +??? struct scsi_device *sdev =
> cmd->device;
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> ??? int res;
>
> ??? BUILD_BUG_ON(sizeof(struct
> uas_cmd_info) > sizeof(struct scsi_pointer));
>
> -??? if (!cmdinfo->status_urb &&
> sdev->current_cmnd)
> -??? ??? return
> SCSI_MLQUEUE_DEVICE_BUSY;
> -
> -??? if (blk_rq_tagged(cmnd->request)) {
> -??? ??? cmdinfo->stream =
> cmnd->request->tag + 1;
> +??? /* If LLDD are NOT to maintain their
> own tags, (but the block
> +?????* layer would), THEN ANY AND
> ALL scsi_cmnds passed to the
> +?????* queuecommand entry of a
> LLDD MUST HAVE A VALID,
> +?????* REVERSE-MAPPABLE tag,
> REGARDLESS of where the command came
> +?????* from, regardless of
> whether the device supports tags, and
> +?????* regardless of how many
> tags it supports.
> +?????*/
> +??? if (blk_rq_tagged(cmd->request)) {
> +??? ??? cmdinfo->tag =
> cmd->request->tag + 2;
> +??? } else if (sdev->current_cmnd ==
> NULL) {
> +??? ???
> sdev->current_cmnd = cmd; /* Hilarious, isn't it?! */
> +??? ??? cmdinfo->tag =
> 1;
> ??? } else {
> -??? ???
> sdev->current_cmnd = cmnd;
> -??? ??? cmdinfo->stream =
> 1;
> +??? ??? cmd->result =
> DID_ABORT << 16;
> +??? ??? goto Out_err;
> ??? }
>
> -??? cmnd->scsi_done = done;
> -
> -??? cmdinfo->state = ALLOC_STATUS_URB |
> SUBMIT_STATUS_URB |
> -??? ??? ???
> ALLOC_CMD_URB | SUBMIT_CMD_URB;
> +??? cmd->scsi_done = done;
>
> -??? switch (cmnd->sc_data_direction) {
> -??? case DMA_FROM_DEVICE:
> -??? ??? cmdinfo->state |=
> ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB;
> -??? ??? break;
> -??? case DMA_BIDIRECTIONAL:
> -??? ??? cmdinfo->state |=
> ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB;
> -??? case DMA_TO_DEVICE:
> -??? ??? cmdinfo->state |=
> ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB;
> -??? case DMA_NONE:
> -??? ??? break;
> +??? res = uas_alloc_urbs(cmd, GFP_ATOMIC);
> +??? if (res) {
> +??? ??? cmd->result =
> DID_ABORT << 16;
> +??? ??? goto Out_err;
> ??? }
>
> -??? if (!devinfo->use_streams) {
> -??? ??? cmdinfo->state
> &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
> -??? ??? cmdinfo->stream =
> 0;
> +??? res = uas_submit_urbs(cmd,
> GFP_ATOMIC);
> +??? if (res) {
> +??? ??? cmd->result =
> DID_NO_CONNECT << 16;
> +??? ??? uas_free_urbs(cmd);
> +??? ??? goto Out_err;
> ??? }
> +???
> +??? UAS_DPRINTK("%s: cmd:%p (0x%02x) res:%d
> tag:%d\n",
> +??? ??? ? ?
> __func__, cmd, cmd->cmnd[0], res, cmdinfo->tag);
>
> -??? res = uas_submit_urbs(cmnd, devinfo,
> GFP_ATOMIC);
> -??? UAS_DPRINTK("%s: cmd:%p (0x%02x),
> err:%d, state:0x%x\n", __FUNCTION__,
> -??? ??? ? ? cmnd,
> cmnd->cmnd[0], res, cmdinfo->state);
> -??? if (res) {
> -??? ???
> usb_unlink_urb(cmdinfo->status_urb);
> -??? ???
> usb_unlink_urb(cmdinfo->data_in_urb);
> -??? ???
> usb_unlink_urb(cmdinfo->data_out_urb);
> -??? ???
> usb_unlink_urb(cmdinfo->cmd_urb);
> +??? return 0;
> + Out_err:
> +??? sdev->current_cmnd = NULL;
> +??? done(cmd);
> +
> +??? return 0;
> +}
>
> -??? ???
> sdev->current_cmnd = NULL;
> +/* ---------- Error Recovery ---------- */
>
> -??? ??? cmnd->result =
> DID_NO_CONNECT << 16;
> -??? ??? done(cmnd);
> -??? }
> +/* [1, UAS_MAX_STREAMS+1] belong to commands.
> + * [UAS_MAX_STREAMS+2, UAS_MAX_STREAMS+2+TMF_MAX_TAGS)
> belong to TMFs.
> + */
> +#define TMF_TAG_FIRST ??? (UAS_MAX_STREAMS
> + 2)
> +#define TMF_MAX_TAGS??? 1024
> +
> +static int uas_get_tmf_tag(void)
> +{
> +??? static int tmf_tag = 0;
> +??? int res;
> +
> +??? /* [TMF_TAG_FIRST, TMF_MAX_TAGS +
> TMF_MAX_TAGS) */
> +??? res = tmf_tag + TMF_TAG_FIRST;
> +
> +??? tmf_tag += 1;
> +??? tmf_tag %= TMF_MAX_TAGS; /*
> [0,TMF_MAX_TAGS) */
> +
> +??? return res;
> +}
> +
> +static int uas_alloc_tmf_urb(struct urb **urb, struct
> uas_tport_info *tpinfo,
> +??? ??? ???
> ? ???u8 tmf, u16 ttbm, struct scsi_lun
> *lun)
> +{
> +??? struct tmf_iu *tmf_iu;
> +???
> +??? *urb = usb_alloc_urb(0, GFP_KERNEL);
> +??? if (!*urb)
> +??? ??? return -ENOMEM;
> +??? tmf_iu = kzalloc(sizeof(*tmf_iu),
> GFP_KERNEL);
> +??? if (!tmf_iu)
> +??? ??? goto Out_err;
> +
> +??? /* If LLDD are NOT to maintain their
> own tags, (but the block
> +?????* layer would), THEN LLDDs
> must be able to call a function of
> +?????* some sort and reserve a
> tag from the same pool and obtain
> +?????* it for their own use, as
> well as being able to free it back
> +?????* later.
> +?????*/
> +??? tmf_iu->iu_id = IU_ID_TASK_MGMT;
> +??? tmf_iu->tag =
> cpu_to_be16(uas_get_tmf_tag());
> +??? tmf_iu->tmf = tmf;
> +??? tmf_iu->ttbm = cpu_to_be16(ttbm);
> +??? tmf_iu->lun = *lun;
> +
> +??? uas_fill_cmdp_urb(*urb, tpinfo, tmf_iu,
> sizeof(*tmf_iu));
>
> ??? return 0;
> + Out_err:
> +??? usb_free_urb(*urb);
> +??? return -ENOMEM;
> }
>
> -static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
> +static int uas_alloc_resp_urb(struct urb **urb, struct
> scsi_device *sdev,
> +??? ??? ???
> ? ? ? struct resp_iu *resp, int tag)
> {
> -??? struct scsi_device *sdev =
> cmnd->device;
> -??? sdev_printk(KERN_INFO, sdev, "%s tag
> %d\n", __func__,
> -??? ??? ???
> ??? ??? ???
> ??? cmnd->request->tag);
> +??? *urb = usb_alloc_urb(0, GFP_KERNEL);
> +??? if (!*urb)
> +??? ??? return -ENOMEM;
>
> -/* XXX: Send ABORT TASK Task Management command */
> -??? return FAILED;
> +??? uas_fill_statp_urb(*urb, sdev, resp,
> tag);
> +
> +??? return 0;
> }
>
> -static int uas_eh_device_reset_handler(struct scsi_cmnd
> *cmnd)
> +#define UAS_TMF_TIMEOUT??? (5*HZ)
> +
> +/**
> + * uas_do_tmf -- Execute the desired TMF
> + * @sdev: the device to which we should send the TMF
> + * @tmf:? the task management function to execute
> + * @ttbm: the tag of task to be managed
> + *
> + * This function returns a negative value on error
> (-ENOMEM, etc), or
> + * an integer with bytes 3, 2 and 1 being the high to low
> byte of the
> + * Additional Response Information field and byte 0 being
> the Response
> + * code of the Response IU.
> + *
> + * If the response code is 0xFF, then the TMF timed out.
> + */
> +static int uas_do_tmf(struct scsi_cmnd *cmd, u8 tmf, u8
> ttbm)
> {
> -??? struct scsi_device *sdev =
> cmnd->device;
> -??? sdev_printk(KERN_INFO, sdev, "%s tag
> %d\n", __func__,
> -??? ??? ???
> ??? ??? ???
> ??? cmnd->request->tag);
> +??? struct scsi_device *sdev =
> cmd->device;
> +??? struct uas_tport_info *tpinfo =
> SDEV_TPORT_INFO(sdev);
> +??? struct
> uas_lu_info???*luinfo= SDEV_LU_INFO(sdev);
> +??? struct uas_cmd_info *cmdinfo =
> UAS_CMD_INFO(cmd);
> +??? struct urb *tmf_urb = NULL;
> +??? struct urb *resp_urb = NULL;
> +??? struct scsi_lun lun;
> +??? long???timeout;
> +??? int res;
>
> -/* XXX: Send LOGICAL UNIT RESET Task Management command
> */
> -??? return FAILED;
> +??? /* scsi_dev should contain u8[8] for a
> LUN, not an unsigned int!
> +?????*/
> +??? int_to_scsilun(sdev->lun,
> &lun);
> +??? res = uas_alloc_tmf_urb(&tmf_urb,
> tpinfo, tmf, ttbm, &lun);
> +??? if (res)
> +??? ??? return -ENOMEM;
> +
> +??? /* If we're not using streams, we'll
> get the Response IU via
> +?????* the sense urb we previosly
> submitted, so there is no need
> +?????* to allocate a response
> urb.
> +?????* If we're using streams, we
> need a response urb.
> +?????* Or we need a response urb
> if we've previosly executed a TMF
> +?????* which used up the sense
> urb as a response urb.
> +?????*/
> +??? if (tpinfo->use_streams ||
> luinfo->freed_urb == cmdinfo->status_urb) {
> +??? ??? struct tmf_iu
> *tmf_iu;
> +??? ??? struct resp_iu *resp
> = NULL;
> +
> +??? ??? resp =
> kzalloc(SENSE_IU_SIZE, GFP_KERNEL);
> +??? ??? if (!resp) {
> +??? ??? ???
> usb_free_urb(tmf_urb);
> +??? ??? ???
> return -ENOMEM;
> +??? ??? }
> +
> +??? ??? tmf_iu =
> tmf_urb->transfer_buffer;
> +??? ??? res =
> uas_alloc_resp_urb(&resp_urb, sdev, resp,
> +??? ??? ???
> ???
> ?????be16_to_cpu(tmf_iu->tag));
> +??? ??? if (res) {
> +??? ??? ???
> usb_free_urb(tmf_urb);
> +??? ??? ???
> kfree(resp);
> +??? ??? ???
> return -ENOMEM;
> +??? ??? }
> +??? }
> +
> +??? /* Now submit them */
> +
> +???
> init_completion(&luinfo->tmf_completion);
> +??? luinfo->resp.resp = 0xFFFFFFFF;
> +
> +??? if (tpinfo->use_streams ||
> luinfo->freed_urb == cmdinfo->status_urb) {
> +??? ??? res =
> usb_submit_urb(resp_urb, GFP_KERNEL);
> +??? ??? if (res) {
> +??? ??? ???
> complete(&luinfo->tmf_completion);
> +??? ??? ???
> usb_free_urb(tmf_urb);
> +??? ??? ???
> res = usb_unlink_urb(resp_urb);
> +??? ??? ???
> if (res != -EINPROGRESS)
> +??? ??? ???
> ??? usb_free_urb(resp_urb);
> +??? ??? ???
> return res;
> +??? ??? }
> +??? }
> +
> +??? res = usb_submit_urb(tmf_urb,
> GFP_KERNEL);
> +??? if (res) {
> +??? ???
> complete(&luinfo->tmf_completion);
> +??? ??? res =
> usb_unlink_urb(tmf_urb);
> +??? ??? if (res !=
> -EINPROGRESS)
> +??? ??? ???
> usb_free_urb(tmf_urb);
> +??? ??? if
> (tpinfo->use_streams ||
> +??? ??? ? ?
> luinfo->freed_urb == cmdinfo->status_urb) {
> +??? ??? ???
> res = usb_unlink_urb(resp_urb);
> +??? ??? ???
> if (res != -EINPROGRESS)
> +??? ??? ???
> ??? usb_free_urb(resp_urb);
> +??? ??? ???
> return res;
> +??? ??? }
> +??? }
> +
> +??? timeout =
> wait_for_completion_timeout(&luinfo->tmf_completion,
> +??? ??? ???
> ??? ??? ? ? ?
> UAS_TMF_TIMEOUT);
> +??? if (timeout &&
> luinfo->resp.iu_id == IU_ID_RESPONSE &&
> +??? ? ?
> be16_to_cpup(&luinfo->resp.tag) >= TMF_TAG_FIRST)
> {
> +??? ??? res =
> be32_to_cpup(&luinfo->resp.resp);
> +??? } else {
> +??? ??? res = 0xFF;
> +??? }
> +??? return res;
> }
>
> -static int uas_eh_target_reset_handler(struct scsi_cmnd
> *cmnd)
> +static int uas_er_tmf(struct scsi_cmnd *cmd, u8 tmf)
> {
> -??? struct scsi_device *sdev =
> cmnd->device;
> -??? sdev_printk(KERN_INFO, sdev, "%s tag
> %d\n", __func__,
> -??? ??? ???
> ??? ??? ???
> ??? cmnd->request->tag);
> +??? struct scsi_device *sdev =
> cmd->device;
> +??? int tag;
> +??? int res;
>
> -/* XXX: Can we reset just the one USB interface?
> - * Would calling usb_set_interface() have the right
> effect?
> - */
> -??? return FAILED;
> +??? if (sdev->current_cmnd == cmd)
> +??? ??? tag = 1;
> +??? else
> +??? ??? tag =
> cmd->request->tag + 2;
> +
> +??? res = uas_do_tmf(cmd, tmf, tag);
> +
> +??? UAS_DPRINTK("%s: cmd:%p (0x%02x) tag:%d
> tmf:0x%02x resp:0x%08x\n",
> +??? ??? ? ?
> __func__, cmd, cmd->cmnd[0], tag, tmf, res);
> +
> +??? switch (TMR_RESPONSE_CODE(res)) {
> +??? case TMR_COMPLETE:
> +??? case TMR_SUCC:
> +??? ??? return SUCCESS;
> +??? default:
> +??? ??? return FAILED;
> +??? }
> }
>
> -static int uas_eh_bus_reset_handler(struct scsi_cmnd
> *cmnd)
> +static int uas_eh_abort_handler(struct scsi_cmnd *cmd)
> {
> -??? struct scsi_device *sdev =
> cmnd->device;
> -??? struct uas_dev_info *devinfo =
> sdev->hostdata;
> -??? struct usb_device *udev =
> devinfo->udev;
> +??? return uas_er_tmf(cmd,
> TMF_ABORT_TASK);
> +}
> +
> +static int uas_eh_device_reset_handler(struct scsi_cmnd
> *cmd)
> +{
> +??? return uas_er_tmf(cmd, TMF_LU_RESET);
> +}
>
> -??? sdev_printk(KERN_INFO, sdev, "%s tag
> %d\n", __func__,
> -??? ??? ???
> ??? ??? ???
> ??? cmnd->request->tag);
> +static int uas_eh_target_reset_handler(struct scsi_cmnd
> *cmd)
> +{
> +??? return uas_er_tmf(cmd,
> TMF_IT_NEXUS_RESET);
> +}
> +
> +static int uas_eh_bus_reset_handler(struct scsi_cmnd
> *cmd)
> +{
> +??? struct scsi_device *sdev =
> cmd->device;
> +??? struct uas_tport_info *tpinfo =
> SDEV_TPORT_INFO(sdev);
> +??? struct usb_device *udev =
> tpinfo->udev;
> +??? int res;
> +
> +??? res = usb_reset_device(udev);
>
> -??? if (usb_reset_device(udev))
> +??? UAS_DPRINTK("%s: cmd:%p (0x%02x)
> res:0x%08x\n",
> +??? ??? ? ?
> __func__, cmd, cmd->cmnd[0], res);
> +
> +??? if (res)
> ??? ??? return SUCCESS;
>
> ??? return FAILED;
> }
>
> +/* ---------- SCSI Host support ---------- */
> +
> static int uas_slave_alloc(struct scsi_device *sdev)
> {
> -??? sdev->hostdata = (void
> *)sdev->host->hostdata[0];
> +??? sdev->hostdata =
> kzalloc(sizeof(struct uas_lu_info), GFP_KERNEL);
> +??? if (sdev->hostdata == NULL)
> +??? ??? return -ENOMEM;
> ??? return 0;
> }
>
> static int uas_slave_configure(struct scsi_device *sdev)
> {
> -??? struct uas_dev_info *devinfo =
> sdev->hostdata;
> +??? struct uas_tport_info *tpinfo =
> SDEV_TPORT_INFO(sdev);
> +
> ??? scsi_set_tag_type(sdev,
> MSG_ORDERED_TAG);
> -??? scsi_activate_tcq(sdev,
> devinfo->qdepth - 1);
> +??? scsi_activate_tcq(sdev,
> tpinfo->num_tags);
> +
> ??? return 0;
> }
>
> +static void uas_slave_destroy(struct scsi_device *sdev)
> +{
> +??? kfree(sdev->hostdata);
> +}
> +
> static struct scsi_host_template uas_host_template = {
> ??? .module = THIS_MODULE,
> ??? .name = "uas",
> ??? .queuecommand = uas_queuecommand,
> ??? .slave_alloc = uas_slave_alloc,
> ??? .slave_configure =
> uas_slave_configure,
> +??? .slave_destroy = uas_slave_destroy,
> ??? .eh_abort_handler =
> uas_eh_abort_handler,
> ??? .eh_device_reset_handler =
> uas_eh_device_reset_handler,
> ??? .eh_target_reset_handler =
> uas_eh_target_reset_handler,
> ??? .eh_bus_reset_handler =
> uas_eh_bus_reset_handler,
> -??? .can_queue = 65536,???
> /* Is there a limit on the _host_ ? */
> +??? .can_queue = 0xFFEF,???
> ? /* [1, 0xFFF0) */
> ??? .this_id = -1,
> ??? .sg_tablesize = SG_NONE,
> -??? .cmd_per_lun = 1,??? /*
> until we override it */
> +??? .cmd_per_lun = 1,???
> ? /* until we override it */
> ??? .skip_settle_delay = 1,
> ??? .ordered_tag = 1,
> };
>
> +/* ---------- USB related ---------- */
> +
> static struct usb_device_id uas_usb_ids[] = {
> ??? {
> USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI,
> USB_PR_BULK) },
> ??? {
> USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI,
> USB_PR_UAS) },
> @@ -610,15 +946,15 @@ static struct usb_device_id
> uas_usb_ids[] = {
> };
> MODULE_DEVICE_TABLE(usb, uas_usb_ids);
>
> -static void uas_configure_endpoints(struct uas_dev_info
> *devinfo)
> +static void uas_configure_endpoints(struct uas_tport_info
> *tpinfo)
> {
> ??? struct usb_host_endpoint *eps[4] = {
> };
> -??? struct usb_interface *intf =
> devinfo->intf;
> -??? struct usb_device *udev =
> devinfo->udev;
> +??? struct usb_interface *intf =
> tpinfo->intf;
> +??? struct usb_device *udev =
> tpinfo->udev;
> ??? struct usb_host_endpoint *endpoint =
> intf->cur_altsetting->endpoint;
> ??? unsigned i, n_endpoints =
> intf->cur_altsetting->desc.bNumEndpoints;
>
> -??? devinfo->uas_sense_old = 0;
> +??? tpinfo->uas_sense_old = 0;
>
> ??? for (i = 0; i < n_endpoints; i++) {
> ??? ??? unsigned char *extra
> = endpoint[i].extra;
> @@ -641,32 +977,38 @@ static void
> uas_configure_endpoints(struct uas_dev_info *devinfo)
> ?????* this.
> ?????*/
> ??? if (!eps[0]) {
> -??? ??? devinfo->cmd_pipe
> = usb_sndbulkpipe(udev, 1);
> -??? ???
> devinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
> -??? ???
> devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
> -??? ???
> devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
> -
> -??? ??? eps[1] =
> usb_pipe_endpoint(udev, devinfo->status_pipe);
> -??? ??? eps[2] =
> usb_pipe_endpoint(udev, devinfo->data_in_pipe);
> -??? ??? eps[3] =
> usb_pipe_endpoint(udev, devinfo->data_out_pipe);
> +??? ??? tpinfo->cmd_pipe
> = usb_sndbulkpipe(udev, 1);
> +??? ???
> tpinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
> +??? ???
> tpinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
> +??? ???
> tpinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
> +
> +??? ??? eps[1] =
> usb_pipe_endpoint(udev, tpinfo->status_pipe);
> +??? ??? eps[2] =
> usb_pipe_endpoint(udev, tpinfo->data_in_pipe);
> +??? ??? eps[3] =
> usb_pipe_endpoint(udev, tpinfo->data_out_pipe);
> ??? } else {
> -??? ??? devinfo->cmd_pipe
> = usb_sndbulkpipe(udev,
> +??? ??? tpinfo->cmd_pipe
> = usb_sndbulkpipe(udev,
> ??? ??? ???
> ??? ??? ???
> eps[0]->desc.bEndpointAddress);
> -??? ???
> devinfo->status_pipe = usb_rcvbulkpipe(udev,
> +??? ???
> tpinfo->status_pipe = usb_rcvbulkpipe(udev,
> ??? ??? ???
> ??? ??? ???
> eps[1]->desc.bEndpointAddress);
> -??? ???
> devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
> +??? ???
> tpinfo->data_in_pipe = usb_rcvbulkpipe(udev,
> ??? ??? ???
> ??? ??? ???
> eps[2]->desc.bEndpointAddress);
> -??? ???
> devinfo->data_out_pipe = usb_sndbulkpipe(udev,
> +??? ???
> tpinfo->data_out_pipe = usb_sndbulkpipe(udev,
> ??? ??? ???
> ??? ??? ???
> eps[3]->desc.bEndpointAddress);
> ??? }
>
> -??? devinfo->qdepth =
> usb_alloc_streams(devinfo->intf, eps + 1, 3, 256,
> -??? ??? ???
> ??? ??? ???
> ??? ??? GFP_KERNEL);
> -??? if (devinfo->qdepth < 0) {
> -??? ??? devinfo->qdepth =
> 256;
> -??? ???
> devinfo->use_streams = 0;
> +??? if (udev->speed == USB_SPEED_SUPER)
> {
> +??? ???
> tpinfo->use_streams = 1;
> +??? ??? tpinfo->num_tags
> = usb_alloc_streams(tpinfo->intf,
> +??? ??? ???
> ??? ??? ???
> ? ???eps + 1, 3,
> +??? ??? ???
> ??? ??? ???
> ? ???UAS_MAX_STREAMS,
> +??? ??? ???
> ??? ??? ???
> ? ???GFP_KERNEL);
> +??? ??? ???
> +??? ??? if
> (tpinfo->num_tags <= 0)
> +??? ??? ???
> tpinfo->num_tags = 1;
> ??? } else {
> -??? ???
> devinfo->use_streams = 1;
> +??? ??? /* Be conservative
> */
> +??? ??? tpinfo->num_tags
> = 32;
> +??? ???
> tpinfo->use_streams = 0;
> ??? }
> }
>
> @@ -680,7 +1022,7 @@ static int uas_probe(struct
> usb_interface *intf, const struct usb_device_id *id)
> {
> ??? int result;
> ??? struct Scsi_Host *shost;
> -??? struct uas_dev_info *devinfo;
> +??? struct uas_tport_info *tpinfo;
> ??? struct usb_device *udev =
> interface_to_usbdev(intf);
>
> ??? if (id->bInterfaceProtocol == 0x50)
> {
> @@ -691,8 +1033,8 @@ static int uas_probe(struct
> usb_interface *intf, const struct usb_device_id *id)
> ??? ??? ???
> return -ENODEV;
> ??? }
>
> -??? devinfo = kzalloc(sizeof(struct
> uas_dev_info), GFP_KERNEL);
> -??? if (!devinfo)
> +??? tpinfo = kzalloc(sizeof(struct
> uas_tport_info), GFP_KERNEL);
> +??? if (!tpinfo)
> ??? ??? return -ENOMEM;
>
> ??? result = -ENOMEM;
> @@ -707,17 +1049,17 @@ static int uas_probe(struct
> usb_interface *intf, const struct usb_device_id *id)
> ??? result = scsi_add_host(shost,
> &intf->dev);
> ??? if (result)
> ??? ??? goto free;
> -??? shost->hostdata[0] = (unsigned
> long)devinfo;
> +??? shost->hostdata[0] = (unsigned
> long)tpinfo;
>
> -??? devinfo->intf = intf;
> -??? devinfo->udev = udev;
> -??? uas_configure_endpoints(devinfo);
> +??? tpinfo->intf = intf;
> +??? tpinfo->udev = udev;
> +??? uas_configure_endpoints(tpinfo);
>
> ??? scsi_scan_host(shost);
> ??? usb_set_intfdata(intf, shost);
> ??? return result;
> ? free:
> -??? kfree(devinfo);
> +??? kfree(tpinfo);
> ??? if (shost)
> ??? ???
> scsi_host_put(shost);
> ??? return result;
> @@ -740,16 +1082,16 @@ static void uas_disconnect(struct
> usb_interface *intf)
> ??? struct usb_device *udev =
> interface_to_usbdev(intf);
> ??? struct usb_host_endpoint *eps[3];
> ??? struct Scsi_Host *shost =
> usb_get_intfdata(intf);
> -??? struct uas_dev_info *devinfo = (void
> *)shost->hostdata[0];
> +??? struct uas_tport_info *tpinfo = (void
> *)shost->hostdata[0];
>
> ??? scsi_remove_host(shost);
>
> -??? eps[0] = usb_pipe_endpoint(udev,
> devinfo->status_pipe);
> -??? eps[1] = usb_pipe_endpoint(udev,
> devinfo->data_in_pipe);
> -??? eps[2] = usb_pipe_endpoint(udev,
> devinfo->data_out_pipe);
> +??? eps[0] = usb_pipe_endpoint(udev,
> tpinfo->status_pipe);
> +??? eps[1] = usb_pipe_endpoint(udev,
> tpinfo->data_in_pipe);
> +??? eps[2] = usb_pipe_endpoint(udev,
> tpinfo->data_out_pipe);
> ??? usb_free_streams(intf, eps, 3,
> GFP_KERNEL);
>
> -??? kfree(devinfo);

> +??? kfree(tpinfo);
> }
>
> /*
> --
> 1.7.0.1
>
>

2010-12-14 07:58:42

by Luben Tuikov

[permalink] [raw]
Subject: Re: [PATCH] [USB] UAS: Achitecture; TMF; more

--- On Fri, 11/19/10, Sarah Sharp <[email protected]> wrote:
>
> Why do you limit the number of streams this way?? USB
> 3.0 devices can
> support up to 65533 streams, so why place an arbitrary
> limit like this?
> Just use the MaxStreams field in the SuperSpeed Endpoint
> Companion
> descriptor in the call to usb_alloc_streams().? I
> don't see how limiting
> the number of streams you allocate to 128 when the original
> driver tried
> to allocate 256 is an improvement.

Is any of this relevant now? Just take a look at uasp.c--it's how it's done.

> Couldn't the renaming code could have made it into a
> separate
> patch?

That was the least of the changed I would've done. Refer to uasp.c--it's how it's done.

> It also makes it hard to wade through the
> changes.? You should
> have two patches at least: one to rename uas_dev_info, and
> one to rename
> qdepth.? There's places in the code where you've
> changed how you use
> qdepth, and it's hard to see because the rename and change
> are in one
> patch.

I think I've responded to this before to Alan and Greg, it basically goes some like "... one changed warranted another and so on and so forth and I ended up with this big thing..." Better off rip the whole thing off and do it right.

> Matthew and you can dicker over the name for uas_dev_info,

"dicker"? Sorry, I've not time for this. Refer to uasp.c--it's how it's done.

> but I really
> don't understand your reasoning for renaming it to
> uas_tport_info.? What
> type of "ports" are you talking about?? USB ports or
> some other type of
> ports?? To me, uas_dev_info made sense because it
> stored information
> about the particular UAS device, not the USB port it was
> attached to...

Refer to uasp.c, and the 18 points which I listed talking about the I_T_L_Q nexus...

> Tag ID or stream ID is the same, I suppose.? But
> stream IDs are always
> positive integers, so why should tag be signed?

I don't like to educate on mailing lists, especially here. (It always leads to this pointless argument, so I either ignore the question or patronize the questioner in order to get on with it.) Let alone trying to pass a patch that changes this... Just use uasp.c.

> I'm a bit confused by this comment, perhaps because you
> didn't explain
> what TMFs are in your comment or your commit message.
>
> However, if you're telling the xHCI driver to allocate
> UAS_MAX_STREAMS,
> the UAS driver gets to put stream ID 1 to stream ID
> UAS_MAX_STREAMS in
> urb->stream_id.? If you submit an URB with
> urb->stream_id greater than
> UAS_MAX_STREAMS, the xHCI driver is going to reject that
> submission.
>
> It looks like from the comment you expect to be able to use
> a stream ID
> as big as UAS_MAX_STREAMS+2+TMF_MAX_TAGS, is that what you
> were trying
> to do?

Check with uasp.c on how it's done.

Luben