Commit 98145cb7 authored by Jeff Skirvin's avatar Jeff Skirvin Committed by James Bottomley
Browse files

[SCSI] isci: Fix task management for SMP, SATA and on dev remove.



libsas uses the LLDD abort task interface to handle I/O timeouts
in the SATA/STP and SMP discovery paths, so this change will terminate
STP/SMP requests. Also, if the device is gone, the lldd will prevent
libsas from further escalations in the error handler.

Signed-off-by: default avatarJeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent db49c2d0
Loading
Loading
Loading
Loading
+0 −47
Original line number Diff line number Diff line
@@ -1438,53 +1438,6 @@ int isci_remote_device_found(struct domain_device *domain_dev)

	return status == SCI_SUCCESS ? 0 : -ENODEV;
}
/**
 * isci_device_is_reset_pending() - This function will check if there is any
 *    pending reset condition on the device.
 * @request: This parameter is the isci_device object.
 *
 * true if there is a reset pending for the device.
 */
bool isci_device_is_reset_pending(
	struct isci_host *isci_host,
	struct isci_remote_device *isci_device)
{
	struct isci_request *isci_request;
	struct isci_request *tmp_req;
	bool reset_is_pending = false;
	unsigned long flags;

	dev_dbg(&isci_host->pdev->dev,
		"%s: isci_device = %p\n", __func__, isci_device);

	spin_lock_irqsave(&isci_host->scic_lock, flags);

	/* Check for reset on all pending requests. */
	list_for_each_entry_safe(isci_request, tmp_req,
				 &isci_device->reqs_in_process, dev_node) {
		dev_dbg(&isci_host->pdev->dev,
			"%s: isci_device = %p request = %p\n",
			__func__, isci_device, isci_request);

		if (isci_request->ttype == io_task) {
			struct sas_task *task = isci_request_access_task(
				isci_request);

			spin_lock(&task->task_state_lock);
			if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
				reset_is_pending = true;
			spin_unlock(&task->task_state_lock);
		}
	}

	spin_unlock_irqrestore(&isci_host->scic_lock, flags);

	dev_dbg(&isci_host->pdev->dev,
		"%s: isci_device = %p reset_is_pending = %d\n",
		__func__, isci_device, reset_is_pending);

	return reset_is_pending;
}

