diff options
Diffstat (limited to 'drivers/usb/storage/uas.c')
| -rw-r--r-- | drivers/usb/storage/uas.c | 688 | 
1 files changed, 438 insertions, 250 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d966b59f7d7..511b2295316 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -2,6 +2,7 @@   * USB Attached SCSI   * Note that this is not the same as the USB Mass Storage driver   * + * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013   * Copyright Matthew Wilcox for Intel Corp, 2010   * Copyright Sarah Sharp for Intel Corp, 2010   * @@ -13,17 +14,21 @@  #include <linux/types.h>  #include <linux/module.h>  #include <linux/usb.h> +#include <linux/usb_usual.h>  #include <linux/usb/hcd.h>  #include <linux/usb/storage.h>  #include <linux/usb/uas.h>  #include <scsi/scsi.h> +#include <scsi/scsi_eh.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> +#include "uas-detect.h" +  /*   * The r00-r01c specs define this version of the SENSE IU data structure.   * It's still in use by several different firmware releases. @@ -45,12 +50,17 @@ struct uas_dev_info {  	struct usb_anchor sense_urbs;  	struct usb_anchor data_urbs;  	int qdepth, resetting; -	struct response_ui response; +	struct response_iu response;  	unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;  	unsigned use_streams:1;  	unsigned uas_sense_old:1; +	unsigned running_task:1; +	unsigned shutdown:1;  	struct scsi_cmnd *cmnd;  	spinlock_t lock; +	struct work_struct work; +	struct list_head inflight_list; +	struct list_head dead_list;  };  enum { @@ -85,103 +95,117 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,  				struct uas_dev_info *devinfo, gfp_t gfp);  static void uas_do_work(struct work_struct *work);  static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); +static void uas_free_streams(struct uas_dev_info *devinfo); +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller); -static DECLARE_WORK(uas_work, uas_do_work); -static DEFINE_SPINLOCK(uas_work_lock); -static LIST_HEAD(uas_work_list); - +/* Must be called with devinfo->lock held, will temporary unlock the lock */  static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, -				 struct uas_cmd_info *cmdinfo) +				 struct uas_cmd_info *cmdinfo, +				 unsigned long *lock_flags)  { -	unsigned long flags; -  	/*  	 * The UNLINK_DATA_URBS flag makes sure uas_try_complete  	 * (called by urb completion) doesn't release cmdinfo  	 * underneath us.  	 */ -	spin_lock_irqsave(&devinfo->lock, flags);  	cmdinfo->state |= UNLINK_DATA_URBS; -	spin_unlock_irqrestore(&devinfo->lock, flags); +	spin_unlock_irqrestore(&devinfo->lock, *lock_flags);  	if (cmdinfo->data_in_urb)  		usb_unlink_urb(cmdinfo->data_in_urb);  	if (cmdinfo->data_out_urb)  		usb_unlink_urb(cmdinfo->data_out_urb); -	spin_lock_irqsave(&devinfo->lock, flags); +	spin_lock_irqsave(&devinfo->lock, *lock_flags);  	cmdinfo->state &= ~UNLINK_DATA_URBS; -	spin_unlock_irqrestore(&devinfo->lock, flags);  }  static void uas_do_work(struct work_struct *work)  { +	struct uas_dev_info *devinfo = +		container_of(work, struct uas_dev_info, work);  	struct uas_cmd_info *cmdinfo; -	struct uas_cmd_info *temp; -	struct list_head list;  	unsigned long flags;  	int err; -	spin_lock_irq(&uas_work_lock); -	list_replace_init(&uas_work_list, &list); -	spin_unlock_irq(&uas_work_lock); - -	list_for_each_entry_safe(cmdinfo, temp, &list, list) { +	spin_lock_irqsave(&devinfo->lock, flags); +	list_for_each_entry(cmdinfo, &devinfo->inflight_list, list) {  		struct scsi_pointer *scp = (void *)cmdinfo; -		struct scsi_cmnd *cmnd = container_of(scp, -							struct scsi_cmnd, SCp); -		struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; -		spin_lock_irqsave(&devinfo->lock, flags); +		struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, +						      SCp); + +		if (!(cmdinfo->state & IS_IN_WORK_LIST)) +			continue; +  		err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);  		if (!err)  			cmdinfo->state &= ~IS_IN_WORK_LIST; -		spin_unlock_irqrestore(&devinfo->lock, flags); -		if (err) { -			list_del(&cmdinfo->list); -			spin_lock_irq(&uas_work_lock); -			list_add_tail(&cmdinfo->list, &uas_work_list); -			spin_unlock_irq(&uas_work_lock); -			schedule_work(&uas_work); -		} +		else +			schedule_work(&devinfo->work);  	} +	spin_unlock_irqrestore(&devinfo->lock, flags);  } -static void uas_abort_work(struct uas_dev_info *devinfo) +static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, +			      struct uas_cmd_info *cmdinfo, +			      int result, const char *caller) +{ +	struct scsi_pointer *scp = (void *)cmdinfo; +	struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + +	uas_log_cmd_state(cmnd, caller); +	WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); +	WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); +	cmdinfo->state |= COMMAND_ABORTED; +	cmdinfo->state &= ~IS_IN_WORK_LIST; +	cmnd->result = result << 16; +	list_move_tail(&cmdinfo->list, &devinfo->dead_list); +} + +static void uas_abort_inflight(struct uas_dev_info *devinfo, int result, +			       const char *caller)  {  	struct uas_cmd_info *cmdinfo;  	struct uas_cmd_info *temp; -	struct list_head list;  	unsigned long flags; -	spin_lock_irq(&uas_work_lock); -	list_replace_init(&uas_work_list, &list); -	spin_unlock_irq(&uas_work_lock); +	spin_lock_irqsave(&devinfo->lock, flags); +	list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, list) +		uas_mark_cmd_dead(devinfo, cmdinfo, result, caller); +	spin_unlock_irqrestore(&devinfo->lock, flags); +} + +static void uas_add_work(struct uas_cmd_info *cmdinfo) +{ +	struct scsi_pointer *scp = (void *)cmdinfo; +	struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); +	struct uas_dev_info *devinfo = cmnd->device->hostdata; + +	WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); +	cmdinfo->state |= IS_IN_WORK_LIST; +	schedule_work(&devinfo->work); +} + +static void uas_zap_dead(struct uas_dev_info *devinfo) +{ +	struct uas_cmd_info *cmdinfo; +	struct uas_cmd_info *temp; +	unsigned long flags;  	spin_lock_irqsave(&devinfo->lock, flags); -	list_for_each_entry_safe(cmdinfo, temp, &list, list) { +	list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, list) {  		struct scsi_pointer *scp = (void *)cmdinfo; -		struct scsi_cmnd *cmnd = container_of(scp, -							struct scsi_cmnd, SCp); -		struct uas_dev_info *di = (void *)cmnd->device->hostdata; - -		if (di == devinfo) { -			cmdinfo->state |= COMMAND_ABORTED; -			cmdinfo->state &= ~IS_IN_WORK_LIST; -			if (devinfo->resetting) { -				/* uas_stat_cmplt() will not do that -				 * when a device reset is in -				 * progress */ -				cmdinfo->state &= ~COMMAND_INFLIGHT; -			} -			uas_try_complete(cmnd, __func__); -		} else { -			/* not our uas device, relink into list */ -			list_del(&cmdinfo->list); -			spin_lock_irq(&uas_work_lock); -			list_add_tail(&cmdinfo->list, &uas_work_list); -			spin_unlock_irq(&uas_work_lock); -		} +		struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, +						      SCp); +		uas_log_cmd_state(cmnd, __func__); +		WARN_ON_ONCE(!(cmdinfo->state & COMMAND_ABORTED)); +		/* all urbs are killed, clear inflight bits */ +		cmdinfo->state &= ~(COMMAND_INFLIGHT | +				    DATA_IN_URB_INFLIGHT | +				    DATA_OUT_URB_INFLIGHT); +		uas_try_complete(cmnd, __func__);  	} +	devinfo->running_task = 0;  	spin_unlock_irqrestore(&devinfo->lock, flags);  } @@ -259,20 +283,19 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)  	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;  	struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; -	WARN_ON(!spin_is_locked(&devinfo->lock)); +	WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));  	if (cmdinfo->state & (COMMAND_INFLIGHT |  			      DATA_IN_URB_INFLIGHT |  			      DATA_OUT_URB_INFLIGHT |  			      UNLINK_DATA_URBS))  		return -EBUSY; -	BUG_ON(cmdinfo->state & COMMAND_COMPLETED); +	WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED);  	cmdinfo->state |= COMMAND_COMPLETED;  	usb_free_urb(cmdinfo->data_in_urb);  	usb_free_urb(cmdinfo->data_out_urb); -	if (cmdinfo->state & COMMAND_ABORTED) { +	if (cmdinfo->state & COMMAND_ABORTED)  		scmd_printk(KERN_INFO, cmnd, "abort completed\n"); -		cmnd->result = DID_ABORT << 16; -	} +	list_del(&cmdinfo->list);  	cmnd->scsi_done(cmnd);  	return 0;  } @@ -286,11 +309,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,  	cmdinfo->state |= direction | SUBMIT_STATUS_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); -		cmdinfo->state |= IS_IN_WORK_LIST; -		spin_unlock(&uas_work_lock); -		schedule_work(&uas_work); +		uas_add_work(cmdinfo);  	}  } @@ -298,14 +317,20 @@ static void uas_stat_cmplt(struct urb *urb)  {  	struct iu *iu = urb->transfer_buffer;  	struct Scsi_Host *shost = urb->context; -	struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;  	struct scsi_cmnd *cmnd;  	struct uas_cmd_info *cmdinfo;  	unsigned long flags;  	u16 tag;  	if (urb->status) { -		dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status); +		if (urb->status == -ENOENT) { +			dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n", +				urb->stream_id); +		} else { +			dev_err(&urb->dev->dev, "stat urb: status %d\n", +				urb->status); +		}  		usb_free_urb(urb);  		return;  	} @@ -324,6 +349,9 @@ static void uas_stat_cmplt(struct urb *urb)  	if (!cmnd) {  		if (iu->iu_id == IU_ID_RESPONSE) { +			if (!devinfo->running_task) +				dev_warn(&urb->dev->dev, +				    "stat urb: recv unexpected response iu\n");  			/* store results for uas_eh_task_mgmt() */  			memcpy(&devinfo->response, iu, sizeof(devinfo->response));  		} @@ -346,17 +374,25 @@ static void uas_stat_cmplt(struct urb *urb)  			uas_sense(urb, cmnd);  		if (cmnd->result != 0) {  			/* cancel data transfers on error */ -			spin_unlock_irqrestore(&devinfo->lock, flags); -			uas_unlink_data_urbs(devinfo, cmdinfo); -			spin_lock_irqsave(&devinfo->lock, flags); +			uas_unlink_data_urbs(devinfo, cmdinfo, &flags);  		}  		cmdinfo->state &= ~COMMAND_INFLIGHT;  		uas_try_complete(cmnd, __func__);  		break;  	case IU_ID_READ_READY: +		if (!cmdinfo->data_in_urb || +				(cmdinfo->state & DATA_IN_URB_INFLIGHT)) { +			scmd_printk(KERN_ERR, cmnd, "unexpected read rdy\n"); +			break; +		}  		uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);  		break;  	case IU_ID_WRITE_READY: +		if (!cmdinfo->data_out_urb || +				(cmdinfo->state & DATA_OUT_URB_INFLIGHT)) { +			scmd_printk(KERN_ERR, cmnd, "unexpected write rdy\n"); +			break; +		}  		uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);  		break;  	default: @@ -383,8 +419,15 @@ static void uas_data_cmplt(struct urb *urb)  		sdb = scsi_out(cmnd);  		cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT;  	} -	BUG_ON(sdb == NULL); -	if (urb->status) { +	if (sdb == NULL) { +		WARN_ON_ONCE(1); +	} else if (urb->status) { +		if (urb->status != -ECONNRESET) { +			uas_log_cmd_state(cmnd, __func__); +			scmd_printk(KERN_ERR, cmnd, +				"data cmplt err %d stream %d\n", +				urb->status, urb->stream_id); +		}  		/* error: no data transfered */  		sdb->resid = sdb->length;  	} else { @@ -394,6 +437,17 @@ static void uas_data_cmplt(struct urb *urb)  	spin_unlock_irqrestore(&devinfo->lock, flags);  } +static void uas_cmd_cmplt(struct urb *urb) +{ +	struct scsi_cmnd *cmnd = urb->context; + +	if (urb->status) { +		uas_log_cmd_state(cmnd, __func__); +		scmd_printk(KERN_ERR, cmnd, "cmd cmplt err %d\n", 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_cmnd *cmnd, @@ -408,8 +462,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,  		goto out;  	usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,  			  uas_data_cmplt, cmnd); -	if (devinfo->use_streams) -		urb->stream_id = stream_id; +	urb->stream_id = stream_id;  	urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;  	urb->sg = sdb->table.sgl;   out: @@ -442,7 +495,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,  }  static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, -					struct scsi_cmnd *cmnd, u16 stream_id) +					struct scsi_cmnd *cmnd)  {  	struct usb_device *udev = devinfo->udev;  	struct scsi_device *sdev = cmnd->device; @@ -472,7 +525,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,  	memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);  	usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, -							usb_free_urb, NULL); +							uas_cmd_cmplt, cmnd);  	urb->transfer_flags |= URB_FREE_BUFFER;   out:  	return urb; @@ -512,13 +565,17 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp,  	}  	usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), -			  usb_free_urb, NULL); +			  uas_cmd_cmplt, cmnd);  	urb->transfer_flags |= URB_FREE_BUFFER; +	usb_anchor_urb(urb, &devinfo->cmd_urbs);  	err = usb_submit_urb(urb, gfp); -	if (err) +	if (err) { +		usb_unanchor_urb(urb); +		uas_log_cmd_state(cmnd, __func__); +		scmd_printk(KERN_ERR, cmnd, "task submission err %d\n", err);  		goto err; -	usb_anchor_urb(urb, &devinfo->cmd_urbs); +	}  	return 0; @@ -533,38 +590,43 @@ err:   * daft to me.   */ -static int uas_submit_sense_urb(struct Scsi_Host *shost, -				gfp_t gfp, unsigned int stream) +static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, +					gfp_t gfp, unsigned int stream)  { -	struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; +	struct Scsi_Host *shost = cmnd->device->host; +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;  	struct urb *urb; +	int err;  	urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream);  	if (!urb) -		return SCSI_MLQUEUE_DEVICE_BUSY; -	if (usb_submit_urb(urb, gfp)) { +		return NULL; +	usb_anchor_urb(urb, &devinfo->sense_urbs); +	err = usb_submit_urb(urb, gfp); +	if (err) { +		usb_unanchor_urb(urb); +		uas_log_cmd_state(cmnd, __func__);  		shost_printk(KERN_INFO, shost, -			     "sense urb submission failure\n"); +			     "sense urb submission error %d stream %d\n", +			     err, stream);  		usb_free_urb(urb); -		return SCSI_MLQUEUE_DEVICE_BUSY; +		return NULL;  	} -	usb_anchor_urb(urb, &devinfo->sense_urbs); -	return 0; +	return urb;  }  static int uas_submit_urbs(struct scsi_cmnd *cmnd,  			   struct uas_dev_info *devinfo, gfp_t gfp)  {  	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; +	struct urb *urb;  	int err; -	WARN_ON(!spin_is_locked(&devinfo->lock)); +	WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));  	if (cmdinfo->state & SUBMIT_STATUS_URB) { -		err = uas_submit_sense_urb(cmnd->device->host, gfp, -					   cmdinfo->stream); -		if (err) { -			return err; -		} +		urb = uas_submit_sense_urb(cmnd, gfp, cmdinfo->stream); +		if (!urb) +			return SCSI_MLQUEUE_DEVICE_BUSY;  		cmdinfo->state &= ~SUBMIT_STATUS_URB;  	} @@ -578,14 +640,18 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,  	}  	if (cmdinfo->state & SUBMIT_DATA_IN_URB) { -		if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) { +		usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); +		err = usb_submit_urb(cmdinfo->data_in_urb, gfp); +		if (err) { +			usb_unanchor_urb(cmdinfo->data_in_urb); +			uas_log_cmd_state(cmnd, __func__);  			scmd_printk(KERN_INFO, cmnd, -					"data in urb submission failure\n"); +				"data in urb submission error %d stream %d\n", +				err, cmdinfo->data_in_urb->stream_id);  			return SCSI_MLQUEUE_DEVICE_BUSY;  		}  		cmdinfo->state &= ~SUBMIT_DATA_IN_URB;  		cmdinfo->state |= DATA_IN_URB_INFLIGHT; -		usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);  	}  	if (cmdinfo->state & ALLOC_DATA_OUT_URB) { @@ -598,33 +664,37 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,  	}  	if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { -		if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) { +		usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); +		err = usb_submit_urb(cmdinfo->data_out_urb, gfp); +		if (err) { +			usb_unanchor_urb(cmdinfo->data_out_urb); +			uas_log_cmd_state(cmnd, __func__);  			scmd_printk(KERN_INFO, cmnd, -					"data out urb submission failure\n"); +				"data out urb submission error %d stream %d\n", +				err, cmdinfo->data_out_urb->stream_id);  			return SCSI_MLQUEUE_DEVICE_BUSY;  		}  		cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;  		cmdinfo->state |= DATA_OUT_URB_INFLIGHT; -		usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);  	}  	if (cmdinfo->state & ALLOC_CMD_URB) { -		cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd, -						     cmdinfo->stream); +		cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd);  		if (!cmdinfo->cmd_urb)  			return SCSI_MLQUEUE_DEVICE_BUSY;  		cmdinfo->state &= ~ALLOC_CMD_URB;  	}  	if (cmdinfo->state & SUBMIT_CMD_URB) { -		usb_get_urb(cmdinfo->cmd_urb); -		if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) { +		usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); +		err = usb_submit_urb(cmdinfo->cmd_urb, gfp); +		if (err) { +			usb_unanchor_urb(cmdinfo->cmd_urb); +			uas_log_cmd_state(cmnd, __func__);  			scmd_printk(KERN_INFO, cmnd, -					"cmd urb submission failure\n"); +				    "cmd urb submission error %d\n", err);  			return SCSI_MLQUEUE_DEVICE_BUSY;  		} -		usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); -		usb_put_urb(cmdinfo->cmd_urb);  		cmdinfo->cmd_urb = NULL;  		cmdinfo->state &= ~SUBMIT_CMD_URB;  		cmdinfo->state |= COMMAND_INFLIGHT; @@ -644,18 +714,22 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,  	BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); +	spin_lock_irqsave(&devinfo->lock, flags); +  	if (devinfo->resetting) {  		cmnd->result = DID_ERROR << 16;  		cmnd->scsi_done(cmnd); +		spin_unlock_irqrestore(&devinfo->lock, flags);  		return 0;  	} -	spin_lock_irqsave(&devinfo->lock, flags);  	if (devinfo->cmnd) {  		spin_unlock_irqrestore(&devinfo->lock, flags);  		return SCSI_MLQUEUE_DEVICE_BUSY;  	} +	memset(cmdinfo, 0, sizeof(*cmdinfo)); +  	if (blk_rq_tagged(cmnd->request)) {  		cmdinfo->stream = cmnd->request->tag + 2;  	} else { @@ -692,13 +766,10 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,  			spin_unlock_irqrestore(&devinfo->lock, flags);  			return SCSI_MLQUEUE_DEVICE_BUSY;  		} -		spin_lock(&uas_work_lock); -		list_add_tail(&cmdinfo->list, &uas_work_list); -		cmdinfo->state |= IS_IN_WORK_LIST; -		spin_unlock(&uas_work_lock); -		schedule_work(&uas_work); +		uas_add_work(cmdinfo);  	} +	list_add_tail(&cmdinfo->list, &devinfo->inflight_list);  	spin_unlock_irqrestore(&devinfo->lock, flags);  	return 0;  } @@ -709,16 +780,36 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,  			    const char *fname, u8 function)  {  	struct Scsi_Host *shost = cmnd->device->host; -	struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; -	u16 tag = devinfo->qdepth - 1; +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; +	u16 tag = devinfo->qdepth;  	unsigned long flags; +	struct urb *sense_urb; +	int result = SUCCESS;  	spin_lock_irqsave(&devinfo->lock, flags); + +	if (devinfo->resetting) { +		spin_unlock_irqrestore(&devinfo->lock, flags); +		return FAILED; +	} + +	if (devinfo->running_task) { +		shost_printk(KERN_INFO, shost, +			     "%s: %s: error already running a task\n", +			     __func__, fname); +		spin_unlock_irqrestore(&devinfo->lock, flags); +		return FAILED; +	} + +	devinfo->running_task = 1;  	memset(&devinfo->response, 0, sizeof(devinfo->response)); -	if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) { +	sense_urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC, +					 devinfo->use_streams ? tag : 0); +	if (!sense_urb) {  		shost_printk(KERN_INFO, shost,  			     "%s: %s: submit sense urb failed\n",  			     __func__, fname); +		devinfo->running_task = 0;  		spin_unlock_irqrestore(&devinfo->lock, flags);  		return FAILED;  	} @@ -726,29 +817,41 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,  		shost_printk(KERN_INFO, shost,  			     "%s: %s: submit task mgmt urb failed\n",  			     __func__, fname); +		devinfo->running_task = 0;  		spin_unlock_irqrestore(&devinfo->lock, flags); +		usb_kill_urb(sense_urb);  		return FAILED;  	}  	spin_unlock_irqrestore(&devinfo->lock, flags);  	if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { +		/* +		 * Note we deliberately do not clear running_task here. If we +		 * allow new tasks to be submitted, there is no way to figure +		 * out if a received response_iu is for the failed task or for +		 * the new one. A bus-reset will eventually clear running_task. +		 */  		shost_printk(KERN_INFO, shost,  			     "%s: %s timed out\n", __func__, fname);  		return FAILED;  	} + +	spin_lock_irqsave(&devinfo->lock, flags); +	devinfo->running_task = 0;  	if (be16_to_cpu(devinfo->response.tag) != tag) {  		shost_printk(KERN_INFO, shost,  			     "%s: %s failed (wrong tag %d/%d)\n", __func__,  			     fname, be16_to_cpu(devinfo->response.tag), tag); -		return FAILED; -	} -	if (devinfo->response.response_code != RC_TMF_COMPLETE) { +		result = FAILED; +	} else if (devinfo->response.response_code != RC_TMF_COMPLETE) {  		shost_printk(KERN_INFO, shost,  			     "%s: %s failed (rc 0x%x)\n", __func__,  			     fname, devinfo->response.response_code); -		return FAILED; +		result = FAILED;  	} -	return SUCCESS; +	spin_unlock_irqrestore(&devinfo->lock, flags); + +	return result;  }  static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) @@ -758,22 +861,19 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)  	unsigned long flags;  	int ret; -	uas_log_cmd_state(cmnd, __func__);  	spin_lock_irqsave(&devinfo->lock, flags); -	cmdinfo->state |= COMMAND_ABORTED; -	if (cmdinfo->state & IS_IN_WORK_LIST) { -		spin_lock(&uas_work_lock); -		list_del(&cmdinfo->list); -		cmdinfo->state &= ~IS_IN_WORK_LIST; -		spin_unlock(&uas_work_lock); + +	if (devinfo->resetting) { +		spin_unlock_irqrestore(&devinfo->lock, flags); +		return FAILED;  	} + +	uas_mark_cmd_dead(devinfo, cmdinfo, DID_ABORT, __func__);  	if (cmdinfo->state & COMMAND_INFLIGHT) {  		spin_unlock_irqrestore(&devinfo->lock, flags);  		ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);  	} else { -		spin_unlock_irqrestore(&devinfo->lock, flags); -		uas_unlink_data_urbs(devinfo, cmdinfo); -		spin_lock_irqsave(&devinfo->lock, flags); +		uas_unlink_data_urbs(devinfo, cmdinfo, &flags);  		uas_try_complete(cmnd, __func__);  		spin_unlock_irqrestore(&devinfo->lock, flags);  		ret = SUCCESS; @@ -795,14 +895,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)  	struct usb_device *udev = devinfo->udev;  	int err; +	err = usb_lock_device_for_reset(udev, devinfo->intf); +	if (err) { +		shost_printk(KERN_ERR, sdev->host, +			     "%s FAILED to get lock err %d\n", __func__, err); +		return FAILED; +	} + +	shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__);  	devinfo->resetting = 1; -	uas_abort_work(devinfo); +	uas_abort_inflight(devinfo, DID_RESET, __func__);  	usb_kill_anchored_urbs(&devinfo->cmd_urbs);  	usb_kill_anchored_urbs(&devinfo->sense_urbs);  	usb_kill_anchored_urbs(&devinfo->data_urbs); +	uas_zap_dead(devinfo);  	err = usb_reset_device(udev);  	devinfo->resetting = 0; +	usb_unlock_device(udev); +  	if (err) {  		shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__);  		return FAILED; @@ -814,7 +925,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)  static int uas_slave_alloc(struct scsi_device *sdev)  { -	sdev->hostdata = (void *)sdev->host->hostdata[0]; +	sdev->hostdata = (void *)sdev->host->hostdata; + +	/* USB has unusual DMA-alignment requirements: Although the +	 * starting address of each scatter-gather element doesn't matter, +	 * the length of each element except the last must be divisible +	 * by the Bulk maxpacket value.  There's currently no way to +	 * express this by block-layer constraints, so we'll cop out +	 * and simply require addresses to be aligned at 512-byte +	 * boundaries.  This is okay since most block I/O involves +	 * hardware sectors that are multiples of 512 bytes in length, +	 * and since host controllers up through USB 2.0 have maxpacket +	 * values no larger than 512. +	 * +	 * But it doesn't suffice for Wireless USB, where Bulk maxpacket +	 * values can be as large as 2048.  To make that work properly +	 * will require changes to the block layer. +	 */ +	blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); +  	return 0;  } @@ -822,7 +951,7 @@ static int uas_slave_configure(struct scsi_device *sdev)  {  	struct uas_dev_info *devinfo = sdev->hostdata;  	scsi_set_tag_type(sdev, MSG_ORDERED_TAG); -	scsi_activate_tcq(sdev, devinfo->qdepth - 3); +	scsi_activate_tcq(sdev, devinfo->qdepth - 2);  	return 0;  } @@ -843,7 +972,14 @@ static struct scsi_host_template uas_host_template = {  	.ordered_tag = 1,  }; +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ +		    vendorName, productName, useProtocol, useTransport, \ +		    initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ +	.driver_info = (flags) } +  static struct usb_device_id uas_usb_ids[] = { +#	include "unusual_uas.h"  	{ 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) },  	/* 0xaa is a prototype device I happen to have access to */ @@ -852,105 +988,55 @@ static struct usb_device_id uas_usb_ids[] = {  };  MODULE_DEVICE_TABLE(usb, uas_usb_ids); -static int uas_is_interface(struct usb_host_interface *intf) -{ -	return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && -		intf->desc.bInterfaceSubClass == USB_SC_SCSI && -		intf->desc.bInterfaceProtocol == USB_PR_UAS); -} - -static int uas_isnt_supported(struct usb_device *udev) -{ -	struct usb_hcd *hcd = bus_to_hcd(udev->bus); - -	dev_warn(&udev->dev, "The driver for the USB controller %s does not " -			"support scatter-gather which is\n", -			hcd->driver->description); -	dev_warn(&udev->dev, "required by the UAS driver. Please try an" -			"alternative USB controller if you wish to use UAS.\n"); -	return -ENODEV; -} +#undef UNUSUAL_DEV  static int uas_switch_interface(struct usb_device *udev, -						struct usb_interface *intf) +				struct usb_interface *intf)  { -	int i; -	int sg_supported = udev->bus->sg_tablesize != 0; - -	for (i = 0; i < intf->num_altsetting; i++) { -		struct usb_host_interface *alt = &intf->altsetting[i]; - -		if (uas_is_interface(alt)) { -			if (!sg_supported) -				return uas_isnt_supported(udev); -			return usb_set_interface(udev, -						alt->desc.bInterfaceNumber, -						alt->desc.bAlternateSetting); -		} -	} +	int alt; + +	alt = uas_find_uas_alt_setting(intf); +	if (alt < 0) +		return alt; -	return -ENODEV; +	return usb_set_interface(udev, +			intf->altsetting[0].desc.bInterfaceNumber, alt);  } -static void uas_configure_endpoints(struct uas_dev_info *devinfo) +static int uas_configure_endpoints(struct uas_dev_info *devinfo)  {  	struct usb_host_endpoint *eps[4] = { }; -	struct usb_interface *intf = devinfo->intf;  	struct usb_device *udev = devinfo->udev; -	struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint; -	unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints; +	int r;  	devinfo->uas_sense_old = 0;  	devinfo->cmnd = NULL; -	for (i = 0; i < n_endpoints; i++) { -		unsigned char *extra = endpoint[i].extra; -		int len = endpoint[i].extralen; -		while (len > 1) { -			if (extra[1] == USB_DT_PIPE_USAGE) { -				unsigned pipe_id = extra[2]; -				if (pipe_id > 0 && pipe_id < 5) -					eps[pipe_id - 1] = &endpoint[i]; -				break; -			} -			len -= extra[0]; -			extra += extra[0]; -		} -	} +	r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); +	if (r) +		return r; -	/* -	 * Assume that if we didn't find a control pipe descriptor, we're -	 * using a device with old firmware that happens to be set up like -	 * 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); -	} else { -		devinfo->cmd_pipe = usb_sndbulkpipe(udev, -						eps[0]->desc.bEndpointAddress); -		devinfo->status_pipe = usb_rcvbulkpipe(udev, -						eps[1]->desc.bEndpointAddress); -		devinfo->data_in_pipe = usb_rcvbulkpipe(udev, -						eps[2]->desc.bEndpointAddress); -		devinfo->data_out_pipe = usb_sndbulkpipe(udev, -						eps[3]->desc.bEndpointAddress); -	} +	devinfo->cmd_pipe = usb_sndbulkpipe(udev, +					    usb_endpoint_num(&eps[0]->desc)); +	devinfo->status_pipe = usb_rcvbulkpipe(udev, +					    usb_endpoint_num(&eps[1]->desc)); +	devinfo->data_in_pipe = usb_rcvbulkpipe(udev, +					    usb_endpoint_num(&eps[2]->desc)); +	devinfo->data_out_pipe = usb_sndbulkpipe(udev, +					    usb_endpoint_num(&eps[3]->desc)); -	devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256, -								GFP_KERNEL); -	if (devinfo->qdepth < 0) { +	if (udev->speed != USB_SPEED_SUPER) {  		devinfo->qdepth = 256;  		devinfo->use_streams = 0;  	} else { +		devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, +						    3, 256, GFP_NOIO); +		if (devinfo->qdepth < 0) +			return devinfo->qdepth;  		devinfo->use_streams = 1;  	} + +	return 0;  }  static void uas_free_streams(struct uas_dev_info *devinfo) @@ -961,33 +1047,26 @@ static void uas_free_streams(struct uas_dev_info *devinfo)  	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(devinfo->intf, eps, 3, GFP_KERNEL); +	usb_free_streams(devinfo->intf, eps, 3, GFP_NOIO);  } -/* - * XXX: What I'd like to do here is register a SCSI host for each USB host in - * the system.  Follow usb-storage's design of registering a SCSI host for - * each USB device for the moment.  Can implement this by walking up the - * USB hierarchy until we find a USB host. - */  static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)  { -	int result; -	struct Scsi_Host *shost; +	int result = -ENOMEM; +	struct Scsi_Host *shost = NULL;  	struct uas_dev_info *devinfo;  	struct usb_device *udev = interface_to_usbdev(intf); -	if (uas_switch_interface(udev, intf)) +	if (!uas_use_uas_driver(intf, id))  		return -ENODEV; -	devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL); -	if (!devinfo) -		return -ENOMEM; +	if (uas_switch_interface(udev, intf)) +		return -ENODEV; -	result = -ENOMEM; -	shost = scsi_host_alloc(&uas_host_template, sizeof(void *)); +	shost = scsi_host_alloc(&uas_host_template, +				sizeof(struct uas_dev_info));  	if (!shost) -		goto free; +		goto set_alt0;  	shost->max_cmd_len = 16 + 252;  	shost->max_id = 1; @@ -995,33 +1074,41 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)  	shost->max_channel = 0;  	shost->sg_tablesize = udev->bus->sg_tablesize; +	devinfo = (struct uas_dev_info *)shost->hostdata;  	devinfo->intf = intf;  	devinfo->udev = udev;  	devinfo->resetting = 0; +	devinfo->running_task = 0; +	devinfo->shutdown = 0;  	init_usb_anchor(&devinfo->cmd_urbs);  	init_usb_anchor(&devinfo->sense_urbs);  	init_usb_anchor(&devinfo->data_urbs);  	spin_lock_init(&devinfo->lock); -	uas_configure_endpoints(devinfo); +	INIT_WORK(&devinfo->work, uas_do_work); +	INIT_LIST_HEAD(&devinfo->inflight_list); +	INIT_LIST_HEAD(&devinfo->dead_list); -	result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); +	result = uas_configure_endpoints(devinfo);  	if (result) -		goto free; +		goto set_alt0; -	result = scsi_add_host(shost, &intf->dev); +	result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);  	if (result) -		goto deconfig_eps; +		goto free_streams; -	shost->hostdata[0] = (unsigned long)devinfo; +	usb_set_intfdata(intf, shost); +	result = scsi_add_host(shost, &intf->dev); +	if (result) +		goto free_streams;  	scsi_scan_host(shost); -	usb_set_intfdata(intf, shost);  	return result; -deconfig_eps: +free_streams:  	uas_free_streams(devinfo); - free: -	kfree(devinfo); +	usb_set_intfdata(intf, NULL); +set_alt0: +	usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);  	if (shost)  		scsi_host_put(shost);  	return result; @@ -1029,45 +1116,146 @@ deconfig_eps:  static int uas_pre_reset(struct usb_interface *intf)  { -/* XXX: Need to return 1 if it's not our device in error handling */ +	struct Scsi_Host *shost = usb_get_intfdata(intf); +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; +	unsigned long flags; + +	if (devinfo->shutdown) +		return 0; + +	/* Block new requests */ +	spin_lock_irqsave(shost->host_lock, flags); +	scsi_block_requests(shost); +	spin_unlock_irqrestore(shost->host_lock, flags); + +	/* Wait for any pending requests to complete */ +	flush_work(&devinfo->work); +	if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { +		shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); +		return 1; +	} + +	uas_free_streams(devinfo); +  	return 0;  }  static int uas_post_reset(struct usb_interface *intf)  { -/* XXX: Need to return 1 if it's not our device in error handling */ +	struct Scsi_Host *shost = usb_get_intfdata(intf); +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; +	unsigned long flags; + +	if (devinfo->shutdown) +		return 0; + +	if (uas_configure_endpoints(devinfo) != 0) { +		shost_printk(KERN_ERR, shost, +			     "%s: alloc streams error after reset", __func__); +		return 1; +	} + +	spin_lock_irqsave(shost->host_lock, flags); +	scsi_report_bus_reset(shost, 0); +	spin_unlock_irqrestore(shost->host_lock, flags); + +	scsi_unblock_requests(shost); + +	return 0; +} + +static int uas_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct Scsi_Host *shost = usb_get_intfdata(intf); +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + +	/* Wait for any pending requests to complete */ +	flush_work(&devinfo->work); +	if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { +		shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); +		return -ETIME; +	} + +	return 0; +} + +static int uas_resume(struct usb_interface *intf) +{ +	return 0; +} + +static int uas_reset_resume(struct usb_interface *intf) +{ +	struct Scsi_Host *shost = usb_get_intfdata(intf); +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; +	unsigned long flags; + +	if (uas_configure_endpoints(devinfo) != 0) { +		shost_printk(KERN_ERR, shost, +			     "%s: alloc streams error after reset", __func__); +		return -EIO; +	} + +	spin_lock_irqsave(shost->host_lock, flags); +	scsi_report_bus_reset(shost, 0); +	spin_unlock_irqrestore(shost->host_lock, flags); +  	return 0;  }  static void uas_disconnect(struct usb_interface *intf)  {  	struct Scsi_Host *shost = usb_get_intfdata(intf); -	struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;  	devinfo->resetting = 1; -	uas_abort_work(devinfo); +	cancel_work_sync(&devinfo->work); +	uas_abort_inflight(devinfo, DID_NO_CONNECT, __func__);  	usb_kill_anchored_urbs(&devinfo->cmd_urbs);  	usb_kill_anchored_urbs(&devinfo->sense_urbs);  	usb_kill_anchored_urbs(&devinfo->data_urbs); +	uas_zap_dead(devinfo);  	scsi_remove_host(shost);  	uas_free_streams(devinfo); -	kfree(devinfo); +	scsi_host_put(shost);  }  /* - * XXX: Should this plug into libusual so we can auto-upgrade devices from - * Bulk-Only to UAS? + * Put the device back in usb-storage mode on shutdown, as some BIOS-es + * hang on reboot when the device is still in uas mode. Note the reset is + * necessary as some devices won't revert to usb-storage mode without it.   */ +static void uas_shutdown(struct device *dev) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_device *udev = interface_to_usbdev(intf); +	struct Scsi_Host *shost = usb_get_intfdata(intf); +	struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + +	if (system_state != SYSTEM_RESTART) +		return; + +	devinfo->shutdown = 1; +	uas_free_streams(devinfo); +	usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); +	usb_reset_device(udev); +} +  static struct usb_driver uas_driver = {  	.name = "uas",  	.probe = uas_probe,  	.disconnect = uas_disconnect,  	.pre_reset = uas_pre_reset,  	.post_reset = uas_post_reset, +	.suspend = uas_suspend, +	.resume = uas_resume, +	.reset_resume = uas_reset_resume, +	.drvwrap.driver.shutdown = uas_shutdown,  	.id_table = uas_usb_ids,  };  module_usb_driver(uas_driver);  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matthew Wilcox and Sarah Sharp"); +MODULE_AUTHOR( +	"Hans de Goede <hdegoede@redhat.com>, Matthew Wilcox and Sarah Sharp");  | 
