Re: [PATCH 2/2] Add UAS driver

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

 



Am Dienstag, 28. September 2010, 12:14:56 schrieb Matthew Wilcox:
> From: Matthew Wilcox <matthew.r.wilcox@xxxxxxxxx>
> 
> USB Attached SCSI is a new protocol specified jointly by the SCSI T10
> committee and the USB Implementors Forum.
> 
> Signed-off-by: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
> ---
>  MAINTAINERS                  |    8 +
>  drivers/usb/storage/Kconfig  |   13 +
>  drivers/usb/storage/Makefile |    1 +
>  drivers/usb/storage/uas.c    |  751 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 773 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/usb/storage/uas.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 668682d..30a5c22 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5900,6 +5900,14 @@ S:	Maintained
>  F:	Documentation/usb/acm.txt
>  F:	drivers/usb/class/cdc-acm.*
>  
> +USB ATTACHED SCSI
> +M:	Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
> +M:	Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
> +L:	linux-usb@xxxxxxxxxxxxxxx
> +L:	linux-scsi@xxxxxxxxxxxxxxx
> +S:	Supported
> +F:	drivers/usb/storage/uas.c
> +
>  USB BLOCK DRIVER (UB ub)
>  M:	Pete Zaitcev <zaitcev@xxxxxxxxxx>
>  L:	linux-usb@xxxxxxxxxxxxxxx
> diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
> index 8a372ba..f2767cf 100644
> --- a/drivers/usb/storage/Kconfig
> +++ b/drivers/usb/storage/Kconfig
> @@ -172,6 +172,19 @@ config USB_STORAGE_CYPRESS_ATACB
>  
>  	  If this driver is compiled as a module, it will be named ums-cypress.
>  
> +config USB_UAS
> +	tristate "USB Attached SCSI"
> +	depends on USB && SCSI
> +	help
> +	  The USB Attached SCSI protocol is supported by some USB
> +	  storage devices.  It permits higher performance by supporting
> +	  multiple outstanding commands.
> +
> +	  If you don't know whether you have a UAS device, it is safe to
> +	  say 'Y' or 'M' here and the kernel will use the right driver.
> +
> +	  If you compile this driver as a module, it will be named uas.
> +
>  config USB_LIBUSUAL
>  	bool "The shared table of common (or usual) storage devices"
>  	depends on USB
> diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
> index ef7e5a8..0332aa5 100644
> --- a/drivers/usb/storage/Makefile
> +++ b/drivers/usb/storage/Makefile
> @@ -7,6 +7,7 @@
>  
>  EXTRA_CFLAGS	:= -Idrivers/scsi
>  
> +obj-$(CONFIG_USB_UAS)		+= uas.o
>  obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
>  
>  usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)	+= debug.o
> diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
> new file mode 100644
> index 0000000..7fd049b
> --- /dev/null
> +++ b/drivers/usb/storage/uas.c
> @@ -0,0 +1,751 @@
> +/*
> + * USB Attached SCSI
> + * Note that this is not the same as the USB Mass Storage driver
> + *
> + * Copyright Matthew Wilcox for Intel Corp, 2010
> + * Copyright Sarah Sharp for Intel Corp, 2010
> + *
> + * Distributed under the terms of the GNU GPL, version two.
> + */
> +
> +#include <linux/blkdev.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/usb.h>
> +#include <linux/usb/storage.h>
> +
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_dbg.h>
> +#include <scsi/scsi_cmnd.h>
> +#include <scsi/scsi_device.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> +
> +/* Common header for all IUs */
> +struct iu {
> +	__u8 iu_id;
> +	__u8 rsvd1;
> +	__be16 tag;
> +};
> +
> +enum {
> +	IU_ID_COMMAND		= 0x01,
> +	IU_ID_STATUS		= 0x03,
> +	IU_ID_RESPONSE		= 0x04,
> +	IU_ID_TASK_MGMT		= 0x05,
> +	IU_ID_READ_READY	= 0x06,
> +	IU_ID_WRITE_READY	= 0x07,
> +};
> +
> +struct command_iu {
> +	__u8 iu_id;
> +	__u8 rsvd1;
> +	__be16 tag;
> +	__u8 prio_attr;
> +	__u8 rsvd5;
> +	__u8 len;
> +	__u8 rsvd7;
> +	struct scsi_lun lun;
> +	__u8 cdb[16];	/* XXX: Overflow-checking tools may misunderstand */
> +};
> +
> +struct sense_iu {
> +	__u8 iu_id;
> +	__u8 rsvd1;
> +	__be16 tag;
> +	__be16 status_qual;
> +	__u8 status;
> +	__u8 service_response;
> +	__u8 rsvd8[6];
> +	__be16 len;
> +	__u8 sense[SCSI_SENSE_BUFFERSIZE];
> +};
> +
> +/*
> + * The r00-r01c specs define this version of the SENSE IU data structure.
> + * It's still in use by several different firmware releases.
> + */
> +struct sense_iu_old {
> +	__u8 iu_id;
> +	__u8 rsvd1;
> +	__be16 tag;
> +	__be16 len;
> +	__u8 status;
> +	__u8 service_response;
> +	__u8 sense[SCSI_SENSE_BUFFERSIZE];
> +};
> +
> +enum {
> +	CMD_PIPE_ID		= 1,
> +	STATUS_PIPE_ID		= 2,
> +	DATA_IN_PIPE_ID		= 3,
> +	DATA_OUT_PIPE_ID	= 4,
> +
> +	UAS_SIMPLE_TAG		= 0,
> +	UAS_HEAD_TAG		= 1,
> +	UAS_ORDERED_TAG		= 2,
> +	UAS_ACA			= 4,
> +};
> +
> +struct uas_dev_info {
> +	struct usb_interface *intf;
> +	struct usb_device *udev;
> +	int qdepth;
> +	unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
> +	unsigned use_streams:1;
> +	unsigned uas_sense_old:1;
> +};
> +
> +enum {
> +	ALLOC_SENSE_URB		= (1 << 0),
> +	SUBMIT_SENSE_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),
> +};
> +
> +/* Overrides scsi_pointer */
> +struct uas_cmd_info {
> +	unsigned int state;
> +	unsigned int stream;
> +	struct urb *cmd_urb;
> +	struct urb *sense_urb;
> +	struct urb *data_in_urb;
> +	struct urb *data_out_urb;
> +	struct list_head list;
> +};
> +
> +/* I hate forward declarations, but I actually have a loop */
> +static int uas_submit_urbs(struct scsi_cmnd *cmnd,
> +				struct uas_dev_info *devinfo, gfp_t gfp);
> +
> +static DEFINE_SPINLOCK(uas_work_lock);
> +static LIST_HEAD(uas_work_list);
> +
> +static void uas_do_work(struct work_struct *work)
> +{
> +	struct uas_cmd_info *cmdinfo;
> +	struct list_head list;
> +
> +	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);
> +		uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_KERNEL);