/**
 * isci_device_clear_reset_pending() - This function will clear if any pending
+0 −2
Original line number Diff line number Diff line
@@ -132,8 +132,6 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost,
				      struct isci_remote_device *idev);
void isci_remote_device_gone(struct domain_device *domain_dev);
int isci_remote_device_found(struct domain_device *domain_dev);
bool isci_device_is_reset_pending(struct isci_host *ihost,
				  struct isci_remote_device *idev);
void isci_device_clear_reset_pending(struct isci_host *ihost,
				     struct isci_remote_device *idev);
/**
+64 −105
Original line number Diff line number Diff line
@@ -920,22 +920,14 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
		"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
		 __func__, domain_device, isci_host, isci_device);

	if (isci_device)
		set_bit(IDEV_EH, &isci_device->flags);
	if (!isci_device) {
		/* If the device is gone, stop the escalations. */
		dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__);

	/* If there is a device reset pending on any request in the
	 * device's list, fail this LUN reset request in order to
	 * escalate to the device reset.
	 */
	if (!isci_device ||
	    isci_device_is_reset_pending(isci_host, isci_device)) {
		dev_dbg(&isci_host->pdev->dev,
			 "%s: No dev (%p), or "
			 "RESET PENDING: domain_device=%p\n",
			 __func__, isci_device, domain_device);
		ret = TMF_RESP_FUNC_FAILED;
		ret = TMF_RESP_FUNC_COMPLETE;
		goto out;
	}
	set_bit(IDEV_EH, &isci_device->flags);

	/* Send the task management part of the reset. */
	if (sas_protocol_ata(domain_device->tproto)) {
@@ -1044,7 +1036,7 @@ int isci_task_abort_task(struct sas_task *task)
	struct isci_tmf           tmf;
	int                       ret = TMF_RESP_FUNC_FAILED;
	unsigned long             flags;
	bool                      any_dev_reset = false;
	int                       perform_termination = 0;

	/* Get the isci_request reference from the task.  Note that
	 * this check does not depend on the pending request list
@@ -1066,78 +1058,26 @@ int isci_task_abort_task(struct sas_task *task)
	spin_unlock_irqrestore(&isci_host->scic_lock, flags);

	dev_dbg(&isci_host->pdev->dev,
		"%s: task = %p\n", __func__, task);

	if (!isci_device || !old_request)
		goto out;
		"%s: dev = %p, task = %p, old_request == %p\n",
		__func__, isci_device, task, old_request);

	if (isci_device)
		set_bit(IDEV_EH, &isci_device->flags);

	/* This version of the driver will fail abort requests for
	 * SATA/STP.  Failing the abort request this way will cause the
	 * SCSI error handler thread to escalate to LUN reset
	 */
	if (sas_protocol_ata(task->task_proto)) {
		dev_dbg(&isci_host->pdev->dev,
			    " task %p is for a STP/SATA device;"
			    " returning TMF_RESP_FUNC_FAILED\n"
			    " to cause a LUN reset...\n", task);
		goto out;
	}

	dev_dbg(&isci_host->pdev->dev,
		"%s: old_request == %p\n", __func__, old_request);

	any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device);

	spin_lock_irqsave(&task->task_state_lock, flags);

	any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET);

	/* If the extraction of the request reference from the task
	 * failed, then the request has been completed (or if there is a
	 * pending reset then this abort request function must be failed
	 * in order to escalate to the target reset).
	 */
	if ((old_request == NULL) || any_dev_reset) {

		/* If the device reset task flag is set, fail the task
		 * management request.  Otherwise, the original request
		 * has completed.
		 */
		if (any_dev_reset) {

			/* Turn off the task's DONE to make sure this
			 * task is escalated to a target reset.
			 */
			task->task_state_flags &= ~SAS_TASK_STATE_DONE;

			/* Make the reset happen as soon as possible. */
			task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;

			spin_unlock_irqrestore(&task->task_state_lock, flags);

			/* Fail the task management request in order to
			 * escalate to the target reset.
	/* Device reset conditions signalled in task_state_flags are the
	 * responsbility of libsas to observe at the start of the error
	 * handler thread.
	 */
			ret = TMF_RESP_FUNC_FAILED;

			dev_dbg(&isci_host->pdev->dev,
				"%s: Failing task abort in order to "
				"escalate to target reset because\n"
				"SAS_TASK_NEED_DEV_RESET is set for "
				"task %p on dev %p\n",
				__func__, task, isci_device);


		} else {
	if (!isci_device || !old_request) {
		/* The request has already completed and there
		* is nothing to do here other than to set the task
		* done bit, and indicate that the task abort function
		* was sucessful.
		*/
			isci_set_task_doneflags(task);

		spin_lock_irqsave(&task->task_state_lock, flags);
		task->task_state_flags |= SAS_TASK_STATE_DONE;
		task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
					    SAS_TASK_STATE_PENDING);
		spin_unlock_irqrestore(&task->task_state_lock, flags);

		ret = TMF_RESP_FUNC_COMPLETE;
@@ -1145,10 +1085,7 @@ int isci_task_abort_task(struct sas_task *task)
		dev_dbg(&isci_host->pdev->dev,
			"%s: abort task not needed for %p\n",
			__func__, task);
		}
		goto out;
	} else {
		spin_unlock_irqrestore(&task->task_state_lock, flags);
	}

	spin_lock_irqsave(&isci_host->scic_lock, flags);
@@ -1177,24 +1114,44 @@ int isci_task_abort_task(struct sas_task *task)
		goto out;
	}
	if (task->task_proto == SAS_PROTOCOL_SMP ||
	    sas_protocol_ata(task->task_proto) ||
	    test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {

		spin_unlock_irqrestore(&isci_host->scic_lock, flags);

		dev_dbg(&isci_host->pdev->dev,
			"%s: SMP request (%d)"
			"%s: %s request"
			" or complete_in_target (%d), thus no TMF\n",
			__func__, (task->task_proto == SAS_PROTOCOL_SMP),
			__func__,
			((task->task_proto == SAS_PROTOCOL_SMP)
				? "SMP"
				: (sas_protocol_ata(task->task_proto)
					? "SATA/STP"
					: "<other>")
			 ),
			test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));

		/* Set the state on the task. */
		isci_task_all_done(task);

		if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
			spin_lock_irqsave(&task->task_state_lock, flags);
			task->task_state_flags |= SAS_TASK_STATE_DONE;
			task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
						    SAS_TASK_STATE_PENDING);
			spin_unlock_irqrestore(&task->task_state_lock, flags);
			ret = TMF_RESP_FUNC_COMPLETE;
		} else {
			spin_lock_irqsave(&task->task_state_lock, flags);
			task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
						    SAS_TASK_STATE_PENDING);
			spin_unlock_irqrestore(&task->task_state_lock, flags);
		}

		/* Stopping and SMP devices are not sent a TMF, and are not
		 * reset, but the outstanding I/O request is terminated below.
		/* STP and SMP devices are not sent a TMF, but the
		 * outstanding I/O request is terminated below.  This is
		 * because SATA/STP and SMP discovery path timeouts directly
		 * call the abort task interface for cleanup.
		 */
		perform_termination = 1;

	} else {
		/* Fill in the tmf stucture */
		isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
@@ -1203,22 +1160,24 @@ int isci_task_abort_task(struct sas_task *task)

		spin_unlock_irqrestore(&isci_host->scic_lock, flags);

		#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */
		#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
		ret = isci_task_execute_tmf(isci_host, isci_device, &tmf,
					    ISCI_ABORT_TASK_TIMEOUT_MS);

		if (ret != TMF_RESP_FUNC_COMPLETE)
		if (ret == TMF_RESP_FUNC_COMPLETE)
			perform_termination = 1;
		else
			dev_dbg(&isci_host->pdev->dev,
				"%s: isci_task_send_tmf failed\n",
				__func__);
				"%s: isci_task_send_tmf failed\n", __func__);
	}
	if (ret == TMF_RESP_FUNC_COMPLETE) {
	if (perform_termination) {
		set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags);

		/* Clean up the request on our side, and wait for the aborted
		 * I/O to complete.
		 */
		isci_terminate_request_core(isci_host, isci_device, old_request);
		isci_terminate_request_core(isci_host, isci_device,
					    old_request);
	}

	/* Make sure we do not leave a reference to aborted_io_completion */
+3 −30
Original line number Diff line number Diff line
@@ -226,35 +226,6 @@ enum isci_completion_selection {
	isci_perform_error_io_completion        /* Use sas_task_abort */
};

static inline void isci_set_task_doneflags(
	struct sas_task *task)
{
	/* Since no futher action will be taken on this task,
	 * make sure to mark it complete from the lldd perspective.
	 */
	task->task_state_flags |= SAS_TASK_STATE_DONE;
	task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
	task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
}
/**
 * isci_task_all_done() - This function clears the task bits to indicate the
 *    LLDD is done with the task.
 *
 *
 */
static inline void isci_task_all_done(
	struct sas_task *task)
{
	unsigned long flags;

	/* Since no futher action will be taken on this task,
	 * make sure to mark it complete from the lldd perspective.
	 */
	spin_lock_irqsave(&task->task_state_lock, flags);
	isci_set_task_doneflags(task);
	spin_unlock_irqrestore(&task->task_state_lock, flags);
}

/**
 * isci_task_set_completion_status() - This function sets the completion status
 *    for the request.
@@ -336,7 +307,9 @@ isci_task_set_completion_status(
		/* Fall through to the normal case... */
	case isci_perform_normal_io_completion:
		/* Normal notification (task_done) */
		isci_set_task_doneflags(task);
		task->task_state_flags |= SAS_TASK_STATE_DONE;
		task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
					    SAS_TASK_STATE_PENDING);
		break;
	default:
		WARN_ONCE(1, "unknown task_notification_selection: %d\n",