Re: [PATCH] pm8001: deficient responses to IO_XFER_ERROR_BREAK and IO_XFER_OPEN_RETRY_TIMEOUT

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

 



Thanks Mark for fix this.
Acked-by: Jack Wang <jack_wang@xxxxxxxxx>



> IO_XFER_ERROR_BREAK and IO_XFER_OPEN_RETRY_TIMEOUT are deficient of the
> required actions as outlined in the programming manual for the pm8001. Due
to
> the overlapping code requirements of these recovery responses, we found it
> necessary to bundle them together into one patch.
> 
> When a break is received during the command phase (ssp_completion), this
is
> a result of a timeout or interruption on the bus. Logic suggests that we
should
> retry the command.
> 
> When a break is received during the data-phase (ssp_event), the task must
be
> aborted on the target or it will retain a data-phase lock turning the
target
> reticent to all future media commands yet will successfully respond to
TUR,
> INQUIRY and ABORT leading eventually to target failure through several
> abort-cycle loops.
> 
> The open retry interval is exceedingly short resulting in occasional
target
> drop-off during expander resets or when targets push-back during bad-block
> remapping. Increased effective timeout from 130ms to 1.5 seconds for each
try
> so as to trigger after the administrative inquiry/tur timeout in the scsi
> subsystem to keep error-recovery harmonics to a minimum.
> 
> When an open retry timeout event is received, the action required by the
targets
> is to issue an abort for the outstanding command then logic suggests we
retry
> the command as this state is usually an indication of a credit block or
busy
> condition on the target.
> 
> We hijacked the pm8001_handle_event work queue handler so that it will
handle
> task as an argument instead of device for the workers in support of the
deferred
> handling outlined above.
> 
> Moderate to Heavy bad-path testing on a 2.6.32 vintage kernel,
compile-testing
> on scsi-misc-2.6 kernel ...
> 
> Signed-off-by: mark_salyzyn@xxxxxxxxxxx
> Cc: jack_wang@xxxxxxxxx
> Cc: JBottomley@xxxxxxxxxxxxx
> Cc: crystal_yu@xxxxxxxxx
> Cc: john_gong@xxxxxxxxx
> Cc: lindar_liu <lindar_liu@xxxxxxxxx>
> 
>  drivers/scsi/pm8001/pm8001_hwi.c |  196
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
>  drivers/scsi/pm8001/pm8001_sas.c |   72 +++++++++++++++++++++
>  drivers/scsi/pm8001/pm8001_sas.h |    5 +
>  3 files changed, 261 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/scsi/pm8001/pm8001_hwi.c
> b/drivers/scsi/pm8001/pm8001_hwi.c
> index b7b92f7..faeda89 100644
> --- a/drivers/scsi/pm8001/pm8001_hwi.c
> +++ b/drivers/scsi/pm8001/pm8001_hwi.c
> @@ -607,7 +607,8 @@ static int __devinit pm8001_chip_init(struct
> pm8001_hba_info *pm8001_ha)
>  	update_inbnd_queue_table(pm8001_ha, 0);
>  	update_outbnd_queue_table(pm8001_ha, 0);
>  	mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
> -	mpi_set_open_retry_interval_reg(pm8001_ha, 7);
> +	/* 7->130ms, 34->500ms, 119->1.5s */
> +	mpi_set_open_retry_interval_reg(pm8001_ha, 119);
>  	/* notify firmware update finished and check initialization status
*/
>  	if (0 == mpi_init_check(pm8001_ha)) {
>  		PM8001_INIT_DBG(pm8001_ha,
> @@ -1388,24 +1389,191 @@ static void pm8001_work_fn(struct work_struct
*work)
>  	struct pm8001_device *pm8001_dev;
>  	struct domain_device *dev;
> 
> +	/*
> +	 * So far, all users of this stash an associated structure here.
> +	 * If we get here, and this pointer is null, then the action
> +	 * was cancelled. This nullification happens when the device
> +	 * goes away.
> +	 */
> +	pm8001_dev = pw->data; /* Most stash device structure */
> +	if ((pm8001_dev == NULL)
> +	 || ((pw->handler != IO_XFER_ERROR_BREAK)
> +	  && (pm8001_dev->dev_type == NO_DEVICE))) {
> +		kfree(pw);
> +		return;
> +	}
> +
>  	switch (pw->handler) {
> +	case IO_XFER_ERROR_BREAK:
> +	{	/* This one stashes the sas_task instead */
> +		struct sas_task *t = (struct sas_task *)pm8001_dev;
> +		u32 tag;
> +		struct pm8001_ccb_info *ccb;
> +		struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
> +		unsigned long flags, flags1;
> +		struct task_status_struct *ts;
> +		int i;
> +
> +		if (pm8001_query_task(t) == TMF_RESP_FUNC_SUCC)
> +			break; /* Task still on lu */
> +		spin_lock_irqsave(&pm8001_ha->lock, flags);
> +
> +		spin_lock_irqsave(&t->task_state_lock, flags1);
> +		if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
> +			spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			break; /* Task got completed by another */
> +		}
> +		spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +
> +		/* Search for a possible ccb that matches the task */
> +		for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
> +			ccb = &pm8001_ha->ccb_info[i];
> +			tag = ccb->ccb_tag;
> +			if ((tag != 0xFFFFFFFF) && (ccb->task == t))
> +				break;
> +		}
> +		if (!ccb) {
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			break; /* Task got freed by another */
> +		}
> +		ts = &t->task_status;
> +		ts->resp = SAS_TASK_COMPLETE;
> +		/* Force the midlayer to retry */
> +		ts->stat = SAS_QUEUE_FULL;
> +		pm8001_dev = ccb->device;
> +		if (pm8001_dev)
> +			pm8001_dev->running_req--;
> +		spin_lock_irqsave(&t->task_state_lock, flags1);
> +		t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
> +		t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
> +		t->task_state_flags |= SAS_TASK_STATE_DONE;
> +		if (unlikely((t->task_state_flags &
SAS_TASK_STATE_ABORTED))) {
> +			spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +			PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p"
> +				" done with event 0x%x resp 0x%x stat 0x%x
but"
> +				" aborted by upper layer!\n",
> +				t, pw->handler, ts->resp, ts->stat));
> +			pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +		} else {
> +			spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +			pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
> +			mb();/* in order to force CPU ordering */
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			t->task_done(t);
> +		}
> +	}	break;
> +	case IO_XFER_OPEN_RETRY_TIMEOUT:
> +	{	/* This one stashes the sas_task instead */
> +		struct sas_task *t = (struct sas_task *)pm8001_dev;
> +		u32 tag;
> +		struct pm8001_ccb_info *ccb;
> +		struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
> +		unsigned long flags, flags1;
> +		int i, ret = 0;
> +
> +		PM8001_IO_DBG(pm8001_ha,
> +			pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
> +
> +		ret = pm8001_query_task(t);
> +
> +		PM8001_IO_DBG(pm8001_ha,
> +			switch (ret) {
> +			case TMF_RESP_FUNC_SUCC:
> +				pm8001_printk("...Task on lu\n");
> +				break;
> +
> +			case TMF_RESP_FUNC_COMPLETE:
> +				pm8001_printk("...Task NOT on lu\n");
> +				break;
> +
> +			default:
> +				pm8001_printk("...query task failed!!!\n");
> +				break;
> +			});
> +
> +		spin_lock_irqsave(&pm8001_ha->lock, flags);
> +
> +		spin_lock_irqsave(&t->task_state_lock, flags1);
> +
> +		if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
> +			spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
> +				(void)pm8001_abort_task(t);
> +			break; /* Task got completed by another */
> +		}
> +
> +		spin_unlock_irqrestore(&t->task_state_lock, flags1);
> +
> +		/* Search for a possible ccb that matches the task */
> +		for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
> +			ccb = &pm8001_ha->ccb_info[i];
> +			tag = ccb->ccb_tag;
> +			if ((tag != 0xFFFFFFFF) && (ccb->task == t))
> +				break;
> +		}
> +		if (!ccb) {
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
> +				(void)pm8001_abort_task(t);
> +			break; /* Task got freed by another */
> +		}
> +
> +		pm8001_dev = ccb->device;
> +		dev = pm8001_dev->sas_device;
> +
> +		switch (ret) {
> +		case TMF_RESP_FUNC_SUCC: /* task on lu */
> +			ccb->open_retry = 1; /* Snub completion */
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			ret = pm8001_abort_task(t);
> +			ccb->open_retry = 0;
> +			switch (ret) {
> +			case TMF_RESP_FUNC_SUCC:
> +			case TMF_RESP_FUNC_COMPLETE:
> +				break;
> +			default: /* device misbehavior */
> +				ret = TMF_RESP_FUNC_FAILED;
> +				PM8001_IO_DBG(pm8001_ha,
> +					pm8001_printk("...Reset phy\n"));
> +				pm8001_I_T_nexus_reset(dev);
> +				break;
> +			}
> +			break;
> +
> +		case TMF_RESP_FUNC_COMPLETE: /* task not on lu */
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			/* Do we need to abort the task locally? */
> +			break;
> +
> +		default: /* device misbehavior */
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			ret = TMF_RESP_FUNC_FAILED;
> +			PM8001_IO_DBG(pm8001_ha,
> +				pm8001_printk("...Reset phy\n"));
> +			pm8001_I_T_nexus_reset(dev);
> +		}
> +
> +		if (ret == TMF_RESP_FUNC_FAILED)
> +			t = NULL;
> +		pm8001_open_reject_retry(pm8001_ha, t, pm8001_dev);
> +		PM8001_IO_DBG(pm8001_ha, pm8001_printk("...Complete\n"));
> +	}	break;
>  	case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
> -		pm8001_dev = pw->data;
>  		dev = pm8001_dev->sas_device;
>  		pm8001_I_T_nexus_reset(dev);
>  		break;
>  	case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
> -		pm8001_dev = pw->data;
>  		dev = pm8001_dev->sas_device;
>  		pm8001_I_T_nexus_reset(dev);
>  		break;
>  	case IO_DS_IN_ERROR:
> -		pm8001_dev = pw->data;
>  		dev = pm8001_dev->sas_device;
>  		pm8001_I_T_nexus_reset(dev);
>  		break;
>  	case IO_DS_NON_OPERATIONAL:
> -		pm8001_dev = pw->data;
>  		dev = pm8001_dev->sas_device;
>  		pm8001_I_T_nexus_reset(dev);
>  		break;
> @@ -1460,6 +1628,11 @@ mpi_ssp_completion(struct pm8001_hba_info
*pm8001_ha ,
> void *piomb)
>  	status = le32_to_cpu(psspPayload->status);
>  	tag = le32_to_cpu(psspPayload->tag);
>  	ccb = &pm8001_ha->ccb_info[tag];
> +	if ((status == IO_ABORTED) && ccb->open_retry) {
> +		/* Being completed by another */
> +		ccb->open_retry = 0;
> +		return;
> +	}
>  	pm8001_dev = ccb->device;
>  	param = le32_to_cpu(psspPayload->param);
> 
> @@ -1515,6 +1688,8 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha
,
> void *piomb)
>  			pm8001_printk("IO_XFER_ERROR_BREAK\n"));
>  		ts->resp = SAS_TASK_COMPLETE;
>  		ts->stat = SAS_OPEN_REJECT;
> +		/* Force the midlayer to retry */
> +		ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
>  		break;
>  	case IO_XFER_ERROR_PHY_NOT_READY:
>  		PM8001_IO_DBG(pm8001_ha,
> @@ -1719,9 +1894,8 @@ static void mpi_ssp_event(struct pm8001_hba_info
> *pm8001_ha , void *piomb)
>  	case IO_XFER_ERROR_BREAK:
>  		PM8001_IO_DBG(pm8001_ha,
>  			pm8001_printk("IO_XFER_ERROR_BREAK\n"));
> -		ts->resp = SAS_TASK_COMPLETE;
> -		ts->stat = SAS_INTERRUPTED;
> -		break;
> +		pm8001_handle_event(pm8001_ha, t, IO_XFER_ERROR_BREAK);
> +		return;
>  	case IO_XFER_ERROR_PHY_NOT_READY:
>  		PM8001_IO_DBG(pm8001_ha,
>  			pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
> @@ -1800,10 +1974,8 @@ static void mpi_ssp_event(struct pm8001_hba_info
> *pm8001_ha , void *piomb)
>  	case IO_XFER_OPEN_RETRY_TIMEOUT:
>  		PM8001_IO_DBG(pm8001_ha,
>  			pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
> -		ts->resp = SAS_TASK_COMPLETE;
> -		ts->stat = SAS_OPEN_REJECT;
> -		ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
> -		break;
> +		pm8001_handle_event(pm8001_ha, t,
IO_XFER_OPEN_RETRY_TIMEOUT);
> +		return;
>  	case IO_XFER_ERROR_UNEXPECTED_PHASE:
>  		PM8001_IO_DBG(pm8001_ha,
>  			pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
> diff --git a/drivers/scsi/pm8001/pm8001_sas.c
> b/drivers/scsi/pm8001/pm8001_sas.c
> index fb3dc99..c8869c0 100644
> --- a/drivers/scsi/pm8001/pm8001_sas.c
> +++ b/drivers/scsi/pm8001/pm8001_sas.c
> @@ -516,6 +516,7 @@ void pm8001_ccb_task_free(struct pm8001_hba_info
> *pm8001_ha,
>  	task->lldd_task = NULL;
>  	ccb->task = NULL;
>  	ccb->ccb_tag = 0xFFFFFFFF;
> +	ccb->open_retry = 0;
>  	pm8001_ccb_free(pm8001_ha, ccb_idx);
>  }
> 
> @@ -860,6 +861,77 @@ static int pm8001_issue_ssp_tmf(struct domain_device
> *dev,
>  		tmf);
>  }
> 
> +/* retry commands by ha, by task and/or by device */
> +void pm8001_open_reject_retry(
> +	struct pm8001_hba_info *pm8001_ha,
> +	struct sas_task *task_to_close,
> +	struct pm8001_device *device_to_close)
> +{
> +	int i;
> +	unsigned long flags;
> +
> +	if (pm8001_ha == NULL)
> +		return;
> +
> +	spin_lock_irqsave(&pm8001_ha->lock, flags);
> +
> +	for (i = 0; i < PM8001_MAX_CCB; i++) {
> +		struct sas_task *task;
> +		struct task_status_struct *ts;
> +		struct pm8001_device *pm8001_dev;
> +		unsigned long flags1;
> +		u32 tag;
> +		struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[i];
> +
> +		pm8001_dev = ccb->device;
> +		if (!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE))
> +			continue;
> +		if (!device_to_close) {
> +			uintptr_t d = (uintptr_t)pm8001_dev
> +					- (uintptr_t)&pm8001_ha->devices;
> +			if (((d % sizeof(*pm8001_dev)) != 0)
> +			 || ((d / sizeof(*pm8001_dev)) >=
PM8001_MAX_DEVICES))
> +				continue;
> +		} else if (pm8001_dev != device_to_close)
> +			continue;
> +		tag = ccb->ccb_tag;
> +		if (!tag || (tag == 0xFFFFFFFF))
> +			continue;
> +		task = ccb->task;
> +		if (!task || !task->task_done)
> +			continue;
> +		if (task_to_close && (task != task_to_close))
> +			continue;
> +		ts = &task->task_status;
> +		ts->resp = SAS_TASK_COMPLETE;
> +		/* Force the midlayer to retry */
> +		ts->stat = SAS_OPEN_REJECT;
> +		ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
> +		if (pm8001_dev)
> +			pm8001_dev->running_req--;
> +		spin_lock_irqsave(&task->task_state_lock, flags1);
> +		task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
> +		task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
> +		task->task_state_flags |= SAS_TASK_STATE_DONE;
> +		if (unlikely((task->task_state_flags
> +				& SAS_TASK_STATE_ABORTED))) {
> +			spin_unlock_irqrestore(&task->task_state_lock,
> +				flags1);
> +			pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
> +		} else {
> +			spin_unlock_irqrestore(&task->task_state_lock,
> +				flags1);
> +			pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
> +			mb();/* in order to force CPU ordering */
> +			spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +			task->task_done(task);
> +			spin_lock_irqsave(&pm8001_ha->lock, flags);
> +		}
> +	}
> +
> +	spin_unlock_irqrestore(&pm8001_ha->lock, flags);
> +}
> +
>  /**
>    * Standard mandates link reset for ATA  (type 0) and hard reset for
>    * SSP (type 1) , only for RECOVERY
> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
> b/drivers/scsi/pm8001/pm8001_sas.h
> index 93959fe..43cd49b 100644
> --- a/drivers/scsi/pm8001/pm8001_sas.h
> +++ b/drivers/scsi/pm8001/pm8001_sas.h
> @@ -235,6 +235,7 @@ struct pm8001_ccb_info {
>  	struct pm8001_device	*device;
>  	struct pm8001_prd	buf_prd[PM8001_MAX_DMA_SG];
>  	struct fw_control_ex	*fw_control_context;
> +	u8			open_retry;
>  };
> 
>  struct mpi_mem {
> @@ -484,6 +485,10 @@ void pm8001_dev_gone(struct domain_device *dev);
>  int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
>  int pm8001_I_T_nexus_reset(struct domain_device *dev);
>  int pm8001_query_task(struct sas_task *task);
> +void pm8001_open_reject_retry(
> +	struct pm8001_hba_info *pm8001_ha,
> +	struct sas_task *task_to_close,
> +	struct pm8001_device *device_to_close);
>  int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
>  	dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
>  	u32 mem_size, u32 align);
> 
> ______________________________________________________________________
> This email may contain privileged or confidential information, which
should
> only be used for the purpose for which it was sent by Xyratex. No further
rights
> or licenses are granted to use such information. If you are not the
intended
> recipient of this message, please notify the sender by return and delete
it.
> You may not use, copy, disclose or rely on the information contained in
it.
> 
> Internet email is susceptible to data corruption, interception and
> unauthorised amendment for which Xyratex does not accept liability. While
we
> have taken reasonable precautions to ensure that this email is free of
viruses,
> Xyratex does not accept liability for the presence of any computer viruses
in
> this email, nor for any losses caused as a result of viruses.
> 
> Xyratex Technology Limited (03134912), Registered in England & Wales,
> Registered Office, Langstone Road, Havant, Hampshire, PO9 1SA.
> 
> The Xyratex group of companies also includes, Xyratex Ltd, registered in
> Bermuda, Xyratex International Inc, registered in California, Xyratex
> (Malaysia) Sdn Bhd registered in Malaysia, Xyratex Technology (Wuxi) Co
Ltd
> registered in The People's Republic of China and Xyratex Japan Limited
> registered in Japan.
> ______________________________________________________________________
> 

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


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux