[<prev] [next>] [day] [month] [year] [list]
Message-ID: <681149.61182.qm@web31816.mail.mud.yahoo.com>
Date: Fri, 10 Dec 2010 02:52:24 -0800 (PST)
From: Luben Tuikov <ltuikov@...oo.com>
To: Greg KH <greg@...ah.com>, linux-usb@...r.kernel.org,
linux-kernel@...r.kernel.org,
Matthew Wilcox <willy@...ux.intel.com>
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 <ltuikov@...oo.com> wrote:
> From: Luben Tuikov <ltuikov@...oo.com>
> Subject: [PATCH] [USB] UAS: Achitecture; TMF; more
> To: "Greg KH" <greg@...ah.com>, linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org, "Matthew Wilcox" <willy@...ux.intel.com>
> 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@...oo.com>
> ---
>
> 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@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists