diff options
Diffstat (limited to 'drivers/scsi/libsas')
| -rw-r--r-- | drivers/scsi/libsas/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/scsi/libsas/Makefile | 6 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 974 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 376 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_dump.c | 5 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_dump.h | 12 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_event.c | 104 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 593 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 123 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_init.c | 373 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 114 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 58 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_port.c | 132 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 676 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_task.c | 1 | 
15 files changed, 2359 insertions, 1196 deletions
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index 18f33cd5441..9dafe64e7c7 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -46,11 +46,3 @@ config SCSI_SAS_HOST_SMP  		Allows sas hosts to receive SMP frames.  Selecting this  		option builds an SMP interpreter into libsas.  Say  		N here if you want to save the few kb this consumes. - -config SCSI_SAS_LIBSAS_DEBUG -	bool "Compile the SAS Domain Transport Attributes in debug mode" -	default y -	depends on SCSI_SAS_LIBSAS -	help -		Compiles the SAS Layer in debug mode.  In debug mode, the -		SAS Layer prints diagnostic and debug messages. diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 1ad1323c60f..2e70140f70c 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -21,10 +21,6 @@  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  # USA -ifeq ($(CONFIG_SCSI_SAS_LIBSAS_DEBUG),y) -	EXTRA_CFLAGS += -DSAS_DEBUG -endif -  obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o  libsas-y +=  sas_init.o     \  		sas_phy.o      \ @@ -36,4 +32,4 @@ libsas-y +=  sas_init.o     \  		sas_scsi_host.o \  		sas_task.o  libsas-$(CONFIG_SCSI_SAS_ATA) +=	sas_ata.o -libsas-$(CONFIG_SCSI_SAS_HOST_SMP) +=	sas_host_smp.o
\ No newline at end of file +libsas-$(CONFIG_SCSI_SAS_HOST_SMP) +=	sas_host_smp.o diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index e1a395b438e..766098af4eb 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -23,6 +23,8 @@  #include <linux/scatterlist.h>  #include <linux/slab.h> +#include <linux/async.h> +#include <linux/export.h>  #include <scsi/sas_ata.h>  #include "sas_internal.h" @@ -71,13 +73,13 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)  		case SAS_SG_ERR:  			return AC_ERR_INVALID; -		case SAM_STAT_CHECK_CONDITION:  		case SAS_OPEN_TO:  		case SAS_OPEN_REJECT:  			SAS_DPRINTK("%s: Saw error %d.  What to do?\n",  				    __func__, ts->stat);  			return AC_ERR_OTHER; +		case SAM_STAT_CHECK_CONDITION:  		case SAS_ABORTED_TASK:  			return AC_ERR_DEV; @@ -93,57 +95,80 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)  static void sas_ata_task_done(struct sas_task *task)  {  	struct ata_queued_cmd *qc = task->uldd_task; -	struct domain_device *dev; +	struct domain_device *dev = task->dev;  	struct task_status_struct *stat = &task->task_status;  	struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; -	struct sas_ha_struct *sas_ha; +	struct sas_ha_struct *sas_ha = dev->port->ha;  	enum ata_completion_errors ac;  	unsigned long flags; +	struct ata_link *link; +	struct ata_port *ap; + +	spin_lock_irqsave(&dev->done_lock, flags); +	if (test_bit(SAS_HA_FROZEN, &sas_ha->state)) +		task = NULL; +	else if (qc && qc->scsicmd) +		ASSIGN_SAS_TASK(qc->scsicmd, NULL); +	spin_unlock_irqrestore(&dev->done_lock, flags); + +	/* check if libsas-eh got to the task before us */ +	if (unlikely(!task)) +		return;  	if (!qc)  		goto qc_already_gone; -	dev = qc->ap->private_data; -	sas_ha = dev->port->ha; - -	spin_lock_irqsave(dev->sata_dev.ap->lock, flags); -	if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD) { -		ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); -		qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); -		dev->sata_dev.sstatus = resp->sstatus; -		dev->sata_dev.serror = resp->serror; -		dev->sata_dev.scontrol = resp->scontrol; -	} else if (stat->stat != SAM_STAT_GOOD) { +	ap = qc->ap; +	link = &ap->link; + +	spin_lock_irqsave(ap->lock, flags); +	/* check if we lost the race with libata/sas_ata_post_internal() */ +	if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) { +		spin_unlock_irqrestore(ap->lock, flags); +		if (qc->scsicmd) +			goto qc_already_gone; +		else { +			/* if eh is not involved and the port is frozen then the +			 * ata internal abort process has taken responsibility +			 * for this sas_task +			 */ +			return; +		} +	} + +	if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || +	    ((stat->stat == SAM_STAT_CHECK_CONDITION && +	      dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { +		memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE); + +		if (!link->sactive) { +			qc->err_mask |= ac_err_mask(dev->sata_dev.fis[2]); +		} else { +			link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]); +			if (unlikely(link->eh_info.err_mask)) +				qc->flags |= ATA_QCFLAG_FAILED; +		} +	} else {  		ac = sas_to_ata_err(stat);  		if (ac) {  			SAS_DPRINTK("%s: SAS error %x\n", __func__,  				    stat->stat);  			/* We saw a SAS error. Send a vague error. */ -			qc->err_mask = ac; -			dev->sata_dev.tf.feature = 0x04; /* status err */ -			dev->sata_dev.tf.command = ATA_ERR; +			if (!link->sactive) { +				qc->err_mask = ac; +			} else { +				link->eh_info.err_mask |= AC_ERR_DEV; +				qc->flags |= ATA_QCFLAG_FAILED; +			} + +			dev->sata_dev.fis[3] = 0x04; /* status err */ +			dev->sata_dev.fis[2] = ATA_ERR;  		}  	}  	qc->lldd_task = NULL; -	if (qc->scsicmd) -		ASSIGN_SAS_TASK(qc->scsicmd, NULL);  	ata_qc_complete(qc); -	spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); - -	/* -	 * If the sas_task has an ata qc, a scsi_cmnd and the aborted -	 * flag is set, then we must have come in via the libsas EH -	 * functions.  When we exit this function, we need to put the -	 * scsi_cmnd on the list of finished errors.  The ata_qc_complete -	 * call cleans up the libata side of things but we're protected -	 * from the scsi_cmnd going away because the scsi_cmnd is owned -	 * by the EH, making libata's call to scsi_done a NOP. -	 */ -	spin_lock_irqsave(&task->task_state_lock, flags); -	if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED) -		scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q); -	spin_unlock_irqrestore(&task->task_state_lock, flags); +	spin_unlock_irqrestore(ap->lock, flags);  qc_already_gone:  	list_del_init(&task->list); @@ -152,23 +177,30 @@ qc_already_gone:  static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)  { -	int res; +	unsigned long flags;  	struct sas_task *task; -	struct domain_device *dev = qc->ap->private_data; +	struct scatterlist *sg; +	int ret = AC_ERR_SYSTEM; +	unsigned int si, xfer = 0; +	struct ata_port *ap = qc->ap; +	struct domain_device *dev = ap->private_data;  	struct sas_ha_struct *sas_ha = dev->port->ha;  	struct Scsi_Host *host = sas_ha->core.shost;  	struct sas_internal *i = to_sas_internal(host->transportt); -	struct scatterlist *sg; -	unsigned int xfer = 0; -	unsigned int si; + +	/* TODO: audit callers to ensure they are ready for qc_issue to +	 * unconditionally re-enable interrupts +	 */ +	local_irq_save(flags); +	spin_unlock(ap->lock);  	/* If the device fell off, no sense in issuing commands */ -	if (dev->gone) -		return AC_ERR_SYSTEM; +	if (test_bit(SAS_DEV_GONE, &dev->state)) +		goto out;  	task = sas_alloc_task(GFP_ATOMIC);  	if (!task) -		return AC_ERR_SYSTEM; +		goto out;  	task->dev = dev;  	task->task_proto = SAS_PROTOCOL_STP;  	task->task_done = sas_ata_task_done; @@ -179,7 +211,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)  		qc->tf.nsect = 0;  	} -	ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis); +	ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8 *)&task->ata_task.fis);  	task->uldd_task = qc;  	if (ata_is_atapi(qc->tf.protocol)) {  		memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); @@ -213,62 +245,248 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)  		ASSIGN_SAS_TASK(qc->scsicmd, task);  	if (sas_ha->lldd_max_execute_num < 2) -		res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); +		ret = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);  	else -		res = sas_queue_up(task); +		ret = sas_queue_up(task);  	/* Examine */ -	if (res) { -		SAS_DPRINTK("lldd_execute_task returned: %d\n", res); +	if (ret) { +		SAS_DPRINTK("lldd_execute_task returned: %d\n", ret);  		if (qc->scsicmd)  			ASSIGN_SAS_TASK(qc->scsicmd, NULL);  		sas_free_task(task); -		return AC_ERR_SYSTEM; +		ret = AC_ERR_SYSTEM;  	} -	return 0; + out: +	spin_lock(ap->lock); +	local_irq_restore(flags); +	return ret;  }  static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)  {  	struct domain_device *dev = qc->ap->private_data; -	memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf)); +	ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf);  	return true;  } -static void sas_ata_phy_reset(struct ata_port *ap) +static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) +{ +	return to_sas_internal(dev->port->ha->core.shost->transportt); +} + +static void sas_get_ata_command_set(struct domain_device *dev); + +int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) +{ +	if (phy->attached_tproto & SAS_PROTOCOL_STP) +		dev->tproto = phy->attached_tproto; +	if (phy->attached_sata_dev) +		dev->tproto |= SAS_SATA_DEV; + +	if (phy->attached_dev_type == SAS_SATA_PENDING) +		dev->dev_type = SAS_SATA_PENDING; +	else { +		int res; + +		dev->dev_type = SAS_SATA_DEV; +		res = sas_get_report_phy_sata(dev->parent, phy->phy_id, +					      &dev->sata_dev.rps_resp); +		if (res) { +			SAS_DPRINTK("report phy sata to %016llx:0x%x returned " +				    "0x%x\n", SAS_ADDR(dev->parent->sas_addr), +				    phy->phy_id, res); +			return res; +		} +		memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis, +		       sizeof(struct dev_to_host_fis)); +		/* TODO switch to ata_dev_classify() */ +		sas_get_ata_command_set(dev); +	} +	return 0; +} + +static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy) +{ +	int res; + +	/* we weren't pending, so successfully end the reset sequence now */ +	if (dev->dev_type != SAS_SATA_PENDING) +		return 1; + +	/* hmmm, if this succeeds do we need to repost the domain_device to the +	 * lldd so it can pick up new parameters? +	 */ +	res = sas_get_ata_info(dev, phy); +	if (res) +		return 0; /* retry */ +	else +		return 1; +} + +static int smp_ata_check_ready(struct ata_link *link) +{ +	int res; +	struct ata_port *ap = link->ap; +	struct domain_device *dev = ap->private_data; +	struct domain_device *ex_dev = dev->parent; +	struct sas_phy *phy = sas_get_local_phy(dev); +	struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number]; + +	res = sas_ex_phy_discover(ex_dev, phy->number); +	sas_put_local_phy(phy); + +	/* break the wait early if the expander is unreachable, +	 * otherwise keep polling +	 */ +	if (res == -ECOMM) +		return res; +	if (res != SMP_RESP_FUNC_ACC) +		return 0; + +	switch (ex_phy->attached_dev_type) { +	case SAS_SATA_PENDING: +		return 0; +	case SAS_END_DEVICE: +		if (ex_phy->attached_sata_dev) +			return sas_ata_clear_pending(dev, ex_phy); +	default: +		return -ENODEV; +	} +} + +static int local_ata_check_ready(struct ata_link *link)  { +	struct ata_port *ap = link->ap;  	struct domain_device *dev = ap->private_data; -	struct sas_internal *i = -		to_sas_internal(dev->port->ha->core.shost->transportt); -	int res = TMF_RESP_FUNC_FAILED; +	struct sas_internal *i = dev_to_sas_internal(dev); + +	if (i->dft->lldd_ata_check_ready) +		return i->dft->lldd_ata_check_ready(dev); +	else { +		/* lldd's that don't implement 'ready' checking get the +		 * old default behavior of not coordinating reset +		 * recovery with libata +		 */ +		return 1; +	} +} + +static int sas_ata_printk(const char *level, const struct domain_device *ddev, +			  const char *fmt, ...) +{ +	struct ata_port *ap = ddev->sata_dev.ap; +	struct device *dev = &ddev->rphy->dev; +	struct va_format vaf; +	va_list args; +	int r; + +	va_start(args, fmt); + +	vaf.fmt = fmt; +	vaf.va = &args; -	if (i->dft->lldd_I_T_nexus_reset) -		res = i->dft->lldd_I_T_nexus_reset(dev); +	r = printk("%ssas: ata%u: %s: %pV", +		   level, ap->print_id, dev_name(dev), &vaf); + +	va_end(args); + +	return r; +} + +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, +			      unsigned long deadline) +{ +	int ret = 0, res; +	struct sas_phy *phy; +	struct ata_port *ap = link->ap; +	int (*check_ready)(struct ata_link *link); +	struct domain_device *dev = ap->private_data; +	struct sas_internal *i = dev_to_sas_internal(dev); + +	res = i->dft->lldd_I_T_nexus_reset(dev); +	if (res == -ENODEV) +		return res;  	if (res != TMF_RESP_FUNC_COMPLETE) -		SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); +		sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n"); +	phy = sas_get_local_phy(dev); +	if (scsi_is_sas_phy_local(phy)) +		check_ready = local_ata_check_ready; +	else +		check_ready = smp_ata_check_ready; +	sas_put_local_phy(phy); + +	ret = ata_wait_after_reset(link, deadline, check_ready); +	if (ret && ret != -EAGAIN) +		sas_ata_printk(KERN_ERR, dev, "reset failed (errno=%d)\n", ret); + +	/* XXX: if the class changes during the reset the upper layer +	 * should be informed, if the device has gone away we assume +	 * libsas will eventually delete it +	 */  	switch (dev->sata_dev.command_set) { -		case ATA_COMMAND_SET: -			SAS_DPRINTK("%s: Found ATA device.\n", __func__); -			ap->link.device[0].class = ATA_DEV_ATA; -			break; -		case ATAPI_COMMAND_SET: -			SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); -			ap->link.device[0].class = ATA_DEV_ATAPI; -			break; -		default: -			SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", -				    __func__, -				    dev->sata_dev.command_set); -			ap->link.device[0].class = ATA_DEV_UNKNOWN; -			break; +	case ATA_COMMAND_SET: +		*class = ATA_DEV_ATA; +		break; +	case ATAPI_COMMAND_SET: +		*class = ATA_DEV_ATAPI; +		break;  	}  	ap->cbl = ATA_CBL_SATA; +	return ret; +} + +/* + * notify the lldd to forget the sas_task for this internal ata command + * that bypasses scsi-eh + */ +static void sas_ata_internal_abort(struct sas_task *task) +{ +	struct sas_internal *si = dev_to_sas_internal(task->dev); +	unsigned long flags; +	int res; + +	spin_lock_irqsave(&task->task_state_lock, flags); +	if (task->task_state_flags & SAS_TASK_STATE_ABORTED || +	    task->task_state_flags & SAS_TASK_STATE_DONE) { +		spin_unlock_irqrestore(&task->task_state_lock, flags); +		SAS_DPRINTK("%s: Task %p already finished.\n", __func__, +			    task); +		goto out; +	} +	task->task_state_flags |= SAS_TASK_STATE_ABORTED; +	spin_unlock_irqrestore(&task->task_state_lock, flags); + +	res = si->dft->lldd_abort_task(task); + +	spin_lock_irqsave(&task->task_state_lock, flags); +	if (task->task_state_flags & SAS_TASK_STATE_DONE || +	    res == TMF_RESP_FUNC_COMPLETE) { +		spin_unlock_irqrestore(&task->task_state_lock, flags); +		goto out; +	} + +	/* XXX we are not prepared to deal with ->lldd_abort_task() +	 * failures.  TODO: lldds need to unconditionally forget about +	 * aborted ata tasks, otherwise we (likely) leak the sas task +	 * here +	 */ +	SAS_DPRINTK("%s: Task %p leaked.\n", __func__, task); + +	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) +		task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; +	spin_unlock_irqrestore(&task->task_state_lock, flags); + +	return; + out: +	list_del_init(&task->list); +	sas_free_task(task);  }  static void sas_ata_post_internal(struct ata_queued_cmd *qc) @@ -278,78 +496,63 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc)  	if (qc->err_mask) {  		/* -		 * Find the sas_task and kill it.  By this point, -		 * libata has decided to kill the qc, so we needn't -		 * bother with sas_ata_task_done.  But we still -		 * ought to abort the task. +		 * Find the sas_task and kill it.  By this point, libata +		 * has decided to kill the qc and has frozen the port. +		 * In this state sas_ata_task_done() will no longer free +		 * the sas_task, so we need to notify the lldd (via +		 * ->lldd_abort_task) that the task is dead and free it +		 *  ourselves.  		 */  		struct sas_task *task = qc->lldd_task; -		unsigned long flags;  		qc->lldd_task = NULL; -		if (task) { -			/* Should this be a AT(API) device reset? */ -			spin_lock_irqsave(&task->task_state_lock, flags); -			task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; -			spin_unlock_irqrestore(&task->task_state_lock, flags); - -			task->uldd_task = NULL; -			__sas_task_abort(task); -		} +		if (!task) +			return; +		task->uldd_task = NULL; +		sas_ata_internal_abort(task);  	}  } -static int sas_ata_scr_write(struct ata_link *link, unsigned int sc_reg_in, -			      u32 val) + +static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)  { -	struct domain_device *dev = link->ap->private_data; - -	SAS_DPRINTK("STUB %s\n", __func__); -	switch (sc_reg_in) { -		case SCR_STATUS: -			dev->sata_dev.sstatus = val; -			break; -		case SCR_CONTROL: -			dev->sata_dev.scontrol = val; -			break; -		case SCR_ERROR: -			dev->sata_dev.serror = val; -			break; -		case SCR_ACTIVE: -			dev->sata_dev.ap->link.sactive = val; -			break; -		default: -			return -EINVAL; -	} -	return 0; +	struct domain_device *dev = ap->private_data; +	struct sas_internal *i = dev_to_sas_internal(dev); + +	if (i->dft->lldd_ata_set_dmamode) +		i->dft->lldd_ata_set_dmamode(dev);  } -static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in, -			    u32 *val) +static void sas_ata_sched_eh(struct ata_port *ap)  { -	struct domain_device *dev = link->ap->private_data; +	struct domain_device *dev = ap->private_data; +	struct sas_ha_struct *ha = dev->port->ha; +	unsigned long flags; -	SAS_DPRINTK("STUB %s\n", __func__); -	switch (sc_reg_in) { -		case SCR_STATUS: -			*val = dev->sata_dev.sstatus; -			return 0; -		case SCR_CONTROL: -			*val = dev->sata_dev.scontrol; -			return 0; -		case SCR_ERROR: -			*val = dev->sata_dev.serror; -			return 0; -		case SCR_ACTIVE: -			*val = dev->sata_dev.ap->link.sactive; -			return 0; -		default: -			return -EINVAL; -	} +	spin_lock_irqsave(&ha->lock, flags); +	if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state)) +		ha->eh_active++; +	ata_std_sched_eh(ap); +	spin_unlock_irqrestore(&ha->lock, flags); +} + +void sas_ata_end_eh(struct ata_port *ap) +{ +	struct domain_device *dev = ap->private_data; +	struct sas_ha_struct *ha = dev->port->ha; +	unsigned long flags; + +	spin_lock_irqsave(&ha->lock, flags); +	if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state)) +		ha->eh_active--; +	spin_unlock_irqrestore(&ha->lock, flags);  }  static struct ata_port_operations sas_sata_ops = { -	.phy_reset		= sas_ata_phy_reset, +	.prereset		= ata_std_prereset, +	.hardreset		= sas_ata_hard_reset, +	.postreset		= ata_std_postreset, +	.error_handler		= ata_std_error_handler,  	.post_internal_cmd	= sas_ata_post_internal,  	.qc_defer               = ata_std_qc_defer,  	.qc_prep		= ata_noop_qc_prep, @@ -357,30 +560,27 @@ static struct ata_port_operations sas_sata_ops = {  	.qc_fill_rtf		= sas_ata_qc_fill_rtf,  	.port_start		= ata_sas_port_start,  	.port_stop		= ata_sas_port_stop, -	.scr_read		= sas_ata_scr_read, -	.scr_write		= sas_ata_scr_write +	.set_dmamode		= sas_ata_set_dmamode, +	.sched_eh		= sas_ata_sched_eh, +	.end_eh			= sas_ata_end_eh,  };  static struct ata_port_info sata_port_info = { -	.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | -		ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, -	.pio_mask = 0x1f, /* PIO0-4 */ -	.mwdma_mask = 0x07, /* MWDMA0-2 */ +	.flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, +	.pio_mask = ATA_PIO4, +	.mwdma_mask = ATA_MWDMA2,  	.udma_mask = ATA_UDMA6,  	.port_ops = &sas_sata_ops  }; -int sas_ata_init_host_and_port(struct domain_device *found_dev, -			       struct scsi_target *starget) +int sas_ata_init(struct domain_device *found_dev)  { -	struct Scsi_Host *shost = dev_to_shost(&starget->dev); -	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); +	struct sas_ha_struct *ha = found_dev->port->ha; +	struct Scsi_Host *shost = ha->core.shost;  	struct ata_port *ap; +	int rc; -	ata_host_init(&found_dev->sata_dev.ata_host, -		      ha->dev, -		      sata_port_info.flags, -		      &sas_sata_ops); +	ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops);  	ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host,  				&sata_port_info,  				shost); @@ -392,6 +592,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev,  	ap->private_data = found_dev;  	ap->cbl = ATA_CBL_SATA;  	ap->scsi_host = shost; +	rc = ata_sas_port_init(ap); +	if (rc) { +		ata_sas_port_destroy(ap); +		return rc; +	}  	found_dev->sata_dev.ap = ap;  	return 0; @@ -410,7 +615,6 @@ void sas_ata_task_abort(struct sas_task *task)  		spin_lock_irqsave(q->queue_lock, flags);  		blk_abort_request(qc->scsicmd->request);  		spin_unlock_irqrestore(q->queue_lock, flags); -		scsi_schedule_eh(qc->scsicmd->device->host);  		return;  	} @@ -422,168 +626,14 @@ void sas_ata_task_abort(struct sas_task *task)  	complete(waiting);  } -static void sas_task_timedout(unsigned long _task) -{ -	struct sas_task *task = (void *) _task; -	unsigned long flags; - -	spin_lock_irqsave(&task->task_state_lock, flags); -	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) -		task->task_state_flags |= SAS_TASK_STATE_ABORTED; -	spin_unlock_irqrestore(&task->task_state_lock, flags); - -	complete(&task->completion); -} - -static void sas_disc_task_done(struct sas_task *task) -{ -	if (!del_timer(&task->timer)) -		return; -	complete(&task->completion); -} - -#define SAS_DEV_TIMEOUT 10 - -/** - * sas_execute_task -- Basic task processing for discovery - * @task: the task to be executed - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction.  DMA_xxx - */ -static int sas_execute_task(struct sas_task *task, void *buffer, int size, -			    enum dma_data_direction dma_dir) -{ -	int res = 0; -	struct scatterlist *scatter = NULL; -	struct task_status_struct *ts = &task->task_status; -	int num_scatter = 0; -	int retries = 0; -	struct sas_internal *i = -		to_sas_internal(task->dev->port->ha->core.shost->transportt); - -	if (dma_dir != DMA_NONE) { -		scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); -		if (!scatter) -			goto out; - -		sg_init_one(scatter, buffer, size); -		num_scatter = 1; -	} - -	task->task_proto = task->dev->tproto; -	task->scatter = scatter; -	task->num_scatter = num_scatter; -	task->total_xfer_len = size; -	task->data_dir = dma_dir; -	task->task_done = sas_disc_task_done; -	if (dma_dir != DMA_NONE && -	    sas_protocol_ata(task->task_proto)) { -		task->num_scatter = dma_map_sg(task->dev->port->ha->dev, -					       task->scatter, -					       task->num_scatter, -					       task->data_dir); -	} - -	for (retries = 0; retries < 5; retries++) { -		task->task_state_flags = SAS_TASK_STATE_PENDING; -		init_completion(&task->completion); - -		task->timer.data = (unsigned long) task; -		task->timer.function = sas_task_timedout; -		task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; -		add_timer(&task->timer); - -		res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); -		if (res) { -			del_timer(&task->timer); -			SAS_DPRINTK("executing SAS discovery task failed:%d\n", -				    res); -			goto ex_err; -		} -		wait_for_completion(&task->completion); -		res = -ECOMM; -		if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { -			int res2; -			SAS_DPRINTK("task aborted, flags:0x%x\n", -				    task->task_state_flags); -			res2 = i->dft->lldd_abort_task(task); -			SAS_DPRINTK("came back from abort task\n"); -			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { -				if (res2 == TMF_RESP_FUNC_COMPLETE) -					continue; /* Retry the task */ -				else -					goto ex_err; -			} -		} -		if (task->task_status.stat == SAM_STAT_BUSY || -			   task->task_status.stat == SAM_STAT_TASK_SET_FULL || -			   task->task_status.stat == SAS_QUEUE_FULL) { -			SAS_DPRINTK("task: q busy, sleeping...\n"); -			schedule_timeout_interruptible(HZ); -		} else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) { -			struct scsi_sense_hdr shdr; - -			if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, -						  &shdr)) { -				SAS_DPRINTK("couldn't normalize sense\n"); -				continue; -			} -			if ((shdr.sense_key == 6 && shdr.asc == 0x29) || -			    (shdr.sense_key == 2 && shdr.asc == 4 && -			     shdr.ascq == 1)) { -				SAS_DPRINTK("device %016llx LUN: %016llx " -					    "powering up or not ready yet, " -					    "sleeping...\n", -					    SAS_ADDR(task->dev->sas_addr), -					    SAS_ADDR(task->ssp_task.LUN)); - -				schedule_timeout_interruptible(5*HZ); -			} else if (shdr.sense_key == 1) { -				res = 0; -				break; -			} else if (shdr.sense_key == 5) { -				break; -			} else { -				SAS_DPRINTK("dev %016llx LUN: %016llx " -					    "sense key:0x%x ASC:0x%x ASCQ:0x%x" -					    "\n", -					    SAS_ADDR(task->dev->sas_addr), -					    SAS_ADDR(task->ssp_task.LUN), -					    shdr.sense_key, -					    shdr.asc, shdr.ascq); -			} -		} else if (task->task_status.resp != SAS_TASK_COMPLETE || -			   task->task_status.stat != SAM_STAT_GOOD) { -			SAS_DPRINTK("task finished with resp:0x%x, " -				    "stat:0x%x\n", -				    task->task_status.resp, -				    task->task_status.stat); -			goto ex_err; -		} else { -			res = 0; -			break; -		} -	} -ex_err: -	if (dma_dir != DMA_NONE) { -		if (sas_protocol_ata(task->task_proto)) -			dma_unmap_sg(task->dev->port->ha->dev, -				     task->scatter, task->num_scatter, -				     task->data_dir); -		kfree(scatter); -	} -out: -	return res; -} - -/* ---------- SATA ---------- */ -  static void sas_get_ata_command_set(struct domain_device *dev)  {  	struct dev_to_host_fis *fis =  		(struct dev_to_host_fis *) dev->frame_rcvd; +	if (dev->dev_type == SAS_SATA_PENDING) +		return; +  	if ((fis->sector_count == 1 && /* ATA */  	     fis->lbal         == 1 &&  	     fis->lbam         == 0 && @@ -622,162 +672,244 @@ static void sas_get_ata_command_set(struct domain_device *dev)  		dev->sata_dev.command_set = ATAPI_COMMAND_SET;  } -/** - * sas_issue_ata_cmd -- Basic SATA command processing for discovery - * @dev: the device to send the command to - * @command: the command register - * @features: the features register - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction.  DMA_xxx - */ -static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, -			     u8 features, void *buffer, int size, -			     enum dma_data_direction dma_dir) +void sas_probe_sata(struct asd_sas_port *port)  { -	int res = 0; -	struct sas_task *task; -	struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) -		&dev->frame_rcvd[0]; +	struct domain_device *dev, *n; -	res = -ENOMEM; -	task = sas_alloc_task(GFP_KERNEL); -	if (!task) -		goto out; +	mutex_lock(&port->ha->disco_mutex); +	list_for_each_entry(dev, &port->disco_list, disco_list_node) { +		if (!dev_is_sata(dev)) +			continue; -	task->dev = dev; +		ata_sas_async_probe(dev->sata_dev.ap); +	} +	mutex_unlock(&port->ha->disco_mutex); -	task->ata_task.fis.fis_type = 0x27; -	task->ata_task.fis.command = command; -	task->ata_task.fis.features = features; -	task->ata_task.fis.device = d2h_fis->device; -	task->ata_task.retry_count = 1; +	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { +		if (!dev_is_sata(dev)) +			continue; -	res = sas_execute_task(task, buffer, size, dma_dir); +		sas_ata_wait_eh(dev); -	sas_free_task(task); -out: -	return res; -} +		/* if libata could not bring the link up, don't surface +		 * the device +		 */ +		if (ata_dev_disabled(sas_to_ata_dev(dev))) +			sas_fail_probe(dev, __func__, -ENODEV); +	} -#define ATA_IDENTIFY_DEV         0xEC -#define ATA_IDENTIFY_PACKET_DEV  0xA1 -#define ATA_SET_FEATURES         0xEF -#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 +} -/** - * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) - * @dev: STP/SATA device of interest (ATA/ATAPI) - * - * The LLDD has already been notified of this device, so that we can - * send FISes to it.  Here we try to get IDENTIFY DEVICE or IDENTIFY - * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its - * performance for this device. - */ -static int sas_discover_sata_dev(struct domain_device *dev) +static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func)  { -	int     res; -	__le16  *identify_x; -	u8      command; +	struct domain_device *dev, *n; -	identify_x = kzalloc(512, GFP_KERNEL); -	if (!identify_x) -		return -ENOMEM; +	list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) { +		if (!dev_is_sata(dev)) +			continue; -	if (dev->sata_dev.command_set == ATA_COMMAND_SET) { -		dev->sata_dev.identify_device = identify_x; -		command = ATA_IDENTIFY_DEV; -	} else { -		dev->sata_dev.identify_packet_device = identify_x; -		command = ATA_IDENTIFY_PACKET_DEV; -	} +		sas_ata_wait_eh(dev); -	res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, -				DMA_FROM_DEVICE); -	if (res) -		goto out_err; - -	/* lives on the media? */ -	if (le16_to_cpu(identify_x[0]) & 4) { -		/* incomplete response */ -		SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " -			    "dev %llx\n", SAS_ADDR(dev->sas_addr)); -		if (!(identify_x[83] & cpu_to_le16(1<<6))) -			goto cont1; -		res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, -					ATA_FEATURE_PUP_STBY_SPIN_UP, -					NULL, 0, DMA_NONE); -		if (res) -			goto cont1; - -		schedule_timeout_interruptible(5*HZ); /* More time? */ -		res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, -					DMA_FROM_DEVICE); -		if (res) -			goto out_err; +		/* if libata failed to power manage the device, tear it down */ +		if (ata_dev_disabled(sas_to_ata_dev(dev))) +			sas_fail_probe(dev, func, -ENODEV);  	} -cont1: -	/* XXX Hint: register this SATA device with SATL. -	   When this returns, dev->sata_dev->lu is alive and -	   present. -	sas_satl_register_dev(dev); -	*/ +} -	sas_fill_in_rphy(dev, dev->rphy); +void sas_suspend_sata(struct asd_sas_port *port) +{ +	struct domain_device *dev; -	return 0; -out_err: -	dev->sata_dev.identify_packet_device = NULL; -	dev->sata_dev.identify_device = NULL; -	kfree(identify_x); -	return res; +	mutex_lock(&port->ha->disco_mutex); +	list_for_each_entry(dev, &port->dev_list, dev_list_node) { +		struct sata_device *sata; + +		if (!dev_is_sata(dev)) +			continue; + +		sata = &dev->sata_dev; +		if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND) +			continue; + +		ata_sas_port_suspend(sata->ap); +	} +	mutex_unlock(&port->ha->disco_mutex); + +	sas_ata_flush_pm_eh(port, __func__);  } -static int sas_discover_sata_pm(struct domain_device *dev) +void sas_resume_sata(struct asd_sas_port *port)  { -	return -ENODEV; +	struct domain_device *dev; + +	mutex_lock(&port->ha->disco_mutex); +	list_for_each_entry(dev, &port->dev_list, dev_list_node) { +		struct sata_device *sata; + +		if (!dev_is_sata(dev)) +			continue; + +		sata = &dev->sata_dev; +		if (sata->ap->pm_mesg.event == PM_EVENT_ON) +			continue; + +		ata_sas_port_resume(sata->ap); +	} +	mutex_unlock(&port->ha->disco_mutex); + +	sas_ata_flush_pm_eh(port, __func__);  }  /**   * sas_discover_sata -- discover an STP/SATA domain device   * @dev: pointer to struct domain_device of interest   * - * First we notify the LLDD of this device, so we can send frames to - * it.  Then depending on the type of device we call the appropriate - * discover functions.  Once device discover is done, we notify the - * LLDD so that it can fine-tune its parameters for the device, by - * removing it and then adding it.  That is, the second time around, - * the driver would have certain fields, that it is looking at, set. - * Finally we initialize the kobj so that the device can be added to - * the system at registration time.  Devices directly attached to a HA - * port, have no parents.  All other devices do, and should have their - * "parent" pointer set appropriately before calling this function. + * Devices directly attached to a HA port, have no parents.  All other + * devices do, and should have their "parent" pointer set appropriately + * before calling this function.   */  int sas_discover_sata(struct domain_device *dev)  {  	int res; +	if (dev->dev_type == SAS_SATA_PM) +		return -ENODEV; +  	sas_get_ata_command_set(dev); +	sas_fill_in_rphy(dev, dev->rphy);  	res = sas_notify_lldd_dev_found(dev);  	if (res)  		return res; -	switch (dev->dev_type) { -	case SATA_DEV: -		res = sas_discover_sata_dev(dev); -		break; -	case SATA_PM: -		res = sas_discover_sata_pm(dev); -		break; -	default: -		break; -	} -	sas_notify_lldd_dev_gone(dev); -	if (!res) { -		sas_notify_lldd_dev_found(dev); -		res = sas_rphy_add(dev->rphy); +	sas_discover_event(dev->port, DISCE_PROBE); +	return 0; +} + +static void async_sas_ata_eh(void *data, async_cookie_t cookie) +{ +	struct domain_device *dev = data; +	struct ata_port *ap = dev->sata_dev.ap; +	struct sas_ha_struct *ha = dev->port->ha; + +	sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); +	ata_scsi_port_error_handler(ha->core.shost, ap); +	sas_put_device(dev); +} + +void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ +	struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); +	ASYNC_DOMAIN_EXCLUSIVE(async); +	int i; + +	/* it's ok to defer revalidation events during ata eh, these +	 * disks are in one of three states: +	 * 1/ present for initial domain discovery, and these +	 *    resets will cause bcn flutters +	 * 2/ hot removed, we'll discover that after eh fails +	 * 3/ hot added after initial discovery, lost the race, and need +	 *    to catch the next train. +	 */ +	sas_disable_revalidation(sas_ha); + +	spin_lock_irq(&sas_ha->phy_port_lock); +	for (i = 0; i < sas_ha->num_phys; i++) { +		struct asd_sas_port *port = sas_ha->sas_port[i]; +		struct domain_device *dev; + +		spin_lock(&port->dev_list_lock); +		list_for_each_entry(dev, &port->dev_list, dev_list_node) { +			if (!dev_is_sata(dev)) +				continue; + +			/* hold a reference over eh since we may be +			 * racing with final remove once all commands +			 * are completed +			 */ +			kref_get(&dev->kref); + +			async_schedule_domain(async_sas_ata_eh, dev, &async); +		} +		spin_unlock(&port->dev_list_lock);  	} +	spin_unlock_irq(&sas_ha->phy_port_lock); + +	async_synchronize_full_domain(&async); + +	sas_enable_revalidation(sas_ha); +} + +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, +		struct list_head *done_q) +{ +	struct scsi_cmnd *cmd, *n; +	struct domain_device *eh_dev; + +	do { +		LIST_HEAD(sata_q); +		eh_dev = NULL; + +		list_for_each_entry_safe(cmd, n, work_q, eh_entry) { +			struct domain_device *ddev = cmd_to_domain_dev(cmd); + +			if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) +				continue; +			if (eh_dev && eh_dev != ddev) +				continue; +			eh_dev = ddev; +			list_move(&cmd->eh_entry, &sata_q); +		} + +		if (!list_empty(&sata_q)) { +			struct ata_port *ap = eh_dev->sata_dev.ap; + +			sas_ata_printk(KERN_DEBUG, eh_dev, "cmd error handler\n"); +			ata_scsi_cmd_error_handler(shost, ap, &sata_q); +			/* +			 * ata's error handler may leave the cmd on the list +			 * so make sure they don't remain on a stack list +			 * about to go out of scope. +			 * +			 * This looks strange, since the commands are +			 * now part of no list, but the next error +			 * action will be ata_port_error_handler() +			 * which takes no list and sweeps them up +			 * anyway from the ata tag array. +			 */ +			while (!list_empty(&sata_q)) +				list_del_init(sata_q.next); +		} +	} while (eh_dev); +} + +void sas_ata_schedule_reset(struct domain_device *dev) +{ +	struct ata_eh_info *ehi; +	struct ata_port *ap; +	unsigned long flags; + +	if (!dev_is_sata(dev)) +		return; + +	ap = dev->sata_dev.ap; +	ehi = &ap->link.eh_info; + +	spin_lock_irqsave(ap->lock, flags); +	ehi->err_mask |= AC_ERR_TIMEOUT; +	ehi->action |= ATA_EH_RESET; +	ata_port_schedule_eh(ap); +	spin_unlock_irqrestore(ap->lock, flags); +} +EXPORT_SYMBOL_GPL(sas_ata_schedule_reset); + +void sas_ata_wait_eh(struct domain_device *dev) +{ +	struct ata_port *ap; + +	if (!dev_is_sata(dev)) +		return; -	return res; +	ap = dev->sata_dev.ap; +	ata_port_wait_eh(ap);  } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index f5831930df9..62b58d38ce2 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -24,35 +24,32 @@  #include <linux/scatterlist.h>  #include <linux/slab.h> +#include <linux/async.h>  #include <scsi/scsi_host.h>  #include <scsi/scsi_eh.h>  #include "sas_internal.h"  #include <scsi/scsi_transport.h>  #include <scsi/scsi_transport_sas.h> +#include <scsi/sas_ata.h>  #include "../scsi_sas_internal.h"  /* ---------- Basic task processing for discovery purposes ---------- */  void sas_init_dev(struct domain_device *dev)  { -        INIT_LIST_HEAD(&dev->siblings); -        INIT_LIST_HEAD(&dev->dev_list_node); -        switch (dev->dev_type) { -        case SAS_END_DEV: -                break; -        case EDGE_DEV: -        case FANOUT_DEV: -                INIT_LIST_HEAD(&dev->ex_dev.children); -                break; -        case SATA_DEV: -        case SATA_PM: -        case SATA_PM_PORT: -                INIT_LIST_HEAD(&dev->sata_dev.children); -                break; -        default: -                break; -        } +	switch (dev->dev_type) { +	case SAS_END_DEVICE: +		INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node); +		break; +	case SAS_EDGE_EXPANDER_DEVICE: +	case SAS_FANOUT_EXPANDER_DEVICE: +		INIT_LIST_HEAD(&dev->ex_dev.children); +		mutex_init(&dev->ex_dev.cmd_mutex); +		break; +	default: +		break; +	}  }  /* ---------- Domain device discovery ---------- */ @@ -68,19 +65,19 @@ void sas_init_dev(struct domain_device *dev)   */  static int sas_get_port_device(struct asd_sas_port *port)  { -	unsigned long flags;  	struct asd_sas_phy *phy;  	struct sas_rphy *rphy;  	struct domain_device *dev; +	int rc = -ENODEV; -	dev = kzalloc(sizeof(*dev), GFP_KERNEL); +	dev = sas_alloc_device();  	if (!dev)  		return -ENOMEM; -	spin_lock_irqsave(&port->phy_list_lock, flags); +	spin_lock_irq(&port->phy_list_lock);  	if (list_empty(&port->phy_list)) { -		spin_unlock_irqrestore(&port->phy_list_lock, flags); -		kfree(dev); +		spin_unlock_irq(&port->phy_list_lock); +		sas_put_device(dev);  		return -ENODEV;  	}  	phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); @@ -88,7 +85,7 @@ static int sas_get_port_device(struct asd_sas_port *port)  	memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),  					     (size_t)phy->frame_rcvd_size));  	spin_unlock(&phy->frame_rcvd_lock); -	spin_unlock_irqrestore(&port->phy_list_lock, flags); +	spin_unlock_irq(&port->phy_list_lock);  	if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {  		struct dev_to_host_fis *fis = @@ -96,9 +93,9 @@ static int sas_get_port_device(struct asd_sas_port *port)  		if (fis->interrupt_reason == 1 && fis->lbal == 1 &&  		    fis->byte_count_low==0x69 && fis->byte_count_high == 0x96  		    && (fis->device & ~0x10) == 0) -			dev->dev_type = SATA_PM; +			dev->dev_type = SAS_SATA_PM;  		else -			dev->dev_type = SATA_DEV; +			dev->dev_type = SAS_SATA_DEV;  		dev->tproto = SAS_PROTOCOL_SATA;  	} else {  		struct sas_identify_frame *id = @@ -110,16 +107,23 @@ static int sas_get_port_device(struct asd_sas_port *port)  	sas_init_dev(dev); +	dev->port = port;  	switch (dev->dev_type) { -	case SAS_END_DEV: -	case SATA_DEV: +	case SAS_SATA_DEV: +		rc = sas_ata_init(dev); +		if (rc) { +			rphy = NULL; +			break; +		} +		/* fall through */ +	case SAS_END_DEVICE:  		rphy = sas_end_device_alloc(port->port);  		break; -	case EDGE_DEV: +	case SAS_EDGE_EXPANDER_DEVICE:  		rphy = sas_expander_alloc(port->port,  					  SAS_EDGE_EXPANDER_DEVICE);  		break; -	case FANOUT_DEV: +	case SAS_FANOUT_EXPANDER_DEVICE:  		rphy = sas_expander_alloc(port->port,  					  SAS_FANOUT_EXPANDER_DEVICE);  		break; @@ -130,15 +134,15 @@ static int sas_get_port_device(struct asd_sas_port *port)  	}  	if (!rphy) { -		kfree(dev); -		return -ENODEV; +		sas_put_device(dev); +		return rc;  	} +  	rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;  	memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);  	sas_fill_in_rphy(dev, rphy);  	sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);  	port->port_dev = dev; -	dev->port = port;  	dev->linkrate = port->linkrate;  	dev->min_linkrate = port->linkrate;  	dev->max_linkrate = port->linkrate; @@ -147,11 +151,23 @@ static int sas_get_port_device(struct asd_sas_port *port)  	memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);  	memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);  	port->disc.max_level = 0; +	sas_device_set_phy(dev, port->port);  	dev->rphy = rphy; -	spin_lock_irq(&port->dev_list_lock); -	list_add_tail(&dev->dev_list_node, &port->dev_list); -	spin_unlock_irq(&port->dev_list_lock); +	get_device(&dev->rphy->dev); + +	if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEVICE) +		list_add_tail(&dev->disco_list_node, &port->disco_list); +	else { +		spin_lock_irq(&port->dev_list_lock); +		list_add_tail(&dev->dev_list_node, &port->dev_list); +		spin_unlock_irq(&port->dev_list_lock); +	} + +	spin_lock_irq(&port->phy_list_lock); +	list_for_each_entry(phy, &port->phy_list, port_phy_el) +		sas_phy_set_target(phy, dev); +	spin_unlock_irq(&port->phy_list_lock);  	return 0;  } @@ -165,15 +181,18 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)  	struct Scsi_Host *shost = sas_ha->core.shost;  	struct sas_internal *i = to_sas_internal(shost->transportt); -	if (i->dft->lldd_dev_found) { -		res = i->dft->lldd_dev_found(dev); -		if (res) { -			printk("sas: driver on pcidev %s cannot handle " -			       "device %llx, error:%d\n", -			       dev_name(sas_ha->dev), -			       SAS_ADDR(dev->sas_addr), res); -		} +	if (!i->dft->lldd_dev_found) +		return 0; + +	res = i->dft->lldd_dev_found(dev); +	if (res) { +		printk("sas: driver on pcidev %s cannot handle " +		       "device %llx, error:%d\n", +		       dev_name(sas_ha->dev), +		       SAS_ADDR(dev->sas_addr), res);  	} +	set_bit(SAS_DEV_FOUND, &dev->state); +	kref_get(&dev->kref);  	return res;  } @@ -184,12 +203,83 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)  	struct Scsi_Host *shost = sas_ha->core.shost;  	struct sas_internal *i = to_sas_internal(shost->transportt); -	if (i->dft->lldd_dev_gone) +	if (!i->dft->lldd_dev_gone) +		return; + +	if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) {  		i->dft->lldd_dev_gone(dev); +		sas_put_device(dev); +	} +} + +static void sas_probe_devices(struct work_struct *work) +{ +	struct domain_device *dev, *n; +	struct sas_discovery_event *ev = to_sas_discovery_event(work); +	struct asd_sas_port *port = ev->port; + +	clear_bit(DISCE_PROBE, &port->disc.pending); + +	/* devices must be domain members before link recovery and probe */ +	list_for_each_entry(dev, &port->disco_list, disco_list_node) { +		spin_lock_irq(&port->dev_list_lock); +		list_add_tail(&dev->dev_list_node, &port->dev_list); +		spin_unlock_irq(&port->dev_list_lock); +	} + +	sas_probe_sata(port); + +	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { +		int err; + +		err = sas_rphy_add(dev->rphy); +		if (err) +			sas_fail_probe(dev, __func__, err); +		else +			list_del_init(&dev->disco_list_node); +	}  } -/* ---------- Common/dispatchers ---------- */ +static void sas_suspend_devices(struct work_struct *work) +{ +	struct asd_sas_phy *phy; +	struct domain_device *dev; +	struct sas_discovery_event *ev = to_sas_discovery_event(work); +	struct asd_sas_port *port = ev->port; +	struct Scsi_Host *shost = port->ha->core.shost; +	struct sas_internal *si = to_sas_internal(shost->transportt); + +	clear_bit(DISCE_SUSPEND, &port->disc.pending); + +	sas_suspend_sata(port); + +	/* lldd is free to forget the domain_device across the +	 * suspension, we force the issue here to keep the reference +	 * counts aligned +	 */ +	list_for_each_entry(dev, &port->dev_list, dev_list_node) +		sas_notify_lldd_dev_gone(dev); + +	/* we are suspending, so we know events are disabled and +	 * phy_list is not being mutated +	 */ +	list_for_each_entry(phy, &port->phy_list, port_phy_el) { +		if (si->dft->lldd_port_formed) +			si->dft->lldd_port_deformed(phy); +		phy->suspended = 1; +		port->suspended = 1; +	} +} + +static void sas_resume_devices(struct work_struct *work) +{ +	struct sas_discovery_event *ev = to_sas_discovery_event(work); +	struct asd_sas_port *port = ev->port; +	clear_bit(DISCE_RESUME, &port->disc.pending); + +	sas_resume_sata(port); +}  /**   * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -203,58 +293,138 @@ int sas_discover_end_dev(struct domain_device *dev)  	res = sas_notify_lldd_dev_found(dev);  	if (res) -		goto out_err2; - -	res = sas_rphy_add(dev->rphy); -	if (res) -		goto out_err; +		return res; +	sas_discover_event(dev->port, DISCE_PROBE);  	return 0; - -out_err: -	sas_notify_lldd_dev_gone(dev); -out_err2: -	return res;  }  /* ---------- Device registration and unregistration ---------- */ -static inline void sas_unregister_common_dev(struct domain_device *dev) +void sas_free_device(struct kref *kref) +{ +	struct domain_device *dev = container_of(kref, typeof(*dev), kref); + +	put_device(&dev->rphy->dev); +	dev->rphy = NULL; + +	if (dev->parent) +		sas_put_device(dev->parent); + +	sas_port_put_phy(dev->phy); +	dev->phy = NULL; + +	/* remove the phys and ports, everything else should be gone */ +	if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) +		kfree(dev->ex_dev.ex_phy); + +	if (dev_is_sata(dev) && dev->sata_dev.ap) { +		ata_sas_port_destroy(dev->sata_dev.ap); +		dev->sata_dev.ap = NULL; +	} + +	kfree(dev); +} + +static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev)  { +	struct sas_ha_struct *ha = port->ha; +  	sas_notify_lldd_dev_gone(dev);  	if (!dev->parent)  		dev->port->port_dev = NULL;  	else  		list_del_init(&dev->siblings); + +	spin_lock_irq(&port->dev_list_lock);  	list_del_init(&dev->dev_list_node); +	if (dev_is_sata(dev)) +		sas_ata_end_eh(dev->sata_dev.ap); +	spin_unlock_irq(&port->dev_list_lock); + +	spin_lock_irq(&ha->lock); +	if (dev->dev_type == SAS_END_DEVICE && +	    !list_empty(&dev->ssp_dev.eh_list_node)) { +		list_del_init(&dev->ssp_dev.eh_list_node); +		ha->eh_active--; +	} +	spin_unlock_irq(&ha->lock); + +	sas_put_device(dev);  } -void sas_unregister_dev(struct domain_device *dev) +static void sas_destruct_devices(struct work_struct *work)  { -	if (dev->rphy) { +	struct domain_device *dev, *n; +	struct sas_discovery_event *ev = to_sas_discovery_event(work); +	struct asd_sas_port *port = ev->port; + +	clear_bit(DISCE_DESTRUCT, &port->disc.pending); + +	list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { +		list_del_init(&dev->disco_list_node); +  		sas_remove_children(&dev->rphy->dev);  		sas_rphy_delete(dev->rphy); -		dev->rphy = NULL; +		sas_unregister_common_dev(port, dev);  	} -	if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { -		/* remove the phys and ports, everything else should be gone */ -		kfree(dev->ex_dev.ex_phy); -		dev->ex_dev.ex_phy = NULL; +} + +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) +{ +	if (!test_bit(SAS_DEV_DESTROY, &dev->state) && +	    !list_empty(&dev->disco_list_node)) { +		/* this rphy never saw sas_rphy_add */ +		list_del_init(&dev->disco_list_node); +		sas_rphy_free(dev->rphy); +		sas_unregister_common_dev(port, dev); +		return; +	} + +	if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { +		sas_rphy_unlink(dev->rphy); +		list_move_tail(&dev->disco_list_node, &port->destroy_list); +		sas_discover_event(dev->port, DISCE_DESTRUCT);  	} -	sas_unregister_common_dev(dev);  } -void sas_unregister_domain_devices(struct asd_sas_port *port) +void sas_unregister_domain_devices(struct asd_sas_port *port, int gone)  {  	struct domain_device *dev, *n; -	list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node) -		sas_unregister_dev(dev); +	list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) { +		if (gone) +			set_bit(SAS_DEV_GONE, &dev->state); +		sas_unregister_dev(port, dev); +	} + +	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) +		sas_unregister_dev(port, dev);  	port->port->rphy = NULL;  } +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port) +{ +	struct sas_ha_struct *ha; +	struct sas_phy *new_phy; + +	if (!dev) +		return; + +	ha = dev->port->ha; +	new_phy = sas_port_get_phy(port); + +	/* pin and record last seen phy */ +	spin_lock_irq(&ha->phy_port_lock); +	if (new_phy) { +		sas_port_put_phy(dev->phy); +		dev->phy = new_phy; +	} +	spin_unlock_irq(&ha->phy_port_lock); +} +  /* ---------- Discovery and Revalidation ---------- */  /** @@ -270,12 +440,10 @@ static void sas_discover_domain(struct work_struct *work)  {  	struct domain_device *dev;  	int error = 0; -	struct sas_discovery_event *ev = -		container_of(work, struct sas_discovery_event, work); +	struct sas_discovery_event *ev = to_sas_discovery_event(work);  	struct asd_sas_port *port = ev->port; -	sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock, -			&port->disc.pending); +	clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);  	if (port->port_dev)  		return; @@ -289,15 +457,15 @@ static void sas_discover_domain(struct work_struct *work)  		    task_pid_nr(current));  	switch (dev->dev_type) { -	case SAS_END_DEV: +	case SAS_END_DEVICE:  		error = sas_discover_end_dev(dev);  		break; -	case EDGE_DEV: -	case FANOUT_DEV: +	case SAS_EDGE_EXPANDER_DEVICE: +	case SAS_FANOUT_EXPANDER_DEVICE:  		error = sas_discover_root_expander(dev);  		break; -	case SATA_DEV: -	case SATA_PM: +	case SAS_SATA_DEV: +	case SAS_SATA_PM:  #ifdef CONFIG_SCSI_SAS_ATA  		error = sas_discover_sata(dev);  		break; @@ -313,13 +481,12 @@ static void sas_discover_domain(struct work_struct *work)  	if (error) {  		sas_rphy_free(dev->rphy); -		dev->rphy = NULL; - +		list_del_init(&dev->disco_list_node);  		spin_lock_irq(&port->dev_list_lock);  		list_del_init(&dev->dev_list_node);  		spin_unlock_irq(&port->dev_list_lock); -		kfree(dev); /* not kobject_register-ed yet */ +		sas_put_device(dev);  		port->port_dev = NULL;  	} @@ -330,24 +497,57 @@ static void sas_discover_domain(struct work_struct *work)  static void sas_revalidate_domain(struct work_struct *work)  {  	int res = 0; -	struct sas_discovery_event *ev = -		container_of(work, struct sas_discovery_event, work); +	struct sas_discovery_event *ev = to_sas_discovery_event(work);  	struct asd_sas_port *port = ev->port; +	struct sas_ha_struct *ha = port->ha; + +	/* prevent revalidation from finding sata links in recovery */ +	mutex_lock(&ha->disco_mutex); +	if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { +		SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n", +			    port->id, task_pid_nr(current)); +		goto out; +	} -	sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock, -			&port->disc.pending); +	clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending);  	SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,  		    task_pid_nr(current)); +  	if (port->port_dev)  		res = sas_ex_revalidate_domain(port->port_dev);  	SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",  		    port->id, task_pid_nr(current), res); + out: +	mutex_unlock(&ha->disco_mutex);  }  /* ---------- Events ---------- */ +static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) +{ +	/* chained work is not subject to SA_HA_DRAINING or +	 * SAS_HA_REGISTERED, because it is either submitted in the +	 * workqueue, or known to be submitted from a context that is +	 * not racing against draining +	 */ +	scsi_queue_work(ha->core.shost, &sw->work); +} + +static void sas_chain_event(int event, unsigned long *pending, +			    struct sas_work *sw, +			    struct sas_ha_struct *ha) +{ +	if (!test_and_set_bit(event, pending)) { +		unsigned long flags; + +		spin_lock_irqsave(&ha->lock, flags); +		sas_chain_work(ha, sw); +		spin_unlock_irqrestore(&ha->lock, flags); +	} +} +  int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)  {  	struct sas_discovery *disc; @@ -358,8 +558,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)  	BUG_ON(ev >= DISC_NUM_EVENTS); -	sas_queue_event(ev, &disc->disc_event_lock, &disc->pending, -			&disc->disc_work[ev].work, port->ha); +	sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha);  	return 0;  } @@ -377,12 +576,15 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)  	static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {  		[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,  		[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, +		[DISCE_PROBE] = sas_probe_devices, +		[DISCE_SUSPEND] = sas_suspend_devices, +		[DISCE_RESUME] = sas_resume_devices, +		[DISCE_DESTRUCT] = sas_destruct_devices,  	}; -	spin_lock_init(&disc->disc_event_lock);  	disc->pending = 0;  	for (i = 0; i < DISC_NUM_EVENTS; i++) { -		INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); +		INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);  		disc->disc_work[i].port = port;  	}  } diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index c17c25030f1..cd6f99c1ae7 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -24,8 +24,6 @@  #include "sas_dump.h" -#ifdef SAS_DEBUG -  static const char *sas_hae_str[] = {  	[0] = "HAE_RESET",  }; @@ -43,6 +41,7 @@ static const char *sas_phye_str[] = {  	[1] = "PHYE_OOB_DONE",  	[2] = "PHYE_OOB_ERROR",  	[3] = "PHYE_SPINUP_HOLD", +	[4] = "PHYE_RESUME_TIMEOUT",  };  void sas_dprint_porte(int phyid, enum port_event pe) @@ -72,5 +71,3 @@ void sas_dump_port(struct asd_sas_port *port)  	SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode);  	SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys);  } - -#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h index 47b45d4f525..800e4c69093 100644 --- a/drivers/scsi/libsas/sas_dump.h +++ b/drivers/scsi/libsas/sas_dump.h @@ -24,19 +24,7 @@  #include "sas_internal.h" -#ifdef SAS_DEBUG -  void sas_dprint_porte(int phyid, enum port_event pe);  void sas_dprint_phye(int phyid, enum phy_event pe);  void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he);  void sas_dump_port(struct asd_sas_port *port); - -#else /* SAS_DEBUG */ - -static inline void sas_dprint_porte(int phyid, enum port_event pe) { } -static inline void sas_dprint_phye(int phyid, enum phy_event pe) { } -static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha, -				  enum ha_event he) { } -static inline void sas_dump_port(struct asd_sas_port *port) { } - -#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9db30fb5caf..aadbd5314c5 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -22,15 +22,105 @@   *   */ +#include <linux/export.h>  #include <scsi/scsi_host.h>  #include "sas_internal.h"  #include "sas_dump.h" +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) +{ +	if (!test_bit(SAS_HA_REGISTERED, &ha->state)) +		return; + +	if (test_bit(SAS_HA_DRAINING, &ha->state)) { +		/* add it to the defer list, if not already pending */ +		if (list_empty(&sw->drain_node)) +			list_add(&sw->drain_node, &ha->defer_q); +	} else +		scsi_queue_work(ha->core.shost, &sw->work); +} + +static void sas_queue_event(int event, unsigned long *pending, +			    struct sas_work *work, +			    struct sas_ha_struct *ha) +{ +	if (!test_and_set_bit(event, pending)) { +		unsigned long flags; + +		spin_lock_irqsave(&ha->lock, flags); +		sas_queue_work(ha, work); +		spin_unlock_irqrestore(&ha->lock, flags); +	} +} + + +void __sas_drain_work(struct sas_ha_struct *ha) +{ +	struct workqueue_struct *wq = ha->core.shost->work_q; +	struct sas_work *sw, *_sw; + +	set_bit(SAS_HA_DRAINING, &ha->state); +	/* flush submitters */ +	spin_lock_irq(&ha->lock); +	spin_unlock_irq(&ha->lock); + +	drain_workqueue(wq); + +	spin_lock_irq(&ha->lock); +	clear_bit(SAS_HA_DRAINING, &ha->state); +	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { +		list_del_init(&sw->drain_node); +		sas_queue_work(ha, sw); +	} +	spin_unlock_irq(&ha->lock); +} + +int sas_drain_work(struct sas_ha_struct *ha) +{ +	int err; + +	err = mutex_lock_interruptible(&ha->drain_mutex); +	if (err) +		return err; +	if (test_bit(SAS_HA_REGISTERED, &ha->state)) +		__sas_drain_work(ha); +	mutex_unlock(&ha->drain_mutex); + +	return 0; +} +EXPORT_SYMBOL_GPL(sas_drain_work); + +void sas_disable_revalidation(struct sas_ha_struct *ha) +{ +	mutex_lock(&ha->disco_mutex); +	set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); +	mutex_unlock(&ha->disco_mutex); +} + +void sas_enable_revalidation(struct sas_ha_struct *ha) +{ +	int i; + +	mutex_lock(&ha->disco_mutex); +	clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); +	for (i = 0; i < ha->num_phys; i++) { +		struct asd_sas_port *port = ha->sas_port[i]; +		const int ev = DISCE_REVALIDATE_DOMAIN; +		struct sas_discovery *d = &port->disc; + +		if (!test_and_clear_bit(ev, &d->pending)) +			continue; + +		sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); +	} +	mutex_unlock(&ha->disco_mutex); +} +  static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)  {  	BUG_ON(event >= HA_NUM_EVENTS); -	sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, +	sas_queue_event(event, &sas_ha->pending,  			&sas_ha->ha_events[event].work, sas_ha);  } @@ -40,17 +130,17 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)  	BUG_ON(event >= PORT_NUM_EVENTS); -	sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, +	sas_queue_event(event, &phy->port_events_pending,  			&phy->port_events[event].work, ha);  } -static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) +void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)  {  	struct sas_ha_struct *ha = phy->ha;  	BUG_ON(event >= PHY_NUM_EVENTS); -	sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, +	sas_queue_event(event, &phy->phy_events_pending,  			&phy->phy_events[event].work, ha);  } @@ -62,16 +152,14 @@ int sas_init_events(struct sas_ha_struct *sas_ha)  	int i; -	spin_lock_init(&sas_ha->event_lock); -  	for (i = 0; i < HA_NUM_EVENTS; i++) { -		INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); +		INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);  		sas_ha->ha_events[i].ha = sas_ha;  	}  	sas_ha->notify_ha_event = notify_ha_event;  	sas_ha->notify_port_event = notify_port_event; -	sas_ha->notify_phy_event = notify_phy_event; +	sas_ha->notify_phy_event = sas_notify_phy_event;  	return 0;  } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 505ffe35829..0cac7d8fd0f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -28,6 +28,7 @@  #include "sas_internal.h" +#include <scsi/sas_ata.h>  #include <scsi/scsi_transport.h>  #include <scsi/scsi_transport_sas.h>  #include "../scsi_sas_internal.h" @@ -50,14 +51,14 @@ static void smp_task_timedout(unsigned long _task)  		task->task_state_flags |= SAS_TASK_STATE_ABORTED;  	spin_unlock_irqrestore(&task->task_state_lock, flags); -	complete(&task->completion); +	complete(&task->slow_task->completion);  }  static void smp_task_done(struct sas_task *task)  { -	if (!del_timer(&task->timer)) +	if (!del_timer(&task->slow_task->timer))  		return; -	complete(&task->completion); +	complete(&task->slow_task->completion);  }  /* Give it some long enough timeout. In seconds. */ @@ -71,11 +72,18 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,  	struct sas_internal *i =  		to_sas_internal(dev->port->ha->core.shost->transportt); +	mutex_lock(&dev->ex_dev.cmd_mutex);  	for (retry = 0; retry < 3; retry++) { -		task = sas_alloc_task(GFP_KERNEL); -		if (!task) -			return -ENOMEM; +		if (test_bit(SAS_DEV_GONE, &dev->state)) { +			res = -ECOMM; +			break; +		} +		task = sas_alloc_slow_task(GFP_KERNEL); +		if (!task) { +			res = -ENOMEM; +			break; +		}  		task->dev = dev;  		task->task_proto = dev->tproto;  		sg_init_one(&task->smp_task.smp_req, req, req_size); @@ -83,44 +91,50 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,  		task->task_done = smp_task_done; -		task->timer.data = (unsigned long) task; -		task->timer.function = smp_task_timedout; -		task->timer.expires = jiffies + SMP_TIMEOUT*HZ; -		add_timer(&task->timer); +		task->slow_task->timer.data = (unsigned long) task; +		task->slow_task->timer.function = smp_task_timedout; +		task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; +		add_timer(&task->slow_task->timer);  		res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);  		if (res) { -			del_timer(&task->timer); +			del_timer(&task->slow_task->timer);  			SAS_DPRINTK("executing SMP task failed:%d\n", res); -			goto ex_err; +			break;  		} -		wait_for_completion(&task->completion); +		wait_for_completion(&task->slow_task->completion);  		res = -ECOMM;  		if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {  			SAS_DPRINTK("smp task timed out or aborted\n");  			i->dft->lldd_abort_task(task);  			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {  				SAS_DPRINTK("SMP task aborted and not done\n"); -				goto ex_err; +				break;  			}  		}  		if (task->task_status.resp == SAS_TASK_COMPLETE &&  		    task->task_status.stat == SAM_STAT_GOOD) {  			res = 0;  			break; -		} if (task->task_status.resp == SAS_TASK_COMPLETE && -		      task->task_status.stat == SAS_DATA_UNDERRUN) { +		} +		if (task->task_status.resp == SAS_TASK_COMPLETE && +		    task->task_status.stat == SAS_DATA_UNDERRUN) {  			/* no error, but return the number of bytes of  			 * underrun */  			res = task->task_status.residual;  			break; -		} if (task->task_status.resp == SAS_TASK_COMPLETE && -		      task->task_status.stat == SAS_DATA_OVERRUN) { +		} +		if (task->task_status.resp == SAS_TASK_COMPLETE && +		    task->task_status.stat == SAS_DATA_OVERRUN) {  			res = -EMSGSIZE;  			break; -		} else { +		} +		if (task->task_status.resp == SAS_TASK_UNDELIVERED && +		    task->task_status.stat == SAS_DEVICE_UNKNOWN) +			break; +		else {  			SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "  				    "status 0x%x\n", __func__,  				    SAS_ADDR(dev->sas_addr), @@ -130,11 +144,10 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,  			task = NULL;  		}  	} -ex_err: +	mutex_unlock(&dev->ex_dev.cmd_mutex); +  	BUG_ON(retry == 3 && task != NULL); -	if (task != NULL) { -		sas_free_task(task); -	} +	sas_free_task(task);  	return res;  } @@ -153,19 +166,52 @@ static inline void *alloc_smp_resp(int size)  	return kzalloc(size, GFP_KERNEL);  } -/* ---------- Expander configuration ---------- */ +static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) +{ +	switch (phy->routing_attr) { +	case TABLE_ROUTING: +		if (dev->ex_dev.t2t_supp) +			return 'U'; +		else +			return 'T'; +	case DIRECT_ROUTING: +		return 'D'; +	case SUBTRACTIVE_ROUTING: +		return 'S'; +	default: +		return '?'; +	} +} + +static enum sas_device_type to_dev_type(struct discover_resp *dr) +{ +	/* This is detecting a failure to transmit initial dev to host +	 * FIS as described in section J.5 of sas-2 r16 +	 */ +	if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev && +	    dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) +		return SAS_SATA_PENDING; +	else +		return dr->attached_dev_type; +} -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, -			   void *disc_resp) +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)  { +	enum sas_device_type dev_type; +	enum sas_linkrate linkrate; +	u8 sas_addr[SAS_ADDR_SIZE]; +	struct smp_resp *resp = rsp; +	struct discover_resp *dr = &resp->disc; +	struct sas_ha_struct *ha = dev->port->ha;  	struct expander_device *ex = &dev->ex_dev;  	struct ex_phy *phy = &ex->ex_phy[phy_id]; -	struct smp_resp *resp = disc_resp; -	struct discover_resp *dr = &resp->disc;  	struct sas_rphy *rphy = dev->rphy; -	int rediscover = (phy->phy != NULL); +	bool new_phy = !phy->phy; +	char *type; -	if (!rediscover) { +	if (new_phy) { +		if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) +			return;  		phy->phy = sas_phy_alloc(&rphy->dev, phy_id);  		/* FIXME: error_handling */ @@ -184,23 +230,52 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,  		break;  	} +	/* check if anything important changed to squelch debug */ +	dev_type = phy->attached_dev_type; +	linkrate  = phy->linkrate; +	memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + +	/* Handle vacant phy - rest of dr data is not valid so skip it */ +	if (phy->phy_state == PHY_VACANT) { +		memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); +		phy->attached_dev_type = SAS_PHY_UNUSED; +		if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { +			phy->phy_id = phy_id; +			goto skip; +		} else +			goto out; +	} + +	phy->attached_dev_type = to_dev_type(dr); +	if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) +		goto out;  	phy->phy_id = phy_id; -	phy->attached_dev_type = dr->attached_dev_type;  	phy->linkrate = dr->linkrate;  	phy->attached_sata_host = dr->attached_sata_host;  	phy->attached_sata_dev  = dr->attached_sata_dev;  	phy->attached_sata_ps   = dr->attached_sata_ps;  	phy->attached_iproto = dr->iproto << 1;  	phy->attached_tproto = dr->tproto << 1; -	memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); +	/* help some expanders that fail to zero sas_address in the 'no +	 * device' case +	 */ +	if (phy->attached_dev_type == SAS_PHY_UNUSED || +	    phy->linkrate < SAS_LINK_RATE_1_5_GBPS) +		memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); +	else +		memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);  	phy->attached_phy_id = dr->attached_phy_id;  	phy->phy_change_count = dr->change_count;  	phy->routing_attr = dr->routing_attr;  	phy->virtual = dr->virtual;  	phy->last_da_index = -1; +	phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); +	phy->phy->identify.device_type = dr->attached_dev_type;  	phy->phy->identify.initiator_port_protocols = phy->attached_iproto;  	phy->phy->identify.target_port_protocols = phy->attached_tproto; +	if (!phy->attached_tproto && dr->attached_sata_dev) +		phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;  	phy->phy->identify.phy_identifier = phy_id;  	phy->phy->minimum_linkrate_hw = dr->hmin_linkrate;  	phy->phy->maximum_linkrate_hw = dr->hmax_linkrate; @@ -208,20 +283,86 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,  	phy->phy->maximum_linkrate = dr->pmax_linkrate;  	phy->phy->negotiated_linkrate = phy->linkrate; -	if (!rediscover) + skip: +	if (new_phy)  		if (sas_phy_add(phy->phy)) {  			sas_phy_free(phy->phy);  			return;  		} -	SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n", + out: +	switch (phy->attached_dev_type) { +	case SAS_SATA_PENDING: +		type = "stp pending"; +		break; +	case SAS_PHY_UNUSED: +		type = "no device"; +		break; +	case SAS_END_DEVICE: +		if (phy->attached_iproto) { +			if (phy->attached_tproto) +				type = "host+target"; +			else +				type = "host"; +		} else { +			if (dr->attached_sata_dev) +				type = "stp"; +			else +				type = "ssp"; +		} +		break; +	case SAS_EDGE_EXPANDER_DEVICE: +	case SAS_FANOUT_EXPANDER_DEVICE: +		type = "smp"; +		break; +	default: +		type = "unknown"; +	} + +	/* this routine is polled by libata error recovery so filter +	 * unimportant messages +	 */ +	if (new_phy || phy->attached_dev_type != dev_type || +	    phy->linkrate != linkrate || +	    SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) +		/* pass */; +	else +		return; + +	/* if the attached device type changed and ata_eh is active, +	 * make sure we run revalidation when eh completes (see: +	 * sas_enable_revalidation) +	 */ +	if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) +		set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); + +	SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", +		    test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "",  		    SAS_ADDR(dev->sas_addr), phy->phy_id, -		    phy->routing_attr == TABLE_ROUTING ? 'T' : -		    phy->routing_attr == DIRECT_ROUTING ? 'D' : -		    phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?', -		    SAS_ADDR(phy->attached_sas_addr)); +		    sas_route_char(dev, phy), phy->linkrate, +		    SAS_ADDR(phy->attached_sas_addr), type); +} + +/* check if we have an existing attached ata device on this expander phy */ +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +{ +	struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; +	struct domain_device *dev; +	struct sas_rphy *rphy; + +	if (!ex_phy->port) +		return NULL; + +	rphy = ex_phy->port->rphy; +	if (!rphy) +		return NULL; + +	dev = sas_find_dev_by_rphy(rphy); + +	if (dev && dev_is_sata(dev)) +		return dev; -	return; +	return NULL;  }  #define DISCOVER_REQ_SIZE  16 @@ -230,34 +371,25 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,  static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,  				      u8 *disc_resp, int single)  { -	int i, res; +	struct discover_resp *dr; +	int res;  	disc_req[9] = single; -	for (i = 1 ; i < 3; i++) { -		struct discover_resp *dr; -		res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, -				       disc_resp, DISCOVER_RESP_SIZE); -		if (res) -			return res; -		/* This is detecting a failure to transmit inital -		 * dev to host FIS as described in section G.5 of -		 * sas-2 r 04b */ -		dr = &((struct smp_resp *)disc_resp)->disc; -		if (!(dr->attached_dev_type == 0 && -		      dr->attached_sata_dev)) -			break; -		/* In order to generate the dev to host FIS, we -		 * send a link reset to the expander port */ -		sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); -		/* Wait for the reset to trigger the negotiation */ -		msleep(500); +	res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, +			       disc_resp, DISCOVER_RESP_SIZE); +	if (res) +		return res; +	dr = &((struct smp_resp *)disc_resp)->disc; +	if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { +		sas_printk("Found loopback topology, just ignore it!\n"); +		return 0;  	}  	sas_set_ex_phy(dev, single, disc_resp);  	return 0;  } -static int sas_ex_phy_discover(struct domain_device *dev, int single) +int sas_ex_phy_discover(struct domain_device *dev, int single)  {  	struct expander_device *ex = &dev->ex_dev;  	int  res = 0; @@ -268,7 +400,7 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single)  	if (!disc_req)  		return -ENOMEM; -	disc_resp = alloc_smp_req(DISCOVER_RESP_SIZE); +	disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);  	if (!disc_resp) {  		kfree(disc_req);  		return -ENOMEM; @@ -324,6 +456,7 @@ static void ex_assign_report_general(struct domain_device *dev,  	dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count);  	dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes);  	dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); +	dev->ex_dev.t2t_supp = rg->t2t_supp;  	dev->ex_dev.conf_route_table = rg->conf_route_table;  	dev->ex_dev.configuring = rg->configuring;  	memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); @@ -561,9 +694,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy)  #define RPS_REQ_SIZE  16  #define RPS_RESP_SIZE 60 -static int sas_get_report_phy_sata(struct domain_device *dev, -					  int phy_id, -					  struct smp_resp *rps_resp) +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, +			    struct smp_resp *rps_resp)  {  	int res;  	u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); @@ -649,10 +781,11 @@ static struct domain_device *sas_ex_discover_end_dev(  	if (phy->attached_sata_host || phy->attached_sata_ps)  		return NULL; -	child = kzalloc(sizeof(*child), GFP_KERNEL); +	child = sas_alloc_device();  	if (!child)  		return NULL; +	kref_get(&parent->kref);  	child->parent = parent;  	child->port   = parent->port;  	child->iproto = phy->attached_iproto; @@ -668,36 +801,26 @@ static struct domain_device *sas_ex_discover_end_dev(  		}  	}  	sas_ex_get_linkrate(parent, child, phy); +	sas_device_set_phy(child, phy->port);  #ifdef CONFIG_SCSI_SAS_ATA  	if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { -		child->dev_type = SATA_DEV; -		if (phy->attached_tproto & SAS_PROTOCOL_STP) -			child->tproto = phy->attached_tproto; -		if (phy->attached_sata_dev) -			child->tproto |= SATA_DEV; -		res = sas_get_report_phy_sata(parent, phy_id, -					      &child->sata_dev.rps_resp); -		if (res) { -			SAS_DPRINTK("report phy sata to %016llx:0x%x returned " -				    "0x%x\n", SAS_ADDR(parent->sas_addr), -				    phy_id, res); +		res = sas_get_ata_info(child, phy); +		if (res)  			goto out_free; -		} -		memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, -		       sizeof(struct dev_to_host_fis)); +		sas_init_dev(child); +		res = sas_ata_init(child); +		if (res) +			goto out_free;  		rphy = sas_end_device_alloc(phy->port); -		if (unlikely(!rphy)) +		if (!rphy)  			goto out_free; -		sas_init_dev(child); -  		child->rphy = rphy; +		get_device(&rphy->dev); -		spin_lock_irq(&parent->port->dev_list_lock); -		list_add_tail(&child->dev_list_node, &parent->port->dev_list); -		spin_unlock_irq(&parent->port->dev_list_lock); +		list_add_tail(&child->disco_list_node, &parent->port->disco_list);  		res = sas_discover_sata(child);  		if (res) { @@ -710,7 +833,7 @@ static struct domain_device *sas_ex_discover_end_dev(  	} else  #endif  	  if (phy->attached_tproto & SAS_PROTOCOL_SSP) { -		child->dev_type = SAS_END_DEV; +		child->dev_type = SAS_END_DEVICE;  		rphy = sas_end_device_alloc(phy->port);  		/* FIXME: error handling */  		if (unlikely(!rphy)) @@ -719,11 +842,10 @@ static struct domain_device *sas_ex_discover_end_dev(  		sas_init_dev(child);  		child->rphy = rphy; +		get_device(&rphy->dev);  		sas_fill_in_rphy(child, rphy); -		spin_lock_irq(&parent->port->dev_list_lock); -		list_add_tail(&child->dev_list_node, &parent->port->dev_list); -		spin_unlock_irq(&parent->port->dev_list_lock); +		list_add_tail(&child->disco_list_node, &parent->port->disco_list);  		res = sas_discover_end_dev(child);  		if (res) { @@ -745,18 +867,20 @@ static struct domain_device *sas_ex_discover_end_dev(   out_list_del:  	sas_rphy_free(child->rphy); -	child->rphy = NULL; +	list_del(&child->disco_list_node); +	spin_lock_irq(&parent->port->dev_list_lock);  	list_del(&child->dev_list_node); +	spin_unlock_irq(&parent->port->dev_list_lock);   out_free:  	sas_port_delete(phy->port);   out_err:  	phy->port = NULL; -	kfree(child); +	sas_put_device(child);  	return NULL;  }  /* See if this phy is part of a wide port */ -static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id) +static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id)  {  	struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id];  	int i; @@ -772,11 +896,11 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)  			sas_port_add_phy(ephy->port, phy->phy);  			phy->port = ephy->port;  			phy->phy_state = PHY_DEVICE_DISCOVERED; -			return 0; +			return true;  		}  	} -	return -ENODEV; +	return false;  }  static struct domain_device *sas_ex_discover_expander( @@ -798,7 +922,7 @@ static struct domain_device *sas_ex_discover_expander(  			    phy->attached_phy_id);  		return NULL;  	} -	child = kzalloc(sizeof(*child), GFP_KERNEL); +	child = sas_alloc_device();  	if (!child)  		return NULL; @@ -808,11 +932,11 @@ static struct domain_device *sas_ex_discover_expander(  	switch (phy->attached_dev_type) { -	case EDGE_DEV: +	case SAS_EDGE_EXPANDER_DEVICE:  		rphy = sas_expander_alloc(phy->port,  					  SAS_EDGE_EXPANDER_DEVICE);  		break; -	case FANOUT_DEV: +	case SAS_FANOUT_EXPANDER_DEVICE:  		rphy = sas_expander_alloc(phy->port,  					  SAS_FANOUT_EXPANDER_DEVICE);  		break; @@ -822,8 +946,10 @@ static struct domain_device *sas_ex_discover_expander(  	}  	port = parent->port;  	child->rphy = rphy; +	get_device(&rphy->dev);  	edev = rphy_to_expander_device(rphy);  	child->dev_type = phy->attached_dev_type; +	kref_get(&parent->kref);  	child->parent = parent;  	child->port = port;  	child->iproto = phy->attached_iproto; @@ -844,7 +970,11 @@ static struct domain_device *sas_ex_discover_expander(  	res = sas_discover_expander(child);  	if (res) { -		kfree(child); +		sas_rphy_delete(rphy); +		spin_lock_irq(&parent->port->dev_list_lock); +		list_del(&child->dev_list_node); +		spin_unlock_irq(&parent->port->dev_list_lock); +		sas_put_device(child);  		return NULL;  	}  	list_add_tail(&child->siblings, &parent->ex_dev.children); @@ -883,7 +1013,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)  	if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr))  		sas_ex_disable_port(dev, ex_phy->attached_sas_addr); -	if (ex_phy->attached_dev_type == NO_DEVICE) { +	if (ex_phy->attached_dev_type == SAS_PHY_UNUSED) {  		if (ex_phy->routing_attr == DIRECT_ROUTING) {  			memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE);  			sas_configure_routing(dev, ex_phy->attached_sas_addr); @@ -892,9 +1022,10 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)  	} else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN)  		return 0; -	if (ex_phy->attached_dev_type != SAS_END_DEV && -	    ex_phy->attached_dev_type != FANOUT_DEV && -	    ex_phy->attached_dev_type != EDGE_DEV) { +	if (ex_phy->attached_dev_type != SAS_END_DEVICE && +	    ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE && +	    ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE && +	    ex_phy->attached_dev_type != SAS_SATA_PENDING) {  		SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx "  			    "phy 0x%x\n", ex_phy->attached_dev_type,  			    SAS_ADDR(dev->sas_addr), @@ -911,18 +1042,18 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)  		return res;  	} -	res = sas_ex_join_wide_port(dev, phy_id); -	if (!res) { +	if (sas_ex_join_wide_port(dev, phy_id)) {  		SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",  			    phy_id, SAS_ADDR(ex_phy->attached_sas_addr));  		return res;  	}  	switch (ex_phy->attached_dev_type) { -	case SAS_END_DEV: +	case SAS_END_DEVICE: +	case SAS_SATA_PENDING:  		child = sas_ex_discover_end_dev(dev, phy_id);  		break; -	case FANOUT_DEV: +	case SAS_FANOUT_EXPANDER_DEVICE:  		if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) {  			SAS_DPRINTK("second fanout expander %016llx phy 0x%x "  				    "attached to ex %016llx phy 0x%x\n", @@ -936,7 +1067,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)  			memcpy(dev->port->disc.fanout_sas_addr,  			       ex_phy->attached_sas_addr, SAS_ADDR_SIZE);  		/* fallthrough */ -	case EDGE_DEV: +	case SAS_EDGE_EXPANDER_DEVICE:  		child = sas_ex_discover_expander(dev, phy_id);  		break;  	default: @@ -957,8 +1088,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)  			if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==  			    SAS_ADDR(child->sas_addr)) {  				ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED; -				res = sas_ex_join_wide_port(dev, i); -				if (!res) +				if (sas_ex_join_wide_port(dev, i))  					SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",  						    i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr)); @@ -981,8 +1111,8 @@ static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr)  		    phy->phy_state == PHY_NOT_PRESENT)  			continue; -		if ((phy->attached_dev_type == EDGE_DEV || -		     phy->attached_dev_type == FANOUT_DEV) && +		if ((phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE || +		     phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE) &&  		    phy->routing_attr == SUBTRACTIVE_ROUTING) {  			memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE); @@ -1000,8 +1130,8 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)  	u8 sub_addr[8] = {0, };  	list_for_each_entry(child, &ex->children, siblings) { -		if (child->dev_type != EDGE_DEV && -		    child->dev_type != FANOUT_DEV) +		if (child->dev_type != SAS_EDGE_EXPANDER_DEVICE && +		    child->dev_type != SAS_FANOUT_EXPANDER_DEVICE)  			continue;  		if (sub_addr[0] == 0) {  			sas_find_sub_addr(child, sub_addr); @@ -1078,7 +1208,7 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev)  	int i;  	u8  *sub_sas_addr = NULL; -	if (dev->dev_type != EDGE_DEV) +	if (dev->dev_type != SAS_EDGE_EXPANDER_DEVICE)  		return 0;  	for (i = 0; i < ex->num_phys; i++) { @@ -1088,8 +1218,8 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev)  		    phy->phy_state == PHY_NOT_PRESENT)  			continue; -		if ((phy->attached_dev_type == FANOUT_DEV || -		     phy->attached_dev_type == EDGE_DEV) && +		if ((phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE || +		     phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE) &&  		    phy->routing_attr == SUBTRACTIVE_ROUTING) {  			if (!sub_sas_addr) @@ -1114,19 +1244,14 @@ static void sas_print_parent_topology_bug(struct domain_device *child,  						 struct ex_phy *parent_phy,  						 struct ex_phy *child_phy)  { -	static const char ra_char[] = { -		[DIRECT_ROUTING] = 'D', -		[SUBTRACTIVE_ROUTING] = 'S', -		[TABLE_ROUTING] = 'T', -	};  	static const char *ex_type[] = { -		[EDGE_DEV] = "edge", -		[FANOUT_DEV] = "fanout", +		[SAS_EDGE_EXPANDER_DEVICE] = "edge", +		[SAS_FANOUT_EXPANDER_DEVICE] = "fanout",  	};  	struct domain_device *parent = child->parent; -	sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x " -		   "has %c:%c routing link!\n", +	sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx " +		   "phy 0x%x has %c:%c routing link!\n",  		   ex_type[parent->dev_type],  		   SAS_ADDR(parent->sas_addr), @@ -1136,8 +1261,8 @@ static void sas_print_parent_topology_bug(struct domain_device *child,  		   SAS_ADDR(child->sas_addr),  		   child_phy->phy_id, -		   ra_char[parent_phy->routing_attr], -		   ra_char[child_phy->routing_attr]); +		   sas_route_char(parent, parent_phy), +		   sas_route_char(child, child_phy));  }  static int sas_check_eeds(struct domain_device *child, @@ -1196,8 +1321,8 @@ static int sas_check_parent_topology(struct domain_device *child)  	if (!child->parent)  		return 0; -	if (child->parent->dev_type != EDGE_DEV && -	    child->parent->dev_type != FANOUT_DEV) +	if (child->parent->dev_type != SAS_EDGE_EXPANDER_DEVICE && +	    child->parent->dev_type != SAS_FANOUT_EXPANDER_DEVICE)  		return 0;  	parent_ex = &child->parent->ex_dev; @@ -1216,8 +1341,8 @@ static int sas_check_parent_topology(struct domain_device *child)  		child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id];  		switch (child->parent->dev_type) { -		case EDGE_DEV: -			if (child->dev_type == FANOUT_DEV) { +		case SAS_EDGE_EXPANDER_DEVICE: +			if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {  				if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING ||  				    child_phy->routing_attr != TABLE_ROUTING) {  					sas_print_parent_topology_bug(child, parent_phy, child_phy); @@ -1230,13 +1355,18 @@ static int sas_check_parent_topology(struct domain_device *child)  					sas_print_parent_topology_bug(child, parent_phy, child_phy);  					res = -ENODEV;  				} -			} else if (parent_phy->routing_attr == TABLE_ROUTING && -				   child_phy->routing_attr != SUBTRACTIVE_ROUTING) { -				sas_print_parent_topology_bug(child, parent_phy, child_phy); -				res = -ENODEV; +			} else if (parent_phy->routing_attr == TABLE_ROUTING) { +				if (child_phy->routing_attr == SUBTRACTIVE_ROUTING || +				    (child_phy->routing_attr == TABLE_ROUTING && +				     child_ex->t2t_supp && parent_ex->t2t_supp)) { +					/* All good */; +				} else { +					sas_print_parent_topology_bug(child, parent_phy, child_phy); +					res = -ENODEV; +				}  			}  			break; -		case FANOUT_DEV: +		case SAS_FANOUT_EXPANDER_DEVICE:  			if (parent_phy->routing_attr != TABLE_ROUTING ||  			    child_phy->routing_attr != SUBTRACTIVE_ROUTING) {  				sas_print_parent_topology_bug(child, parent_phy, child_phy); @@ -1489,8 +1619,8 @@ static int sas_ex_level_discovery(struct asd_sas_port *port, const int level)  	struct domain_device *dev;  	list_for_each_entry(dev, &port->dev_list, dev_list_node) { -		if (dev->dev_type == EDGE_DEV || -		    dev->dev_type == FANOUT_DEV) { +		if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || +		    dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {  			struct sas_expander_device *ex =  				rphy_to_expander_device(dev->rphy); @@ -1589,8 +1719,8 @@ static int sas_get_phy_change_count(struct domain_device *dev,  	return res;  } -static int sas_get_phy_attached_sas_addr(struct domain_device *dev, -					 int phy_id, u8 *attached_sas_addr) +static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, +				    u8 *sas_addr, enum sas_device_type *type)  {  	int res;  	struct smp_resp *disc_resp; @@ -1602,10 +1732,11 @@ static int sas_get_phy_attached_sas_addr(struct domain_device *dev,  	dr = &disc_resp->disc;  	res = sas_get_phy_discover(dev, phy_id, disc_resp); -	if (!res) { -		memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8); -		if (dr->attached_dev_type == 0) -			memset(attached_sas_addr, 0, 8); +	if (res == 0) { +		memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8); +		*type = to_dev_type(dr); +		if (*type == 0) +			memset(sas_addr, 0, 8);  	}  	kfree(disc_resp);  	return res; @@ -1622,9 +1753,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,  		int phy_change_count = 0;  		res = sas_get_phy_change_count(dev, i, &phy_change_count); -		if (res) -			goto out; -		else if (phy_change_count != ex->ex_phy[i].phy_change_count) { +		switch (res) { +		case SMP_RESP_PHY_VACANT: +		case SMP_RESP_NO_PHY: +			continue; +		case SMP_RESP_FUNC_ACC: +			break; +		default: +			return res; +		} + +		if (phy_change_count != ex->ex_phy[i].phy_change_count) {  			if (update)  				ex->ex_phy[i].phy_change_count =  					phy_change_count; @@ -1632,8 +1771,7 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,  			return 0;  		}  	} -out: -	return res; +	return 0;  }  static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) @@ -1674,7 +1812,7 @@ out:   * @dev:domain device to be detect.   * @src_dev: the device which originated BROADCAST(CHANGE).   * - * Add self-configuration expander suport. Suppose two expander cascading, + * Add self-configuration expander support. Suppose two expander cascading,   * when the first level expander is self-configuring, hotplug the disks in   * second level expander, BROADCAST(CHANGE) will not only be originated   * in the second level expander, but also be originated in the first level @@ -1711,9 +1849,9 @@ static int sas_find_bcast_dev(struct domain_device *dev,  			SAS_DPRINTK("Expander phys DID NOT change\n");  	}  	list_for_each_entry(ch, &ex->children, siblings) { -		if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) { +		if (ch->dev_type == SAS_EDGE_EXPANDER_DEVICE || ch->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {  			res = sas_find_bcast_dev(ch, src_dev); -			if (src_dev) +			if (*src_dev)  				return res;  		}  	} @@ -1721,20 +1859,20 @@ out:  	return res;  } -static void sas_unregister_ex_tree(struct domain_device *dev) +static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev)  {  	struct expander_device *ex = &dev->ex_dev;  	struct domain_device *child, *n;  	list_for_each_entry_safe(child, n, &ex->children, siblings) { -		child->gone = 1; -		if (child->dev_type == EDGE_DEV || -		    child->dev_type == FANOUT_DEV) -			sas_unregister_ex_tree(child); +		set_bit(SAS_DEV_GONE, &child->state); +		if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || +		    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) +			sas_unregister_ex_tree(port, child);  		else -			sas_unregister_dev(child); +			sas_unregister_dev(port, child);  	} -	sas_unregister_dev(dev); +	sas_unregister_dev(port, dev);  }  static void sas_unregister_devs_sas_addr(struct domain_device *parent, @@ -1742,29 +1880,32 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,  {  	struct expander_device *ex_dev = &parent->ex_dev;  	struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; -	struct domain_device *child, *n; +	struct domain_device *child, *n, *found = NULL;  	if (last) {  		list_for_each_entry_safe(child, n,  			&ex_dev->children, siblings) {  			if (SAS_ADDR(child->sas_addr) ==  			    SAS_ADDR(phy->attached_sas_addr)) { -				child->gone = 1; -				if (child->dev_type == EDGE_DEV || -				    child->dev_type == FANOUT_DEV) -					sas_unregister_ex_tree(child); +				set_bit(SAS_DEV_GONE, &child->state); +				if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || +				    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) +					sas_unregister_ex_tree(parent->port, child);  				else -					sas_unregister_dev(child); +					sas_unregister_dev(parent->port, child); +				found = child;  				break;  			}  		} -		parent->gone = 1;  		sas_disable_routing(parent, phy->attached_sas_addr);  	}  	memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); -	sas_port_delete_phy(phy->port, phy->phy); -	if (phy->port->num_phys == 0) -		sas_port_delete(phy->port); -	phy->port = NULL; +	if (phy->port) { +		sas_port_delete_phy(phy->port, phy->phy); +		sas_device_set_phy(found, phy->port); +		if (phy->port->num_phys == 0) +			sas_port_delete(phy->port); +		phy->port = NULL; +	}  }  static int sas_discover_bfs_by_root_level(struct domain_device *root, @@ -1775,8 +1916,8 @@ static int sas_discover_bfs_by_root_level(struct domain_device *root,  	int res = 0;  	list_for_each_entry(child, &ex_root->children, siblings) { -		if (child->dev_type == EDGE_DEV || -		    child->dev_type == FANOUT_DEV) { +		if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || +		    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {  			struct sas_expander_device *ex =  				rphy_to_expander_device(child->rphy); @@ -1812,78 +1953,102 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)  {  	struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];  	struct domain_device *child; -	bool found = false; -	int res, i; +	int res;  	SAS_DPRINTK("ex %016llx phy%d new device attached\n",  		    SAS_ADDR(dev->sas_addr), phy_id);  	res = sas_ex_phy_discover(dev, phy_id);  	if (res) -		goto out; -	/* to support the wide port inserted */ -	for (i = 0; i < dev->ex_dev.num_phys; i++) { -		struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i]; -		if (i == phy_id) -			continue; -		if (SAS_ADDR(ex_phy_temp->attached_sas_addr) == -		    SAS_ADDR(ex_phy->attached_sas_addr)) { -			found = true; -			break; -		} -	} -	if (found) { -		sas_ex_join_wide_port(dev, phy_id); +		return res; + +	if (sas_ex_join_wide_port(dev, phy_id))  		return 0; -	} +  	res = sas_ex_discover_devices(dev, phy_id); -	if (!res) -		goto out; +	if (res) +		return res;  	list_for_each_entry(child, &dev->ex_dev.children, siblings) {  		if (SAS_ADDR(child->sas_addr) ==  		    SAS_ADDR(ex_phy->attached_sas_addr)) { -			if (child->dev_type == EDGE_DEV || -			    child->dev_type == FANOUT_DEV) +			if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || +			    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE)  				res = sas_discover_bfs_by_root(child);  			break;  		}  	} -out:  	return res;  } +static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) +{ +	if (old == new) +		return true; + +	/* treat device directed resets as flutter, if we went +	 * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery +	 */ +	if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) || +	    (old == SAS_END_DEVICE && new == SAS_SATA_PENDING)) +		return true; + +	return false; +} +  static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)  {  	struct expander_device *ex = &dev->ex_dev;  	struct ex_phy *phy = &ex->ex_phy[phy_id]; -	u8 attached_sas_addr[8]; +	enum sas_device_type type = SAS_PHY_UNUSED; +	u8 sas_addr[8];  	int res; -	res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr); +	memset(sas_addr, 0, 8); +	res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type);  	switch (res) {  	case SMP_RESP_NO_PHY:  		phy->phy_state = PHY_NOT_PRESENT;  		sas_unregister_devs_sas_addr(dev, phy_id, last); -		goto out; break; +		return res;  	case SMP_RESP_PHY_VACANT:  		phy->phy_state = PHY_VACANT;  		sas_unregister_devs_sas_addr(dev, phy_id, last); -		goto out; break; +		return res;  	case SMP_RESP_FUNC_ACC:  		break; +	case -ECOMM: +		break; +	default: +		return res;  	} -	if (SAS_ADDR(attached_sas_addr) == 0) { +	if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) {  		phy->phy_state = PHY_EMPTY;  		sas_unregister_devs_sas_addr(dev, phy_id, last); -	} else if (SAS_ADDR(attached_sas_addr) == -		   SAS_ADDR(phy->attached_sas_addr)) { -		SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", -			    SAS_ADDR(dev->sas_addr), phy_id); +		return res; +	} else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && +		   dev_type_flutter(type, phy->attached_dev_type)) { +		struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); +		char *action = ""; +  		sas_ex_phy_discover(dev, phy_id); -	} else -		res = sas_discover_new(dev, phy_id); -out: -	return res; + +		if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) +			action = ", needs recovery"; +		SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", +			    SAS_ADDR(dev->sas_addr), phy_id, action); +		return res; +	} + +	/* delete the old link */ +	if (SAS_ADDR(phy->attached_sas_addr) && +	    SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr)) { +		SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n", +			    SAS_ADDR(dev->sas_addr), phy_id, +			    SAS_ADDR(phy->attached_sas_addr)); +		sas_unregister_devs_sas_addr(dev, phy_id, last); +	} + +	return sas_discover_new(dev, phy_id);  }  /** @@ -1946,9 +2111,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)  	struct domain_device *dev = NULL;  	res = sas_find_bcast_dev(port_dev, &dev); -	if (res) -		goto out; -	if (dev) { +	while (res == 0 && dev) {  		struct expander_device *ex = &dev->ex_dev;  		int i = 0, phy_id; @@ -1960,8 +2123,10 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)  			res = sas_rediscover(dev, phy_id);  			i = phy_id + 1;  		} while (i < ex->num_phys); + +		dev = NULL; +		res = sas_find_bcast_dev(port_dev, &dev);  	} -out:  	return res;  } @@ -1998,10 +2163,10 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,  	}  	/* do we need to support multiple segments? */ -	if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { -		printk("%s: multiple segments req %u %u, rsp %u %u\n", -		       __func__, req->bio->bi_vcnt, blk_rq_bytes(req), -		       rsp->bio->bi_vcnt, blk_rq_bytes(rsp)); +	if (bio_multiple_segments(req->bio) || +	    bio_multiple_segments(rsp->bio)) { +		printk("%s: multiple segments req %u, rsp %u\n", +		       __func__, blk_rq_bytes(req), blk_rq_bytes(rsp));  		return -EINVAL;  	} diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 04ad8dd1a74..d2479257516 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -11,6 +11,7 @@  #include <linux/scatterlist.h>  #include <linux/blkdev.h>  #include <linux/slab.h> +#include <linux/export.h>  #include "sas_internal.h" @@ -51,6 +52,91 @@ static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,  	resp_data[15] = rphy->identify.target_port_protocols;  } +/** + * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od' + * @od: od bit to find + * @data: incoming bitstream (from frame) + * @index: requested data register index (from frame) + * @count: total number of registers in the bitstream (from frame) + * @bit: bit position of 'od' in the returned byte + * + * returns NULL if 'od' is not in 'data' + * + * From SFF-8485 v0.7: + * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0) + *  and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1). + * + *  In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2) + *  and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)." + * + * The general-purpose (raw-bitstream) RX registers have the same layout + * although 'od' is renamed 'id' for 'input data'. + * + * SFF-8489 defines the behavior of the LEDs in response to the 'od' values. + */ +static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit) +{ +	unsigned int reg; +	u8 byte; + +	/* gp registers start at index 1 */ +	if (index == 0) +		return NULL; + +	index--; /* make index 0-based */ +	if (od < index * 32) +		return NULL; + +	od -= index * 32; +	reg = od >> 5; + +	if (reg >= count) +		return NULL; + +	od &= (1 << 5) - 1; +	byte = 3 - (od >> 3); +	*bit = od & ((1 << 3) - 1); + +	return &data[reg * 4 + byte]; +} + +int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count) +{ +	u8 *byte; +	u8 bit; + +	byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); +	if (!byte) +		return -1; + +	return (*byte >> bit) & 1; +} +EXPORT_SYMBOL(try_test_sas_gpio_gp_bit); + +static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, +				   u8 reg_type, u8 reg_index, u8 reg_count, +				   u8 *req_data) +{ +	struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); +	int written; + +	if (i->dft->lldd_write_gpio == NULL) { +		resp_data[2] = SMP_RESP_FUNC_UNK; +		return 0; +	} + +	written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index, +					  reg_count, req_data); + +	if (written < 0) { +		resp_data[2] = SMP_RESP_FUNC_FAILED; +		written = 0; +	} else +		resp_data[2] = SMP_RESP_FUNC_ACC; + +	return written; +} +  static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data,  				u8 phy_id)  { @@ -101,11 +187,14 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,  	struct sas_internal *i =  		to_sas_internal(sas_ha->core.shost->transportt);  	struct sas_phy_linkrates rates; +	struct asd_sas_phy *asd_phy;  	if (phy_id >= sas_ha->num_phys) {  		resp_data[2] = SMP_RESP_NO_PHY;  		return;  	} + +	asd_phy = sas_ha->sas_phy[phy_id];  	switch (phy_op) {  	case PHY_FUNC_NOP:  	case PHY_FUNC_LINK_RESET: @@ -124,7 +213,13 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,  	rates.minimum_linkrate = min;  	rates.maximum_linkrate = max; -	if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates)) +	/* filter reset requests through libata eh */ +	if (phy_op == PHY_FUNC_LINK_RESET && sas_try_ata_reset(asd_phy) == 0) { +		resp_data[2] = SMP_RESP_FUNC_ACC; +		return; +	} + +	if (i->dft->lldd_control_phy(asd_phy, phy_op, &rates))  		resp_data[2] = SMP_RESP_FUNC_FAILED;  	else  		resp_data[2] = SMP_RESP_FUNC_ACC; @@ -160,9 +255,9 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,  	}  	local_irq_disable(); -	buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio); +	buf = kmap_atomic(bio_page(req->bio));  	memcpy(req_data, buf, blk_rq_bytes(req)); -	kunmap_atomic(buf - bio_offset(req->bio), KM_USER0); +	kunmap_atomic(buf - bio_offset(req->bio));  	local_irq_enable();  	if (req_data[0] != SMP_REQUEST) @@ -230,9 +325,23 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,  		/* Can't implement; hosts have no routes */  		break; -	case SMP_WRITE_GPIO_REG: -		/* FIXME: need GPIO support in the transport class */ +	case SMP_WRITE_GPIO_REG: { +		/* SFF-8485 v0.7 */ +		const int base_frame_size = 11; +		int to_write = req_data[4]; + +		if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || +		    req->resid_len < base_frame_size + to_write * 4) { +			resp_data[2] = SMP_RESP_INV_FRM_LEN; +			break; +		} + +		to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], +						   req_data[3], to_write, &req_data[8]); +		req->resid_len -= base_frame_size + to_write * 4; +		rsp->resid_len -= 8;  		break; +	}  	case SMP_CONF_ROUTE_INFO:  		/* Can't implement; hosts have no routes */ @@ -261,10 +370,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,  	}  	local_irq_disable(); -	buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio); +	buf = kmap_atomic(bio_page(rsp->bio));  	memcpy(buf, resp_data, blk_rq_bytes(rsp));  	flush_kernel_dcache_page(bio_page(rsp->bio)); -	kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0); +	kunmap_atomic(buf - bio_offset(rsp->bio));  	local_irq_enable();   out: diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 2dc55343f67..dbc8a793fd8 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -28,6 +28,7 @@  #include <linux/init.h>  #include <linux/device.h>  #include <linux/spinlock.h> +#include <scsi/sas_ata.h>  #include <scsi/scsi_host.h>  #include <scsi/scsi_device.h>  #include <scsi/scsi_transport.h> @@ -37,7 +38,51 @@  #include "../scsi_sas_internal.h" -struct kmem_cache *sas_task_cache; +static struct kmem_cache *sas_task_cache; + +struct sas_task *sas_alloc_task(gfp_t flags) +{ +	struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags); + +	if (task) { +		INIT_LIST_HEAD(&task->list); +		spin_lock_init(&task->task_state_lock); +		task->task_state_flags = SAS_TASK_STATE_PENDING; +	} + +	return task; +} +EXPORT_SYMBOL_GPL(sas_alloc_task); + +struct sas_task *sas_alloc_slow_task(gfp_t flags) +{ +	struct sas_task *task = sas_alloc_task(flags); +	struct sas_task_slow *slow = kmalloc(sizeof(*slow), flags); + +	if (!task || !slow) { +		if (task) +			kmem_cache_free(sas_task_cache, task); +		kfree(slow); +		return NULL; +	} + +	task->slow_task = slow; +	init_timer(&slow->timer); +	init_completion(&slow->completion); + +	return task; +} +EXPORT_SYMBOL_GPL(sas_alloc_slow_task); + +void sas_free_task(struct sas_task *task) +{ +	if (task) { +		BUG_ON(!list_empty(&task->list)); +		kfree(task->slow_task); +		kmem_cache_free(sas_task_cache, task); +	} +} +EXPORT_SYMBOL_GPL(sas_free_task);  /*------------ SAS addr hash -----------*/  void sas_hash_addr(u8 *hashed, const u8 *sas_addr) @@ -68,18 +113,17 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr)  void sas_hae_reset(struct work_struct *work)  { -	struct sas_ha_event *ev = -		container_of(work, struct sas_ha_event, work); +	struct sas_ha_event *ev = to_sas_ha_event(work);  	struct sas_ha_struct *ha = ev->ha; -	sas_begin_event(HAE_RESET, &ha->event_lock, -			&ha->pending); +	clear_bit(HAE_RESET, &ha->pending);  }  int sas_register_ha(struct sas_ha_struct *sas_ha)  {  	int error = 0; +	mutex_init(&sas_ha->disco_mutex);  	spin_lock_init(&sas_ha->phy_port_lock);  	sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); @@ -88,8 +132,12 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)  	else if (sas_ha->lldd_queue_size == -1)  		sas_ha->lldd_queue_size = 128; /* Sanity */ -	sas_ha->state = SAS_HA_REGISTERED; -	spin_lock_init(&sas_ha->state_lock); +	set_bit(SAS_HA_REGISTERED, &sas_ha->state); +	spin_lock_init(&sas_ha->lock); +	mutex_init(&sas_ha->drain_mutex); +	init_waitqueue_head(&sas_ha->eh_wait_q); +	INIT_LIST_HEAD(&sas_ha->defer_q); +	INIT_LIST_HEAD(&sas_ha->eh_dev_q);  	error = sas_register_phys(sas_ha);  	if (error) { @@ -119,6 +167,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)  	}  	INIT_LIST_HEAD(&sas_ha->eh_done_q); +	INIT_LIST_HEAD(&sas_ha->eh_ata_q);  	return 0; @@ -129,19 +178,29 @@ Undo_phys:  	return error;  } -int sas_unregister_ha(struct sas_ha_struct *sas_ha) +static void sas_disable_events(struct sas_ha_struct *sas_ha)  { -	unsigned long flags; - -	/* Set the state to unregistered to avoid further -	 * events to be queued */ -	spin_lock_irqsave(&sas_ha->state_lock, flags); -	sas_ha->state = SAS_HA_UNREGISTERED; -	spin_unlock_irqrestore(&sas_ha->state_lock, flags); -	scsi_flush_work(sas_ha->core.shost); +	/* Set the state to unregistered to avoid further unchained +	 * events to be queued, and flush any in-progress drainers +	 */ +	mutex_lock(&sas_ha->drain_mutex); +	spin_lock_irq(&sas_ha->lock); +	clear_bit(SAS_HA_REGISTERED, &sas_ha->state); +	spin_unlock_irq(&sas_ha->lock); +	__sas_drain_work(sas_ha); +	mutex_unlock(&sas_ha->drain_mutex); +} +int sas_unregister_ha(struct sas_ha_struct *sas_ha) +{ +	sas_disable_events(sas_ha);  	sas_unregister_ports(sas_ha); +	/* flush unregistration work */ +	mutex_lock(&sas_ha->drain_mutex); +	__sas_drain_work(sas_ha); +	mutex_unlock(&sas_ha->drain_mutex); +  	if (sas_ha->lldd_max_execute_num > 1) {  		sas_shutdown_queue(sas_ha);  		sas_ha->lldd_max_execute_num = 1; @@ -152,23 +211,88 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha)  static int sas_get_linkerrors(struct sas_phy *phy)  { -	if (scsi_is_sas_phy_local(phy)) -		/* FIXME: we have no local phy stats -		 * gathering at this time */ -		return -EINVAL; +	if (scsi_is_sas_phy_local(phy)) { +		struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); +		struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); +		struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; +		struct sas_internal *i = +			to_sas_internal(sas_ha->core.shost->transportt); + +		return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); +	}  	return sas_smp_get_phy_events(phy);  } -int sas_phy_enable(struct sas_phy *phy, int enable) +int sas_try_ata_reset(struct asd_sas_phy *asd_phy) +{ +	struct domain_device *dev = NULL; + +	/* try to route user requested link resets through libata */ +	if (asd_phy->port) +		dev = asd_phy->port->port_dev; + +	/* validate that dev has been probed */ +	if (dev) +		dev = sas_find_dev_by_rphy(dev->rphy); + +	if (dev && dev_is_sata(dev)) { +		sas_ata_schedule_reset(dev); +		sas_ata_wait_eh(dev); +		return 0; +	} + +	return -ENODEV; +} + +/** + * transport_sas_phy_reset - reset a phy and permit libata to manage the link + * + * phy reset request via sysfs in host workqueue context so we know we + * can block on eh and safely traverse the domain_device topology + */ +static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ +	enum phy_func reset_type; + +	if (hard_reset) +		reset_type = PHY_FUNC_HARD_RESET; +	else +		reset_type = PHY_FUNC_LINK_RESET; + +	if (scsi_is_sas_phy_local(phy)) { +		struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); +		struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); +		struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; +		struct sas_internal *i = +			to_sas_internal(sas_ha->core.shost->transportt); + +		if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) +			return 0; +		return i->dft->lldd_control_phy(asd_phy, reset_type, NULL); +	} else { +		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); +		struct domain_device *ddev = sas_find_dev_by_rphy(rphy); +		struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number); + +		if (ata_dev && !hard_reset) { +			sas_ata_schedule_reset(ata_dev); +			sas_ata_wait_eh(ata_dev); +			return 0; +		} else +			return sas_smp_phy_control(ddev, phy->number, reset_type, NULL); +	} +} + +static int sas_phy_enable(struct sas_phy *phy, int enable)  {  	int ret; -	enum phy_func command; +	enum phy_func cmd;  	if (enable) -		command = PHY_FUNC_LINK_RESET; +		cmd = PHY_FUNC_LINK_RESET;  	else -		command = PHY_FUNC_DISABLE; +		cmd = PHY_FUNC_DISABLE;  	if (scsi_is_sas_phy_local(phy)) {  		struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); @@ -177,15 +301,18 @@ int sas_phy_enable(struct sas_phy *phy, int enable)  		struct sas_internal *i =  			to_sas_internal(sas_ha->core.shost->transportt); -		if (!enable) { -			sas_phy_disconnected(asd_phy); -			sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL); -		} -		ret = i->dft->lldd_control_phy(asd_phy, command, NULL); +		if (enable) +			ret = transport_sas_phy_reset(phy, 0); +		else +			ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL);  	} else {  		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);  		struct domain_device *ddev = sas_find_dev_by_rphy(rphy); -		ret = sas_smp_phy_control(ddev, phy->number, command, NULL); + +		if (enable) +			ret = transport_sas_phy_reset(phy, 0); +		else +			ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL);  	}  	return ret;  } @@ -195,6 +322,9 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)  	int ret;  	enum phy_func reset_type; +	if (!phy->enabled) +		return -ENODEV; +  	if (hard_reset)  		reset_type = PHY_FUNC_HARD_RESET;  	else @@ -255,9 +385,185 @@ int sas_set_phy_speed(struct sas_phy *phy,  	return ret;  } +void sas_prep_resume_ha(struct sas_ha_struct *ha) +{ +	int i; + +	set_bit(SAS_HA_REGISTERED, &ha->state); + +	/* clear out any stale link events/data from the suspension path */ +	for (i = 0; i < ha->num_phys; i++) { +		struct asd_sas_phy *phy = ha->sas_phy[i]; + +		memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); +		phy->port_events_pending = 0; +		phy->phy_events_pending = 0; +		phy->frame_rcvd_size = 0; +	} +} +EXPORT_SYMBOL(sas_prep_resume_ha); + +static int phys_suspended(struct sas_ha_struct *ha) +{ +	int i, rc = 0; + +	for (i = 0; i < ha->num_phys; i++) { +		struct asd_sas_phy *phy = ha->sas_phy[i]; + +		if (phy->suspended) +			rc++; +	} + +	return rc; +} + +void sas_resume_ha(struct sas_ha_struct *ha) +{ +	const unsigned long tmo = msecs_to_jiffies(25000); +	int i; + +	/* deform ports on phys that did not resume +	 * at this point we may be racing the phy coming back (as posted +	 * by the lldd).  So we post the event and once we are in the +	 * libsas context check that the phy remains suspended before +	 * tearing it down. +	 */ +	i = phys_suspended(ha); +	if (i) +		dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", +			 i, i > 1 ? "s" : ""); +	wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); +	for (i = 0; i < ha->num_phys; i++) { +		struct asd_sas_phy *phy = ha->sas_phy[i]; + +		if (phy->suspended) { +			dev_warn(&phy->phy->dev, "resume timeout\n"); +			sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT); +		} +	} + +	/* all phys are back up or timed out, turn on i/o so we can +	 * flush out disks that did not return +	 */ +	scsi_unblock_requests(ha->core.shost); +	sas_drain_work(ha); +} +EXPORT_SYMBOL(sas_resume_ha); + +void sas_suspend_ha(struct sas_ha_struct *ha) +{ +	int i; + +	sas_disable_events(ha); +	scsi_block_requests(ha->core.shost); +	for (i = 0; i < ha->num_phys; i++) { +		struct asd_sas_port *port = ha->sas_port[i]; + +		sas_discover_event(port, DISCE_SUSPEND); +	} + +	/* flush suspend events while unregistered */ +	mutex_lock(&ha->drain_mutex); +	__sas_drain_work(ha); +	mutex_unlock(&ha->drain_mutex); +} +EXPORT_SYMBOL(sas_suspend_ha); + +static void sas_phy_release(struct sas_phy *phy) +{ +	kfree(phy->hostdata); +	phy->hostdata = NULL; +} + +static void phy_reset_work(struct work_struct *work) +{ +	struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work); + +	d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); +} + +static void phy_enable_work(struct work_struct *work) +{ +	struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work); + +	d->enable_result = sas_phy_enable(d->phy, d->enable); +} + +static int sas_phy_setup(struct sas_phy *phy) +{ +	struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); + +	if (!d) +		return -ENOMEM; + +	mutex_init(&d->event_lock); +	INIT_SAS_WORK(&d->reset_work, phy_reset_work); +	INIT_SAS_WORK(&d->enable_work, phy_enable_work); +	d->phy = phy; +	phy->hostdata = d; + +	return 0; +} + +static int queue_phy_reset(struct sas_phy *phy, int hard_reset) +{ +	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); +	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); +	struct sas_phy_data *d = phy->hostdata; +	int rc; + +	if (!d) +		return -ENOMEM; + +	/* libsas workqueue coordinates ata-eh reset with discovery */ +	mutex_lock(&d->event_lock); +	d->reset_result = 0; +	d->hard_reset = hard_reset; + +	spin_lock_irq(&ha->lock); +	sas_queue_work(ha, &d->reset_work); +	spin_unlock_irq(&ha->lock); + +	rc = sas_drain_work(ha); +	if (rc == 0) +		rc = d->reset_result; +	mutex_unlock(&d->event_lock); + +	return rc; +} + +static int queue_phy_enable(struct sas_phy *phy, int enable) +{ +	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); +	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); +	struct sas_phy_data *d = phy->hostdata; +	int rc; + +	if (!d) +		return -ENOMEM; + +	/* libsas workqueue coordinates ata-eh reset with discovery */ +	mutex_lock(&d->event_lock); +	d->enable_result = 0; +	d->enable = enable; + +	spin_lock_irq(&ha->lock); +	sas_queue_work(ha, &d->enable_work); +	spin_unlock_irq(&ha->lock); + +	rc = sas_drain_work(ha); +	if (rc == 0) +		rc = d->enable_result; +	mutex_unlock(&d->event_lock); + +	return rc; +} +  static struct sas_function_template sft = { -	.phy_enable = sas_phy_enable, -	.phy_reset = sas_phy_reset, +	.phy_enable = queue_phy_enable, +	.phy_reset = queue_phy_reset, +	.phy_setup = sas_phy_setup, +	.phy_release = sas_phy_release,  	.set_phy_speed = sas_set_phy_speed,  	.get_linkerrors = sas_get_linkerrors,  	.smp_handler = sas_smp_handler, @@ -293,8 +599,7 @@ EXPORT_SYMBOL_GPL(sas_domain_release_transport);  static int __init sas_class_init(void)  { -	sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task), -					   0, SLAB_HWCACHE_ALIGN, NULL); +	sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN);  	if (!sas_task_cache)  		return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 0001374bd6b..7e7ba83f0a2 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -30,18 +30,27 @@  #include <scsi/scsi_host.h>  #include <scsi/scsi_transport_sas.h>  #include <scsi/libsas.h> +#include <scsi/sas_ata.h>  #define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) -#ifdef SAS_DEBUG -#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) -#else -#define SAS_DPRINTK(fmt, ...) -#endif +#define SAS_DPRINTK(fmt, ...) printk(KERN_DEBUG "sas: " fmt, ## __VA_ARGS__)  #define TO_SAS_TASK(_scsi_cmd)  ((void *)(_scsi_cmd)->host_scribble)  #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) +struct sas_phy_data { +	/* let reset be performed in sas_queue_work() context */ +	struct sas_phy *phy; +	struct mutex event_lock; +	int hard_reset; +	int reset_result; +	struct sas_work reset_work; +	int enable; +	int enable_result; +	struct sas_work enable_work; +}; +  void sas_scsi_recover_host(struct Scsi_Host *shost);  int sas_show_class(enum sas_class class, char *buf); @@ -60,14 +69,18 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);  int  sas_init_queue(struct sas_ha_struct *sas_ha);  int  sas_init_events(struct sas_ha_struct *sas_ha);  void sas_shutdown_queue(struct sas_ha_struct *sas_ha); +void sas_disable_revalidation(struct sas_ha_struct *ha); +void sas_enable_revalidation(struct sas_ha_struct *ha); +void __sas_drain_work(struct sas_ha_struct *ha); -void sas_deform_port(struct asd_sas_phy *phy); +void sas_deform_port(struct asd_sas_phy *phy, int gone);  void sas_porte_bytes_dmaed(struct work_struct *work);  void sas_porte_broadcast_rcvd(struct work_struct *work);  void sas_porte_link_reset_err(struct work_struct *work);  void sas_porte_timer_event(struct work_struct *work);  void sas_porte_hard_reset(struct work_struct *work); +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw);  int sas_notify_lldd_dev_found(struct domain_device *);  void sas_notify_lldd_dev_gone(struct domain_device *); @@ -76,10 +89,18 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,  			enum phy_func phy_func, struct sas_phy_linkrates *);  int sas_smp_get_phy_events(struct sas_phy *phy); +void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event); +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port);  struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); - +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); +int sas_ex_phy_discover(struct domain_device *dev, int single); +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, +			    struct smp_resp *rps_resp); +int sas_try_ata_reset(struct asd_sas_phy *phy);  void sas_hae_reset(struct work_struct *work); +void sas_free_device(struct kref *kref); +  #ifdef CONFIG_SCSI_SAS_HOST_SMP  extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,  				struct request *rsp); @@ -94,36 +115,13 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost,  }  #endif -static inline void sas_queue_event(int event, spinlock_t *lock, -				   unsigned long *pending, -				   struct work_struct *work, -				   struct sas_ha_struct *sas_ha) -{ -	unsigned long flags; - -	spin_lock_irqsave(lock, flags); -	if (test_bit(event, pending)) { -		spin_unlock_irqrestore(lock, flags); -		return; -	} -	__set_bit(event, pending); -	spin_unlock_irqrestore(lock, flags); - -	spin_lock_irqsave(&sas_ha->state_lock, flags); -	if (sas_ha->state != SAS_HA_UNREGISTERED) { -		scsi_queue_work(sas_ha->core.shost, work); -	} -	spin_unlock_irqrestore(&sas_ha->state_lock, flags); -} - -static inline void sas_begin_event(int event, spinlock_t *lock, -				   unsigned long *pending) +static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err)  { -	unsigned long flags; - -	spin_lock_irqsave(lock, flags); -	__clear_bit(event, pending); -	spin_unlock_irqrestore(lock, flags); +	SAS_DPRINTK("%s: for %s device %16llx returned %d\n", +		    func, dev->parent ? "exp-attached" : +					    "direct-attached", +		    SAS_ADDR(dev->sas_addr), err); +	sas_unregister_dev(dev->port, dev);  }  static inline void sas_fill_in_rphy(struct domain_device *dev, @@ -133,15 +131,16 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,  	rphy->identify.initiator_port_protocols = dev->iproto;  	rphy->identify.target_port_protocols = dev->tproto;  	switch (dev->dev_type) { -	case SATA_DEV: +	case SAS_SATA_DEV:  		/* FIXME: need sata device type */ -	case SAS_END_DEV: +	case SAS_END_DEVICE: +	case SAS_SATA_PENDING:  		rphy->identify.device_type = SAS_END_DEVICE;  		break; -	case EDGE_DEV: +	case SAS_EDGE_EXPANDER_DEVICE:  		rphy->identify.device_type = SAS_EDGE_EXPANDER_DEVICE;  		break; -	case FANOUT_DEV: +	case SAS_FANOUT_EXPANDER_DEVICE:  		rphy->identify.device_type = SAS_FANOUT_EXPANDER_DEVICE;  		break;  	default: @@ -150,6 +149,22 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,  	}  } +static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_device *dev) +{ +	struct sas_phy *phy = p->phy; + +	if (dev) { +		if (dev_is_sata(dev)) +			phy->identify.device_type = SAS_END_DEVICE; +		else +			phy->identify.device_type = dev->dev_type; +		phy->identify.target_port_protocols = dev->tproto; +	} else { +		phy->identify.device_type = SAS_PHY_UNUSED; +		phy->identify.target_port_protocols = 0; +	} +} +  static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)  {  	struct expander_device *ex = &dev->ex_dev; @@ -165,4 +180,23 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)  	sas_port_add_phy(ex->parent_port, ex_phy->phy);  } +static inline struct domain_device *sas_alloc_device(void) +{ +	struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + +	if (dev) { +		INIT_LIST_HEAD(&dev->siblings); +		INIT_LIST_HEAD(&dev->dev_list_node); +		INIT_LIST_HEAD(&dev->disco_list_node); +		kref_init(&dev->kref); +		spin_lock_init(&dev->done_lock); +	} +	return dev; +} + +static inline void sas_put_device(struct domain_device *dev) +{ +	kref_put(&dev->kref, sas_free_device); +} +  #endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index b459c4b635b..cdee446c29e 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -32,41 +32,35 @@  static void sas_phye_loss_of_signal(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock, -			&phy->phy_events_pending); +	clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);  	phy->error = 0; -	sas_deform_port(phy); +	sas_deform_port(phy, 1);  }  static void sas_phye_oob_done(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock, -			&phy->phy_events_pending); +	clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);  	phy->error = 0;  }  static void sas_phye_oob_error(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy;  	struct sas_ha_struct *sas_ha = phy->ha;  	struct asd_sas_port *port = phy->port;  	struct sas_internal *i =  		to_sas_internal(sas_ha->core.shost->transportt); -	sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock, -			&phy->phy_events_pending); +	clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); -	sas_deform_port(phy); +	sas_deform_port(phy, 1);  	if (!port && phy->enabled && i->dft->lldd_control_phy) {  		phy->error++; @@ -88,20 +82,37 @@ static void sas_phye_oob_error(struct work_struct *work)  static void sas_phye_spinup_hold(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy;  	struct sas_ha_struct *sas_ha = phy->ha;  	struct sas_internal *i =  		to_sas_internal(sas_ha->core.shost->transportt); -	sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock, -			&phy->phy_events_pending); +	clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending);  	phy->error = 0;  	i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);  } +static void sas_phye_resume_timeout(struct work_struct *work) +{ +	struct asd_sas_event *ev = to_asd_sas_event(work); +	struct asd_sas_phy *phy = ev->phy; + +	clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); + +	/* phew, lldd got the phy back in the nick of time */ +	if (!phy->suspended) { +		dev_info(&phy->phy->dev, "resume timeout cancelled\n"); +		return; +	} + +	phy->error = 0; +	phy->suspended = 0; +	sas_deform_port(phy, 1); +} + +  /* ---------- Phy class registration ---------- */  int sas_register_phys(struct sas_ha_struct *sas_ha) @@ -113,6 +124,8 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)  		[PHYE_OOB_DONE] = sas_phye_oob_done,  		[PHYE_OOB_ERROR] = sas_phye_oob_error,  		[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, +		[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, +  	};  	static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { @@ -131,14 +144,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)  		phy->error = 0;  		INIT_LIST_HEAD(&phy->port_phy_el);  		for (k = 0; k < PORT_NUM_EVENTS; k++) { -			INIT_WORK(&phy->port_events[k].work, -				  sas_port_event_fns[k]); +			INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]);  			phy->port_events[k].phy = phy;  		}  		for (k = 0; k < PHY_NUM_EVENTS; k++) { -			INIT_WORK(&phy->phy_events[k].work, -				  sas_phy_event_fns[k]); +			INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]);  			phy->phy_events[k].phy = phy;  		} @@ -148,8 +159,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)  		spin_lock_init(&phy->sas_prim_lock);  		phy->frame_rcvd_size = 0; -		phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, -					 i); +		phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);  		if (!phy->phy)  			return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index fe8b74c706d..d3c5297c6c8 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -28,6 +28,60 @@  #include <scsi/scsi_transport_sas.h>  #include "../scsi_sas_internal.h" +static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy) +{ +	struct sas_ha_struct *sas_ha = phy->ha; + +	if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, +		   SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports && +	     memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0)) +		return false; +	return true; +} + +static void sas_resume_port(struct asd_sas_phy *phy) +{ +	struct domain_device *dev; +	struct asd_sas_port *port = phy->port; +	struct sas_ha_struct *sas_ha = phy->ha; +	struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + +	if (si->dft->lldd_port_formed) +		si->dft->lldd_port_formed(phy); + +	if (port->suspended) +		port->suspended = 0; +	else { +		/* we only need to handle "link returned" actions once */ +		return; +	} + +	/* if the port came back: +	 * 1/ presume every device came back +	 * 2/ force the next revalidation to check all expander phys +	 */ +	list_for_each_entry(dev, &port->dev_list, dev_list_node) { +		int i, rc; + +		rc = sas_notify_lldd_dev_found(dev); +		if (rc) { +			sas_unregister_dev(port, dev); +			continue; +		} + +		if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { +			dev->ex_dev.ex_change_count = -1; +			for (i = 0; i < dev->ex_dev.num_phys; i++) { +				struct ex_phy *phy = &dev->ex_dev.ex_phy[i]; + +				phy->phy_change_count = -1; +			} +		} +	} + +	sas_discover_event(port, DISCE_RESUME); +} +  /**   * sas_form_port -- add this phy to a port   * @phy: the phy of interest @@ -45,10 +99,16 @@ static void sas_form_port(struct asd_sas_phy *phy)  	unsigned long flags;  	if (port) { -		if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, -			   SAS_ADDR_SIZE) != 0) -			sas_deform_port(phy); -		else { +		if (!phy_is_wideport_member(port, phy)) +			sas_deform_port(phy, 0); +		else if (phy->suspended) { +			phy->suspended = 0; +			sas_resume_port(phy); + +			/* phy came back, try to cancel the timeout */ +			wake_up(&sas_ha->eh_wait_q); +			return; +		} else {  			SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",  				    __func__, phy->id, phy->port->id,  				    phy->port->num_phys); @@ -62,9 +122,7 @@ static void sas_form_port(struct asd_sas_phy *phy)  		port = sas_ha->sas_port[i];  		spin_lock(&port->phy_list_lock);  		if (*(u64 *) port->sas_addr && -		    memcmp(port->attached_sas_addr, -			   phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 && -		    port->num_phys > 0) { +		    phy_is_wideport_member(port, phy) && port->num_phys > 0) {  			/* wide port */  			SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,  				    port->id); @@ -96,13 +154,11 @@ static void sas_form_port(struct asd_sas_phy *phy)  	/* add the phy to the port */  	list_add_tail(&phy->port_phy_el, &port->phy_list); +	sas_phy_set_target(phy, port->port_dev);  	phy->port = port;  	port->num_phys++;  	port->phy_mask |= (1U << phy->id); -	if (!port->phy) -		port->phy = phy->phy; -  	if (*(u64 *)port->attached_sas_addr == 0) {  		port->class = phy->class;  		memcpy(port->attached_sas_addr, phy->attached_sas_addr, @@ -145,27 +201,30 @@ static void sas_form_port(struct asd_sas_phy *phy)   * This is called when the physical link to the other phy has been   * lost (on this phy), in Event thread context. We cannot delay here.   */ -void sas_deform_port(struct asd_sas_phy *phy) +void sas_deform_port(struct asd_sas_phy *phy, int gone)  {  	struct sas_ha_struct *sas_ha = phy->ha;  	struct asd_sas_port *port = phy->port;  	struct sas_internal *si =  		to_sas_internal(sas_ha->core.shost->transportt); +	struct domain_device *dev;  	unsigned long flags;  	if (!port)  		return;		  /* done by a phy event */ -	if (port->port_dev) -		port->port_dev->pathways--; +	dev = port->port_dev; +	if (dev) +		dev->pathways--;  	if (port->num_phys == 1) { -		sas_unregister_domain_devices(port); +		sas_unregister_domain_devices(port, gone);  		sas_port_delete(port->port);  		port->port = NULL; -	} else +	} else {  		sas_port_delete_phy(port->port, phy->phy); - +		sas_device_set_phy(dev, port->port); +	}  	if (si->dft->lldd_port_deformed)  		si->dft->lldd_port_deformed(phy); @@ -174,6 +233,7 @@ void sas_deform_port(struct asd_sas_phy *phy)  	spin_lock(&port->phy_list_lock);  	list_del_init(&phy->port_phy_el); +	sas_phy_set_target(phy, NULL);  	phy->port = NULL;  	port->num_phys--;  	port->phy_mask &= ~(1U << phy->id); @@ -198,26 +258,22 @@ void sas_deform_port(struct asd_sas_phy *phy)  void sas_porte_bytes_dmaed(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock, -			&phy->port_events_pending); +	clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);  	sas_form_port(phy);  }  void sas_porte_broadcast_rcvd(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy;  	unsigned long flags;  	u32 prim; -	sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock, -			&phy->port_events_pending); +	clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending);  	spin_lock_irqsave(&phy->sas_prim_lock, flags);  	prim = phy->sas_prim; @@ -229,38 +285,32 @@ void sas_porte_broadcast_rcvd(struct work_struct *work)  void sas_porte_link_reset_err(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, -			&phy->port_events_pending); +	clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); -	sas_deform_port(phy); +	sas_deform_port(phy, 1);  }  void sas_porte_timer_event(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, -			&phy->port_events_pending); +	clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); -	sas_deform_port(phy); +	sas_deform_port(phy, 1);  }  void sas_porte_hard_reset(struct work_struct *work)  { -	struct asd_sas_event *ev = -		container_of(work, struct asd_sas_event, work); +	struct asd_sas_event *ev = to_asd_sas_event(work);  	struct asd_sas_phy *phy = ev->phy; -	sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, -			&phy->port_events_pending); +	clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); -	sas_deform_port(phy); +	sas_deform_port(phy, 1);  }  /* ---------- SAS port registration ---------- */ @@ -271,6 +321,8 @@ static void sas_init_port(struct asd_sas_port *port,  	memset(port, 0, sizeof(*port));  	port->id = i;  	INIT_LIST_HEAD(&port->dev_list); +	INIT_LIST_HEAD(&port->disco_list); +	INIT_LIST_HEAD(&port->destroy_list);  	spin_lock_init(&port->phy_list_lock);  	INIT_LIST_HEAD(&port->phy_list);  	port->ha = sas_ha; @@ -298,6 +350,6 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha)  	for (i = 0; i < sas_ha->num_phys; i++)  		if (sas_ha->sas_phy[i]->port) -			sas_deform_port(sas_ha->sas_phy[i]); +			sas_deform_port(sas_ha->sas_phy[i], 0);  } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 55f09e92ab5..25d0f127424 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -25,6 +25,7 @@  #include <linux/kthread.h>  #include <linux/firmware.h> +#include <linux/export.h>  #include <linux/ctype.h>  #include "sas_internal.h" @@ -48,27 +49,12 @@  #include <linux/scatterlist.h>  #include <linux/libata.h> -/* ---------- SCSI Host glue ---------- */ - -static void sas_scsi_task_done(struct sas_task *task) +/* record final status and free the task */ +static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task)  {  	struct task_status_struct *ts = &task->task_status; -	struct scsi_cmnd *sc = task->uldd_task;  	int hs = 0, stat = 0; -	if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { -		/* Aborted tasks will be completed by the error handler */ -		SAS_DPRINTK("task done but aborted\n"); -		return; -	} - -	if (unlikely(!sc)) { -		SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); -		list_del_init(&task->list); -		sas_free_task(task); -		return; -	} -  	if (ts->resp == SAS_TASK_UNDELIVERED) {  		/* transport error */  		hs = DID_NO_CONNECT; @@ -123,10 +109,41 @@ static void sas_scsi_task_done(struct sas_task *task)  			break;  		}  	} -	ASSIGN_SAS_TASK(sc, NULL); +  	sc->result = (hs << 16) | stat; +	ASSIGN_SAS_TASK(sc, NULL);  	list_del_init(&task->list);  	sas_free_task(task); +} + +static void sas_scsi_task_done(struct sas_task *task) +{ +	struct scsi_cmnd *sc = task->uldd_task; +	struct domain_device *dev = task->dev; +	struct sas_ha_struct *ha = dev->port->ha; +	unsigned long flags; + +	spin_lock_irqsave(&dev->done_lock, flags); +	if (test_bit(SAS_HA_FROZEN, &ha->state)) +		task = NULL; +	else +		ASSIGN_SAS_TASK(sc, NULL); +	spin_unlock_irqrestore(&dev->done_lock, flags); + +	if (unlikely(!task)) { +		/* task will be completed by the error handler */ +		SAS_DPRINTK("task done but aborted\n"); +		return; +	} + +	if (unlikely(!sc)) { +		SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); +		list_del_init(&task->list); +		sas_free_task(task); +		return; +	} + +	sas_end_task(sc, task);  	sc->scsi_done(sc);  } @@ -150,7 +167,7 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,  	int_to_scsilun(cmd->device->lun, &lun);  	memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8);  	task->ssp_task.task_attr = TASK_ATTR_SIMPLE; -	memcpy(task->ssp_task.cdb, cmd->cmnd, 16); +	task->ssp_task.cmd = cmd;  	task->scatter = scsi_sglist(cmd);  	task->num_scatter = scsi_sg_count(cmd); @@ -182,98 +199,88 @@ int sas_queue_up(struct sas_task *task)  	return 0;  } -/** - * sas_queuecommand -- Enqueue a command for processing - * @parameters: See SCSI Core documentation - * - * Note: XXX: Remove the host unlock/lock pair when SCSI Core can - * call us without holding an IRQ spinlock... - */ -int sas_queuecommand(struct scsi_cmnd *cmd, -		     void (*scsi_done)(struct scsi_cmnd *)) -	__releases(host->host_lock) -	__acquires(dev->sata_dev.ap->lock) -	__releases(dev->sata_dev.ap->lock) -	__acquires(host->host_lock) +int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)  { -	int res = 0; -	struct domain_device *dev = cmd_to_domain_dev(cmd); -	struct Scsi_Host *host = cmd->device->host;  	struct sas_internal *i = to_sas_internal(host->transportt); +	struct domain_device *dev = cmd_to_domain_dev(cmd); +	struct sas_ha_struct *sas_ha = dev->port->ha; +	struct sas_task *task; +	int res = 0; -	spin_unlock_irq(host->host_lock); - -	{ -		struct sas_ha_struct *sas_ha = dev->port->ha; -		struct sas_task *task; - -		if (dev_is_sata(dev)) { -			unsigned long flags; +	/* If the device fell off, no sense in issuing commands */ +	if (test_bit(SAS_DEV_GONE, &dev->state)) { +		cmd->result = DID_BAD_TARGET << 16; +		goto out_done; +	} -			spin_lock_irqsave(dev->sata_dev.ap->lock, flags); -			res = ata_sas_queuecmd(cmd, scsi_done, -					       dev->sata_dev.ap); -			spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); -			goto out; -		} +	if (dev_is_sata(dev)) { +		spin_lock_irq(dev->sata_dev.ap->lock); +		res = ata_sas_queuecmd(cmd, dev->sata_dev.ap); +		spin_unlock_irq(dev->sata_dev.ap->lock); +		return res; +	} -		/* If the device fell off, no sense in issuing commands */ -		if (dev->gone) { -			cmd->result = DID_BAD_TARGET << 16; -			scsi_done(cmd); -			goto out; -		} +	task = sas_create_task(cmd, dev, GFP_ATOMIC); +	if (!task) +		return SCSI_MLQUEUE_HOST_BUSY; -		res = -ENOMEM; -		task = sas_create_task(cmd, dev, GFP_ATOMIC); -		if (!task) -			goto out; +	/* Queue up, Direct Mode or Task Collector Mode. */ +	if (sas_ha->lldd_max_execute_num < 2) +		res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); +	else +		res = sas_queue_up(task); -		cmd->scsi_done = scsi_done; -		/* Queue up, Direct Mode or Task Collector Mode. */ -		if (sas_ha->lldd_max_execute_num < 2) -			res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); -		else -			res = sas_queue_up(task); +	if (res) +		goto out_free_task; +	return 0; -		/* Examine */ -		if (res) { -			SAS_DPRINTK("lldd_execute_task returned: %d\n", res); -			ASSIGN_SAS_TASK(cmd, NULL); -			sas_free_task(task); -			if (res == -SAS_QUEUE_FULL) { -				cmd->result = DID_SOFT_ERROR << 16; /* retry */ -				res = 0; -				scsi_done(cmd); -			} -			goto out; -		} -	} -out: -	spin_lock_irq(host->host_lock); -	return res; +out_free_task: +	SAS_DPRINTK("lldd_execute_task returned: %d\n", res); +	ASSIGN_SAS_TASK(cmd, NULL); +	sas_free_task(task); +	if (res == -SAS_QUEUE_FULL) +		cmd->result = DID_SOFT_ERROR << 16; /* retry */ +	else +		cmd->result = DID_ERROR << 16; +out_done: +	cmd->scsi_done(cmd); +	return 0;  }  static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)  { -	struct sas_task *task = TO_SAS_TASK(cmd);  	struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host); +	struct sas_task *task = TO_SAS_TASK(cmd); + +	/* At this point, we only get called following an actual abort +	 * of the task, so we should be guaranteed not to be racing with +	 * any completions from the LLD.  Task is freed after this. +	 */ +	sas_end_task(cmd, task); -	/* remove the aborted task flag to allow the task to be -	 * completed now. At this point, we only get called following -	 * an actual abort of the task, so we should be guaranteed not -	 * to be racing with any completions from the LLD (hence we -	 * don't need the task state lock to clear the flag) */ -	task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; -	/* Now call task_done.  However, task will be free'd after -	 * this */ -	task->task_done(task);  	/* now finish the command and move it on to the error  	 * handler done list, this also takes it off the -	 * error handler pending list */ +	 * error handler pending list. +	 */  	scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);  } +static void sas_eh_defer_cmd(struct scsi_cmnd *cmd) +{ +	struct domain_device *dev = cmd_to_domain_dev(cmd); +	struct sas_ha_struct *ha = dev->port->ha; +	struct sas_task *task = TO_SAS_TASK(cmd); + +	if (!dev_is_sata(dev)) { +		sas_eh_finish_cmd(cmd); +		return; +	} + +	/* report the timeout to libata */ +	sas_end_task(cmd, task); +	list_move_tail(&cmd->eh_entry, &ha->eh_ata_q); +} +  static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd)  {  	struct scsi_cmnd *cmd, *n; @@ -281,7 +288,7 @@ static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd  	list_for_each_entry_safe(cmd, n, error_q, eh_entry) {  		if (cmd->device->sdev_target == my_cmd->device->sdev_target &&  		    cmd->device->lun == my_cmd->device->lun) -			sas_eh_finish_cmd(cmd); +			sas_eh_defer_cmd(cmd);  	}  } @@ -316,6 +323,7 @@ enum task_disposition {  	TASK_IS_DONE,  	TASK_IS_ABORTED,  	TASK_IS_AT_LU, +	TASK_IS_NOT_AT_HA,  	TASK_IS_NOT_AT_LU,  	TASK_ABORT_FAILED,  }; @@ -332,19 +340,18 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)  		struct scsi_core *core = &ha->core;  		struct sas_task *t, *n; +		mutex_lock(&core->task_queue_flush);  		spin_lock_irqsave(&core->task_queue_lock, flags); -		list_for_each_entry_safe(t, n, &core->task_queue, list) { +		list_for_each_entry_safe(t, n, &core->task_queue, list)  			if (task == t) {  				list_del_init(&t->list); -				spin_unlock_irqrestore(&core->task_queue_lock, -						       flags); -				SAS_DPRINTK("%s: task 0x%p aborted from " -					    "task_queue\n", -					    __func__, task); -				return TASK_IS_ABORTED; +				break;  			} -		}  		spin_unlock_irqrestore(&core->task_queue_lock, flags); +		mutex_unlock(&core->task_queue_flush); + +		if (task == t) +			return TASK_IS_NOT_AT_HA;  	}  	for (i = 0; i < 5; i++) { @@ -432,39 +439,130 @@ static int sas_recover_I_T(struct domain_device *dev)  	return res;  } -/* Find the sas_phy that's attached to this device */ -struct sas_phy *sas_find_local_phy(struct domain_device *dev) +/* take a reference on the last known good phy for this device */ +struct sas_phy *sas_get_local_phy(struct domain_device *dev)  { -	struct domain_device *pdev = dev->parent; -	struct ex_phy *exphy = NULL; -	int i; +	struct sas_ha_struct *ha = dev->port->ha; +	struct sas_phy *phy; +	unsigned long flags; -	/* Directly attached device */ -	if (!pdev) -		return dev->port->phy; +	/* a published domain device always has a valid phy, it may be +	 * stale, but it is never NULL +	 */ +	BUG_ON(!dev->phy); -	/* Otherwise look in the expander */ -	for (i = 0; i < pdev->ex_dev.num_phys; i++) -		if (!memcmp(dev->sas_addr, -			    pdev->ex_dev.ex_phy[i].attached_sas_addr, -			    SAS_ADDR_SIZE)) { -			exphy = &pdev->ex_dev.ex_phy[i]; -			break; +	spin_lock_irqsave(&ha->phy_port_lock, flags); +	phy = dev->phy; +	get_device(&phy->dev); +	spin_unlock_irqrestore(&ha->phy_port_lock, flags); + +	return phy; +} +EXPORT_SYMBOL_GPL(sas_get_local_phy); + +static void sas_wait_eh(struct domain_device *dev) +{ +	struct sas_ha_struct *ha = dev->port->ha; +	DEFINE_WAIT(wait); + +	if (dev_is_sata(dev)) { +		ata_port_wait_eh(dev->sata_dev.ap); +		return; +	} + retry: +	spin_lock_irq(&ha->lock); + +	while (test_bit(SAS_DEV_EH_PENDING, &dev->state)) { +		prepare_to_wait(&ha->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); +		spin_unlock_irq(&ha->lock); +		schedule(); +		spin_lock_irq(&ha->lock); +	} +	finish_wait(&ha->eh_wait_q, &wait); + +	spin_unlock_irq(&ha->lock); + +	/* make sure SCSI EH is complete */ +	if (scsi_host_in_recovery(ha->core.shost)) { +		msleep(10); +		goto retry; +	} +} +EXPORT_SYMBOL(sas_wait_eh); + +static int sas_queue_reset(struct domain_device *dev, int reset_type, int lun, int wait) +{ +	struct sas_ha_struct *ha = dev->port->ha; +	int scheduled = 0, tries = 100; + +	/* ata: promote lun reset to bus reset */ +	if (dev_is_sata(dev)) { +		sas_ata_schedule_reset(dev); +		if (wait) +			sas_ata_wait_eh(dev); +		return SUCCESS; +	} + +	while (!scheduled && tries--) { +		spin_lock_irq(&ha->lock); +		if (!test_bit(SAS_DEV_EH_PENDING, &dev->state) && +		    !test_bit(reset_type, &dev->state)) { +			scheduled = 1; +			ha->eh_active++; +			list_add_tail(&dev->ssp_dev.eh_list_node, &ha->eh_dev_q); +			set_bit(SAS_DEV_EH_PENDING, &dev->state); +			set_bit(reset_type, &dev->state); +			int_to_scsilun(lun, &dev->ssp_dev.reset_lun); +			scsi_schedule_eh(ha->core.shost);  		} +		spin_unlock_irq(&ha->lock); -	BUG_ON(!exphy); -	return exphy->phy; +		if (wait) +			sas_wait_eh(dev); + +		if (scheduled) +			return SUCCESS; +	} + +	SAS_DPRINTK("%s reset of %s failed\n", +		    reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus", +		    dev_name(&dev->rphy->dev)); + +	return FAILED;  } -EXPORT_SYMBOL_GPL(sas_find_local_phy); + +int sas_eh_abort_handler(struct scsi_cmnd *cmd) +{ +	int res; +	struct sas_task *task = TO_SAS_TASK(cmd); +	struct Scsi_Host *host = cmd->device->host; +	struct sas_internal *i = to_sas_internal(host->transportt); + +	if (current != host->ehandler) +		return FAILED; + +	if (!i->dft->lldd_abort_task) +		return FAILED; + +	res = i->dft->lldd_abort_task(task); +	if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) +		return SUCCESS; + +	return FAILED; +} +EXPORT_SYMBOL_GPL(sas_eh_abort_handler);  /* Attempt to send a LUN reset message to a device */  int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)  { -	struct domain_device *dev = cmd_to_domain_dev(cmd); -	struct sas_internal *i = -		to_sas_internal(dev->port->ha->core.shost->transportt); -	struct scsi_lun lun;  	int res; +	struct scsi_lun lun; +	struct Scsi_Host *host = cmd->device->host; +	struct domain_device *dev = cmd_to_domain_dev(cmd); +	struct sas_internal *i = to_sas_internal(host->transportt); + +	if (current != host->ehandler) +		return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun, 0);  	int_to_scsilun(cmd->device->lun, &lun); @@ -478,19 +576,22 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)  	return FAILED;  } -/* Attempt to send a phy (bus) reset */  int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd)  { -	struct domain_device *dev = cmd_to_domain_dev(cmd); -	struct sas_phy *phy = sas_find_local_phy(dev);  	int res; +	struct Scsi_Host *host = cmd->device->host; +	struct domain_device *dev = cmd_to_domain_dev(cmd); +	struct sas_internal *i = to_sas_internal(host->transportt); -	res = sas_phy_reset(phy, 1); -	if (res) -		SAS_DPRINTK("Bus reset of %s failed 0x%x\n", -			    kobject_name(&phy->dev.kobj), -			    res); -	if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) +	if (current != host->ehandler) +		return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); + +	if (!i->dft->lldd_I_T_nexus_reset) +		return FAILED; + +	res = i->dft->lldd_I_T_nexus_reset(dev); +	if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE || +	    res == -ENODEV)  		return SUCCESS;  	return FAILED; @@ -516,9 +617,7 @@ try_bus_reset:  	return FAILED;  } -static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, -				    struct list_head *work_q, -				    struct list_head *done_q) +static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *work_q)  {  	struct scsi_cmnd *cmd, *n;  	enum task_disposition res = TASK_IS_DONE; @@ -526,13 +625,28 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,  	struct sas_internal *i = to_sas_internal(shost->transportt);  	unsigned long flags;  	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); +	LIST_HEAD(done); -Again: +	/* clean out any commands that won the completion vs eh race */  	list_for_each_entry_safe(cmd, n, work_q, eh_entry) { -		struct sas_task *task = TO_SAS_TASK(cmd); +		struct domain_device *dev = cmd_to_domain_dev(cmd); +		struct sas_task *task; + +		spin_lock_irqsave(&dev->done_lock, flags); +		/* by this point the lldd has either observed +		 * SAS_HA_FROZEN and is leaving the task alone, or has +		 * won the race with eh and decided to complete it +		 */ +		task = TO_SAS_TASK(cmd); +		spin_unlock_irqrestore(&dev->done_lock, flags);  		if (!task) -			continue; +			list_move_tail(&cmd->eh_entry, &done); +	} + + Again: +	list_for_each_entry_safe(cmd, n, work_q, eh_entry) { +		struct sas_task *task = TO_SAS_TASK(cmd);  		list_del_init(&cmd->eh_entry); @@ -552,15 +666,23 @@ Again:  		cmd->eh_eflags = 0;  		switch (res) { +		case TASK_IS_NOT_AT_HA: +			SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n", +				    __func__, task, +				    cmd->retries ? "retry" : "aborted"); +			if (cmd->retries) +				cmd->retries--; +			sas_eh_finish_cmd(cmd); +			continue;  		case TASK_IS_DONE:  			SAS_DPRINTK("%s: task 0x%p is done\n", __func__,  				    task); -			sas_eh_finish_cmd(cmd); +			sas_eh_defer_cmd(cmd);  			continue;  		case TASK_IS_ABORTED:  			SAS_DPRINTK("%s: task 0x%p is aborted\n",  				    __func__, task); -			sas_eh_finish_cmd(cmd); +			sas_eh_defer_cmd(cmd);  			continue;  		case TASK_IS_AT_LU:  			SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); @@ -571,7 +693,7 @@ Again:  					    "recovered\n",  					    SAS_ADDR(task->dev),  					    cmd->device->lun); -				sas_eh_finish_cmd(cmd); +				sas_eh_defer_cmd(cmd);  				sas_scsi_clear_queue_lu(work_q, cmd);  				goto Again;  			} @@ -581,7 +703,8 @@ Again:  			SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",  				    task);  			tmf_resp = sas_recover_I_T(task->dev); -			if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { +			if (tmf_resp == TMF_RESP_FUNC_COMPLETE || +			    tmf_resp == -ENODEV) {  				struct domain_device *dev = task->dev;  				SAS_DPRINTK("I_T %016llx recovered\n",  					    SAS_ADDR(task->dev->sas_addr)); @@ -628,31 +751,77 @@ Again:  			goto clear_q;  		}  	} -	return list_empty(work_q); -clear_q: + out: +	list_splice_tail(&done, work_q); +	list_splice_tail_init(&ha->eh_ata_q, work_q); +	return; + + clear_q:  	SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__);  	list_for_each_entry_safe(cmd, n, work_q, eh_entry)  		sas_eh_finish_cmd(cmd); +	goto out; +} + +static void sas_eh_handle_resets(struct Scsi_Host *shost) +{ +	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); +	struct sas_internal *i = to_sas_internal(shost->transportt); + +	/* handle directed resets to sas devices */ +	spin_lock_irq(&ha->lock); +	while (!list_empty(&ha->eh_dev_q)) { +		struct domain_device *dev; +		struct ssp_device *ssp; + +		ssp = list_entry(ha->eh_dev_q.next, typeof(*ssp), eh_list_node); +		list_del_init(&ssp->eh_list_node); +		dev = container_of(ssp, typeof(*dev), ssp_dev); +		kref_get(&dev->kref); +		WARN_ONCE(dev_is_sata(dev), "ssp reset to ata device?\n"); -	return list_empty(work_q); +		spin_unlock_irq(&ha->lock); + +		if (test_and_clear_bit(SAS_DEV_LU_RESET, &dev->state)) +			i->dft->lldd_lu_reset(dev, ssp->reset_lun.scsi_lun); + +		if (test_and_clear_bit(SAS_DEV_RESET, &dev->state)) +			i->dft->lldd_I_T_nexus_reset(dev); + +		sas_put_device(dev); +		spin_lock_irq(&ha->lock); +		clear_bit(SAS_DEV_EH_PENDING, &dev->state); +		ha->eh_active--; +	} +	spin_unlock_irq(&ha->lock);  } +  void sas_scsi_recover_host(struct Scsi_Host *shost)  {  	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); -	unsigned long flags;  	LIST_HEAD(eh_work_q); +	int tries = 0; +	bool retry; -	spin_lock_irqsave(shost->host_lock, flags); +retry: +	tries++; +	retry = true; +	spin_lock_irq(shost->host_lock);  	list_splice_init(&shost->eh_cmd_q, &eh_work_q); -	spin_unlock_irqrestore(shost->host_lock, flags); +	spin_unlock_irq(shost->host_lock); -	SAS_DPRINTK("Enter %s\n", __func__); +	SAS_DPRINTK("Enter %s busy: %d failed: %d\n", +		    __func__, shost->host_busy, shost->host_failed);  	/*  	 * Deal with commands that still have SAS tasks (i.e. they didn't -	 * complete via the normal sas_task completion mechanism) +	 * complete via the normal sas_task completion mechanism), +	 * SAS_HA_FROZEN gives eh dominion over all sas_task completion.  	 */ -	if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q)) +	set_bit(SAS_HA_FROZEN, &ha->state); +	sas_eh_handle_sas_errors(shost, &eh_work_q); +	clear_bit(SAS_HA_FROZEN, &ha->state); +	if (list_empty(&eh_work_q))  		goto out;  	/* @@ -661,50 +830,39 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)  	 * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any  	 * command we see here has no sas_task and is thus unknown to the HA.  	 */ +	sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q);  	if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))  		scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);  out: -	scsi_eh_flush_done_q(&ha->eh_done_q); -	SAS_DPRINTK("--- Exit %s\n", __func__); -	return; -} +	if (ha->lldd_max_execute_num > 1) +		wake_up_process(ha->core.queue_thread); -enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) -{ -	struct sas_task *task = TO_SAS_TASK(cmd); -	unsigned long flags; +	sas_eh_handle_resets(shost); -	if (!task) { -		cmd->request->timeout /= 2; -		SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n", -			    cmd, task, (cmd->request->timeout ? -			    "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED")); -		if (!cmd->request->timeout) -			return BLK_EH_NOT_HANDLED; -		return BLK_EH_RESET_TIMER; -	} +	/* now link into libata eh --- if we have any ata devices */ +	sas_ata_strategy_handler(shost); -	spin_lock_irqsave(&task->task_state_lock, flags); -	BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); -	if (task->task_state_flags & SAS_TASK_STATE_DONE) { -		spin_unlock_irqrestore(&task->task_state_lock, flags); -		SAS_DPRINTK("command 0x%p, task 0x%p, timed out: " -			    "BLK_EH_HANDLED\n", cmd, task); -		return BLK_EH_HANDLED; -	} -	if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) { -		spin_unlock_irqrestore(&task->task_state_lock, flags); -		SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: " -			    "BLK_EH_RESET_TIMER\n", -			    cmd, task); -		return BLK_EH_RESET_TIMER; +	scsi_eh_flush_done_q(&ha->eh_done_q); + +	/* check if any new eh work was scheduled during the last run */ +	spin_lock_irq(&ha->lock); +	if (ha->eh_active == 0) { +		shost->host_eh_scheduled = 0; +		retry = false;  	} -	task->task_state_flags |= SAS_TASK_STATE_ABORTED; -	spin_unlock_irqrestore(&task->task_state_lock, flags); +	spin_unlock_irq(&ha->lock); + +	if (retry) +		goto retry; -	SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n", -		    cmd, task); +	SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", +		    __func__, shost->host_busy, shost->host_failed, tries); +} + +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) +{ +	scmd_dbg(cmd, "command %p timed out\n", cmd);  	return BLK_EH_NOT_HANDLED;  } @@ -748,33 +906,20 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)  	return found_dev;  } -static inline struct domain_device *sas_find_target(struct scsi_target *starget) -{ -	struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); - -	return sas_find_dev_by_rphy(rphy); -} -  int sas_target_alloc(struct scsi_target *starget)  { -	struct domain_device *found_dev = sas_find_target(starget); -	int res; +	struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); +	struct domain_device *found_dev = sas_find_dev_by_rphy(rphy);  	if (!found_dev)  		return -ENODEV; -	if (dev_is_sata(found_dev)) { -		res = sas_ata_init_host_and_port(found_dev, starget); -		if (res) -			return res; -	} - +	kref_get(&found_dev->kref);  	starget->hostdata = found_dev;  	return 0;  } -#define SAS_DEF_QD 32 -#define SAS_MAX_QD 64 +#define SAS_DEF_QD 256  int sas_slave_configure(struct scsi_device *scsi_dev)  { @@ -809,39 +954,38 @@ int sas_slave_configure(struct scsi_device *scsi_dev)  	return 0;  } -void sas_slave_destroy(struct scsi_device *scsi_dev) +int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason)  { -	struct domain_device *dev = sdev_to_domain_dev(scsi_dev); +	struct domain_device *dev = sdev_to_domain_dev(sdev);  	if (dev_is_sata(dev)) -		dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE; -} - -int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth, -			   int reason) -{ -	int res = min(new_depth, SAS_MAX_QD); - -	if (reason != SCSI_QDEPTH_DEFAULT) +		return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth, +						reason); + +	switch (reason) { +	case SCSI_QDEPTH_DEFAULT: +	case SCSI_QDEPTH_RAMP_UP: +		if (!sdev->tagged_supported) +			depth = 1; +		scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); +		break; +	case SCSI_QDEPTH_QFULL: +		scsi_track_queue_full(sdev, depth); +		break; +	default:  		return -EOPNOTSUPP; - -	if (scsi_dev->tagged_supported) -		scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), -					res); -	else { -		struct domain_device *dev = sdev_to_domain_dev(scsi_dev); -		sas_printk("device %llx LUN %x queue depth changed to 1\n", -			   SAS_ADDR(dev->sas_addr), -			   scsi_dev->lun); -		scsi_adjust_queue_depth(scsi_dev, 0, 1); -		res = 1;  	} -	return res; +	return depth;  }  int sas_change_queue_type(struct scsi_device *scsi_dev, int qt)  { +	struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + +	if (dev_is_sata(dev)) +		return -EINVAL; +  	if (!scsi_dev->tagged_supported)  		return 0; @@ -876,9 +1020,11 @@ static void sas_queue(struct sas_ha_struct *sas_ha)  	int res;  	struct sas_internal *i = to_sas_internal(core->shost->transportt); +	mutex_lock(&core->task_queue_flush);  	spin_lock_irqsave(&core->task_queue_lock, flags);  	while (!kthread_should_stop() && -	       !list_empty(&core->task_queue)) { +	       !list_empty(&core->task_queue) && +	       !test_bit(SAS_HA_FROZEN, &sas_ha->state)) {  		can_queue = sas_ha->lldd_queue_size - core->task_queue_size;  		if (can_queue >= 0) { @@ -914,6 +1060,7 @@ static void sas_queue(struct sas_ha_struct *sas_ha)  		}  	}  	spin_unlock_irqrestore(&core->task_queue_lock, flags); +	mutex_unlock(&core->task_queue_flush);  }  /** @@ -940,6 +1087,7 @@ int sas_init_queue(struct sas_ha_struct *sas_ha)  	struct scsi_core *core = &sas_ha->core;  	spin_lock_init(&core->task_queue_lock); +	mutex_init(&core->task_queue_flush);  	core->task_queue_size = 0;  	INIT_LIST_HEAD(&core->task_queue); @@ -977,49 +1125,6 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha)  }  /* - * Call the LLDD task abort routine directly.  This function is intended for - * use by upper layers that need to tell the LLDD to abort a task. - */ -int __sas_task_abort(struct sas_task *task) -{ -	struct sas_internal *si = -		to_sas_internal(task->dev->port->ha->core.shost->transportt); -	unsigned long flags; -	int res; - -	spin_lock_irqsave(&task->task_state_lock, flags); -	if (task->task_state_flags & SAS_TASK_STATE_ABORTED || -	    task->task_state_flags & SAS_TASK_STATE_DONE) { -		spin_unlock_irqrestore(&task->task_state_lock, flags); -		SAS_DPRINTK("%s: Task %p already finished.\n", __func__, -			    task); -		return 0; -	} -	task->task_state_flags |= SAS_TASK_STATE_ABORTED; -	spin_unlock_irqrestore(&task->task_state_lock, flags); - -	if (!si->dft->lldd_abort_task) -		return -ENODEV; - -	res = si->dft->lldd_abort_task(task); - -	spin_lock_irqsave(&task->task_state_lock, flags); -	if ((task->task_state_flags & SAS_TASK_STATE_DONE) || -	    (res == TMF_RESP_FUNC_COMPLETE)) -	{ -		spin_unlock_irqrestore(&task->task_state_lock, flags); -		task->task_done(task); -		return 0; -	} - -	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) -		task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; -	spin_unlock_irqrestore(&task->task_state_lock, flags); - -	return -EAGAIN; -} - -/*   * Tell an upper layer that it needs to initiate an abort for a given task.   * This should only ever be called by an LLDD.   */ @@ -1029,9 +1134,13 @@ void sas_task_abort(struct sas_task *task)  	/* Escape for libsas internal commands */  	if (!sc) { -		if (!del_timer(&task->timer)) +		struct sas_task_slow *slow = task->slow_task; + +		if (!slow)  			return; -		task->timer.function(task->timer.data); +		if (!del_timer(&slow->timer)) +			return; +		slow->timer.function(slow->timer.data);  		return;  	} @@ -1044,31 +1153,18 @@ void sas_task_abort(struct sas_task *task)  		spin_lock_irqsave(q->queue_lock, flags);  		blk_abort_request(sc->request);  		spin_unlock_irqrestore(q->queue_lock, flags); -		scsi_schedule_eh(sc->device->host);  	}  } -int sas_slave_alloc(struct scsi_device *scsi_dev) -{ -	struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - -	if (dev_is_sata(dev)) -		return ata_sas_port_init(dev->sata_dev.ap); - -	return 0; -} -  void sas_target_destroy(struct scsi_target *starget)  { -	struct domain_device *found_dev = sas_find_target(starget); +	struct domain_device *found_dev = starget->hostdata;  	if (!found_dev)  		return; -	if (dev_is_sata(found_dev)) -		ata_sas_port_destroy(found_dev->sata_dev.ap); - -	return; +	starget->hostdata = NULL; +	sas_put_device(found_dev);  }  static void sas_parse_addr(u8 *sas_addr, const char *p) @@ -1113,16 +1209,12 @@ EXPORT_SYMBOL_GPL(sas_request_addr);  EXPORT_SYMBOL_GPL(sas_queuecommand);  EXPORT_SYMBOL_GPL(sas_target_alloc);  EXPORT_SYMBOL_GPL(sas_slave_configure); -EXPORT_SYMBOL_GPL(sas_slave_destroy);  EXPORT_SYMBOL_GPL(sas_change_queue_depth);  EXPORT_SYMBOL_GPL(sas_change_queue_type);  EXPORT_SYMBOL_GPL(sas_bios_param); -EXPORT_SYMBOL_GPL(__sas_task_abort);  EXPORT_SYMBOL_GPL(sas_task_abort);  EXPORT_SYMBOL_GPL(sas_phy_reset); -EXPORT_SYMBOL_GPL(sas_phy_enable);  EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler);  EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); -EXPORT_SYMBOL_GPL(sas_slave_alloc);  EXPORT_SYMBOL_GPL(sas_target_destroy);  EXPORT_SYMBOL_GPL(sas_ioctl); diff --git a/drivers/scsi/libsas/sas_task.c b/drivers/scsi/libsas/sas_task.c index b13a3346894..a78e5bd3e51 100644 --- a/drivers/scsi/libsas/sas_task.c +++ b/drivers/scsi/libsas/sas_task.c @@ -1,4 +1,5 @@  #include <linux/kernel.h> +#include <linux/export.h>  #include <scsi/sas.h>  #include <scsi/libsas.h>  | 
