Re: [PATCH] [USB] UAS: Achitecture; TMF; more

From: Luben Tuikov
Date: Fri Dec 10 2010 - 05:52:46 EST


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


--- On Fri, 11/5/10, Luben Tuikov <ltuikov@xxxxxxxxx> wrote:

> From: Luben Tuikov <ltuikov@xxxxxxxxx>
> Subject: [PATCH] [USB] UAS: Achitecture; TMF; more
> To: "Greg KH" <greg@xxxxxxxxx>, linux-usb@xxxxxxxxxxxxxxx, linux-kernel@xxxxxxxxxxxxxxx, "Matthew Wilcox" <willy@xxxxxxxxxxxxxxx>
> 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 <ltuikov@xxxxxxxxx>
> ---
>
> 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
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/