isci: Manage device suspensions during TC terminations.
TCs must be terminated only while the RNC is suspended. This commit
adds remote device suspensions and resumptions in the abort, reset and
termination paths.
Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index 9f03877..4f76dcd 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -143,6 +143,12 @@
NULL, NULL);
}
+static int isci_remote_device_suspendcheck(struct isci_remote_device *idev)
+{
+ return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+ || !test_bit(IDEV_ALLOCATED, &idev->flags);
+}
+
enum sci_status isci_remote_device_suspend(
struct isci_host *ihost,
struct isci_remote_device *idev)
@@ -151,18 +157,18 @@
unsigned long flags;
spin_lock_irqsave(&ihost->scic_lock, flags);
-
- if (isci_lookup_device(idev->domain_dev) == NULL) {
+ if (isci_get_device(idev->domain_dev) == NULL) {
spin_unlock_irqrestore(&ihost->scic_lock, flags);
status = SCI_FAILURE;
} else {
status = sci_remote_device_suspend(idev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
if (status == SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: idev=%p, about to wait\n",
+ __func__, idev);
wait_event(ihost->eventq,
- test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
- || !test_bit(IDEV_ALLOCATED, &idev->flags));
-
+ isci_remote_device_suspendcheck(idev));
status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
? SCI_SUCCESS : SCI_FAILURE;
dev_dbg(&ihost->pdev->dev,
@@ -171,7 +177,10 @@
test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
? "<suspended>" : "<deallocated!>");
- }
+ } else
+ dev_dbg(scirdev_to_dev(idev),
+ "%s: sci_remote_device_suspend failed, "
+ "status = %d\n", __func__, status);
isci_put_device(idev);
}
return status;
@@ -1218,6 +1227,35 @@
return SCI_SUCCESS;
}
+enum sci_status sci_remote_device_resume(
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ enum sci_status status;
+
+ status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p);
+ if (status != SCI_SUCCESS)
+ dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n",
+ __func__, status);
+ return status;
+}
+
+enum sci_status isci_remote_device_resume(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ unsigned long flags;
+ enum sci_status status;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ status = sci_remote_device_resume(idev, cb_fn, cb_p);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return status;
+}
/**
* sci_remote_device_start() - This method will start the supplied remote
* device. This method enables normal IO requests to flow through to the
@@ -1244,9 +1282,8 @@
return SCI_FAILURE_INVALID_STATE;
}
- status = sci_remote_node_context_resume(&idev->rnc,
- remote_device_resume_done,
- idev);
+ status = sci_remote_device_resume(idev, remote_device_resume_done,
+ idev);
if (status != SCI_SUCCESS)
return status;
@@ -1461,20 +1498,15 @@
}
enum sci_status isci_remote_device_reset(
+ struct isci_host *ihost,
struct isci_remote_device *idev)
{
- struct isci_host *ihost = dev_to_ihost(idev->domain_dev);
unsigned long flags;
enum sci_status status;
- /* Wait for the device suspend. */
- status = isci_remote_device_suspend(ihost, idev);
- if (status != SCI_SUCCESS) {
- dev_dbg(&ihost->pdev->dev,
- "%s: isci_remote_device_suspend(%p) returned %d!\n",
- __func__, idev, status);
- return status;
- }
+ /* Put the device into a reset state so the suspension will not
+ * automatically resume.
+ */
spin_lock_irqsave(&ihost->scic_lock, flags);
status = sci_remote_device_reset(idev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
@@ -1482,6 +1514,14 @@
dev_dbg(&ihost->pdev->dev,
"%s: sci_remote_device_reset(%p) returned %d!\n",
__func__, idev, status);
+ return status;
+ }
+ /* Wait for the device suspend. */
+ status = isci_remote_device_suspend(ihost, idev);
+ if (status != SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_remote_device_suspend(%p) returned %d!\n",
+ __func__, idev, status);
}
return status;
}
@@ -1497,3 +1537,19 @@
{
return sci_remote_device_terminate_requests_checkabort(idev, 1);
}
+
+enum sci_status isci_remote_device_reset_complete(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ unsigned long flags;
+ enum sci_status status;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ status = sci_remote_device_reset_complete(idev);
+ sci_remote_device_resume(idev, NULL, NULL);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return status;
+}
+
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index ae508ee..a6a376e 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -106,6 +106,16 @@
#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000
/* device reference routines must be called under sci_lock */
+static inline struct isci_remote_device *isci_get_device(
+ struct domain_device *dev)
+{
+ struct isci_remote_device *idev = dev->lldd_dev;
+
+ if (idev)
+ kref_get(&idev->kref);
+ return idev;
+}
+
static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev)
{
struct isci_remote_device *idev = dev->lldd_dev;
@@ -345,4 +355,27 @@
enum sci_status
sci_remote_device_abort_requests_pending_abort(
struct isci_remote_device *idev);
+
+enum sci_status isci_remote_device_suspend(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
+
+enum sci_status sci_remote_device_resume(
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p);
+
+enum sci_status isci_remote_device_resume(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p);
+
+enum sci_status isci_remote_device_reset(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
+
+enum sci_status isci_remote_device_reset_complete(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 374254e..9b8632f 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -716,6 +716,8 @@
unsigned long flags;
LIST_HEAD(list);
+ isci_remote_device_suspend(ihost, idev);
+
spin_lock_irqsave(&ihost->scic_lock, flags);
list_splice_init(&idev->reqs_in_process, &list);
@@ -826,40 +828,47 @@
int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
{
- struct isci_host *isci_host = dev_to_ihost(dev);
+ struct isci_host *ihost = dev_to_ihost(dev);
struct isci_remote_device *isci_device;
unsigned long flags;
int ret;
- spin_lock_irqsave(&isci_host->scic_lock, flags);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
isci_device = isci_lookup_device(dev);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
+ dev_dbg(&ihost->pdev->dev,
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
- __func__, dev, isci_host, isci_device);
+ __func__, dev, ihost, isci_device);
if (!isci_device) {
/* If the device is gone, stop the escalations. */
- dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__);
+ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
+ if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: device = %p; failed to suspend\n",
+ __func__, isci_device);
+ ret = TMF_RESP_FUNC_FAILED;
+ goto out;
+ }
/* Send the task management part of the reset. */
if (dev_is_sata(dev)) {
sas_ata_schedule_reset(dev);
ret = TMF_RESP_FUNC_COMPLETE;
} else
- ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun);
+ ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun);
/* If the LUN reset worked, all the I/O can now be terminated. */
- if (ret == TMF_RESP_FUNC_COMPLETE)
+ if (ret == TMF_RESP_FUNC_COMPLETE) {
/* Terminate all I/O now. */
- isci_terminate_pending_requests(isci_host,
- isci_device);
-
+ isci_terminate_pending_requests(ihost, isci_device);
+ isci_remote_device_resume(ihost, isci_device, NULL, NULL);
+ }
out:
isci_put_device(isci_device);
return ret;
@@ -976,7 +985,7 @@
spin_unlock(&task->task_state_lock);
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
+ dev_warn(&isci_host->pdev->dev,
"%s: dev = %p, task = %p, old_request == %p\n",
__func__, isci_device, task, old_request);
@@ -998,7 +1007,7 @@
ret = TMF_RESP_FUNC_COMPLETE;
- dev_dbg(&isci_host->pdev->dev,
+ dev_warn(&isci_host->pdev->dev,
"%s: abort task not needed for %p\n",
__func__, task);
goto out;
@@ -1022,7 +1031,7 @@
/* The request was already being handled by someone else (because
* they got to set the state away from started).
*/
- dev_dbg(&isci_host->pdev->dev,
+ dev_warn(&isci_host->pdev->dev,
"%s: device = %p; old_request %p already being aborted\n",
__func__,
isci_device, old_request);
@@ -1035,7 +1044,7 @@
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
+ dev_warn(&isci_host->pdev->dev,
"%s: %s request"
" or complete_in_target (%d), thus no TMF\n",
__func__,
@@ -1068,6 +1077,15 @@
*/
perform_termination = 1;
+ if (isci_device && !test_bit(IDEV_GONE, &isci_device->flags) &&
+ (isci_remote_device_suspend(isci_host, isci_device)
+ != SCI_SUCCESS)) {
+ dev_warn(&isci_host->pdev->dev,
+ "%s: device = %p; failed to suspend\n",
+ __func__, isci_device);
+ goto out;
+ }
+
} else {
/* Fill in the tmf stucture */
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
@@ -1076,6 +1094,14 @@
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ if (isci_remote_device_suspend(isci_host, isci_device)
+ != SCI_SUCCESS) {
+ dev_warn(&isci_host->pdev->dev,
+ "%s: device = %p; failed to suspend\n",
+ __func__, isci_device);
+ goto out;
+ }
+
#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);
@@ -1083,7 +1109,7 @@
if (ret == TMF_RESP_FUNC_COMPLETE)
perform_termination = 1;
else
- dev_dbg(&isci_host->pdev->dev,
+ dev_warn(&isci_host->pdev->dev,
"%s: isci_task_send_tmf failed\n", __func__);
}
if (perform_termination) {
@@ -1094,6 +1120,7 @@
*/
isci_terminate_request_core(isci_host, isci_device,
old_request);
+ isci_remote_device_resume(isci_host, isci_device, NULL, NULL);
}
/* Make sure we do not leave a reference to aborted_io_completion */
@@ -1251,21 +1278,13 @@
struct isci_remote_device *idev)
{
int rc;
- unsigned long flags;
enum sci_status status;
struct sas_phy *phy = sas_get_local_phy(dev);
struct isci_port *iport = dev->port->lldd_port;
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
- spin_lock_irqsave(&ihost->scic_lock, flags);
- status = sci_remote_device_reset(idev);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- if (status != SCI_SUCCESS) {
- dev_dbg(&ihost->pdev->dev,
- "%s: sci_remote_device_reset(%p) returned %d!\n",
- __func__, idev, status);
+ if (isci_remote_device_reset(ihost, idev) != SCI_SUCCESS) {
rc = TMF_RESP_FUNC_FAILED;
goto out;
}
@@ -1281,15 +1300,12 @@
isci_remote_device_nuke_requests(ihost, idev);
/* Since all pending TCs have been cleaned, resume the RNC. */
- spin_lock_irqsave(&ihost->scic_lock, flags);
- status = sci_remote_device_reset_complete(idev);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ status = isci_remote_device_reset_complete(ihost, idev);
- if (status != SCI_SUCCESS) {
+ if (status != SCI_SUCCESS)
dev_dbg(&ihost->pdev->dev,
- "%s: sci_remote_device_reset_complete(%p) "
+ "%s: isci_remote_device_reset_complete(%p) "
"returned %d!\n", __func__, idev, status);
- }
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev);
out:
@@ -1305,7 +1321,7 @@
int ret;
spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(dev);
+ idev = isci_get_device(dev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
if (!idev) {