Deadlocky, use GFP_NOIO
> +	}
> +}
> +
> +static DECLARE_WORK(uas_work, uas_do_work);
> +
> +static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
> +{
> +	struct sense_iu *sense_iu = urb->transfer_buffer;
> +	struct scsi_device *sdev = cmnd->device;
> +
> +	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;
> +		}
> +		memcpy(cmnd->sense_buffer, sense_iu->sense, len);
> +	}
> +
> +	cmnd->result = sense_iu->status;
> +	if (sdev->current_cmnd)
> +		sdev->current_cmnd = NULL;

the condition is absolutely senseless

> +	cmnd->scsi_done(cmnd);
> +	usb_free_urb(urb);
> +}
> +
> +static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
> +{
> +	struct sense_iu_old *sense_iu = urb->transfer_buffer;
> +	struct scsi_device *sdev = cmnd->device;
> +
> +	if (urb->actual_length > 8) {
> +		unsigned len = be16_to_cpup(&sense_iu->len) - 2;
> +		if (len + 8 != urb->actual_length) {
> +			int newlen = min(len + 8, urb->actual_length) - 8;
> +			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;
> +		}
> +		memcpy(cmnd->sense_buffer, sense_iu->sense, len);
> +	}
> +
> +	cmnd->result = sense_iu->status;
> +	if (sdev->current_cmnd)
> +		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)
> +{
> +	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
> +	int err;
> +
> +	cmdinfo->state = direction | SUBMIT_SENSE_URB;
> +	err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
> +	if (err) {
> +		spin_lock(&uas_work_lock);
> +		list_add_tail(&cmdinfo->list, &uas_work_list);
> +		spin_unlock(&uas_work_lock);
> +		schedule_work(&uas_work);
> +	}
> +}
> +
> +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 scsi_cmnd *cmnd;
> +	u16 tag;
> +
> +	if (urb->status) {
> +		dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
> +		usb_free_urb(urb);
> +		return;
> +	}
> +
> +	tag = be16_to_cpup(&iu->tag) - 1;
> +	if (sdev->current_cmnd)
> +		cmnd = sdev->current_cmnd;
> +	else
> +		cmnd = scsi_find_tag(sdev, tag);
> +	if (!cmnd)
> +		return;

Where is the URB freed?

> +	switch (iu->iu_id) {
> +	case IU_ID_STATUS:
> +		if (urb->actual_length < 16)
> +			devinfo->uas_sense_old = 1;
> +		if (devinfo->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);
> +		break;
> +	case IU_ID_WRITE_READY:
> +		uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
> +		break;
> +	default:
> +		scmd_printk(KERN_ERR, cmnd,
> +			"Bogus IU (%d) received on status pipe\n", iu->iu_id);
> +	}
> +}
> +

> +static int uas_pre_reset(struct usb_interface *intf)
> +{
> +/* XXX: Need to return 1 if it's not our device in error handling */
> +	return 0;
> +}

You should probably make sure that no further commands are
queued from this point on.

> +static int uas_post_reset(struct usb_interface *intf)
> +{
> +/* XXX: Need to return 1 if it's not our device in error handling */
> +	return 0;
> +}
> +
> +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];
> +
> +	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);
> +	usb_free_streams(intf, eps, 3, GFP_KERNEL);

This implies that this can fail due to a lack of memory.
Then what?

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


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

  Powered by Linux