isci: Terminate dev requests on FIS err bit rx in NCQ
When the remote device transitions to a not-ready state because of
an NCQ error condition, all outstanding requests to that device
are terminated and completed to libsas on the normal path. The
device then waits for a READ LOG EXT command to issue on the task
management path.
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 9f45c2b..c5ce0f0 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -68,17 +68,39 @@
* @isci_host: This parameter specifies the isci host object.
* @isci_device: This parameter specifies the remote device
*
+ * scic_lock is held on entrance to this function.
*/
static void isci_remote_device_not_ready(struct isci_host *ihost,
struct isci_remote_device *idev, u32 reason)
{
+ struct isci_request * ireq;
+
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p\n", __func__, idev);
- if (reason == SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED)
+ switch (reason) {
+ case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
set_bit(IDEV_GONE, &idev->flags);
- else
+ break;
+ case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
+ set_bit(IDEV_IO_NCQERROR, &idev->flags);
+
+ /* Kill all outstanding requests for the device. */
+ list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) {
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p request = %p\n",
+ __func__, idev, ireq);
+
+ scic_controller_terminate_request(&ihost->sci,
+ &idev->sci,
+ &ireq->sci);
+ }
+ /* Fall through into the default case... */
+ default:
clear_bit(IDEV_IO_READY, &idev->flags);
+ break;
+ }
}
/**
@@ -94,6 +116,7 @@
dev_dbg(&ihost->pdev->dev,
"%s: idev = %p\n", __func__, idev);
+ clear_bit(IDEV_IO_NCQERROR, &idev->flags);
set_bit(IDEV_IO_READY, &idev->flags);
if (test_and_clear_bit(IDEV_START_PENDING, &idev->flags))
wake_up(&ihost->eventq);
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index cde5950..0d9e37f 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -136,6 +136,7 @@
#define IDEV_EH 3
#define IDEV_GONE 4
#define IDEV_IO_READY 5
+ #define IDEV_IO_NCQERROR 6
unsigned long flags;
struct kref kref;
struct isci_port *isci_port;
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 1043fed..08a7340b 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -3587,9 +3587,30 @@
spin_lock_irqsave(&ihost->scic_lock, flags);
- /* send the request, let the core assign the IO TAG. */
- status = scic_controller_start_io(&ihost->sci, &idev->sci, &ireq->sci,
- SCI_CONTROLLER_INVALID_IO_TAG);
+ if (test_bit(IDEV_IO_NCQERROR, &idev->flags)) {
+
+ if (isci_task_is_ncq_recovery(task)) {
+
+ /* The device is in an NCQ recovery state. Issue the
+ * request on the task side. Note that it will
+ * complete on the I/O request side because the
+ * request was built that way (ie.
+ * ireq->is_task_management_request is false).
+ */
+ status = scic_controller_start_task(&ihost->sci,
+ &idev->sci,
+ &ireq->sci,
+ SCI_CONTROLLER_INVALID_IO_TAG);
+ } else {
+ status = SCI_FAILURE;
+ }
+ } else {
+
+ /* send the request, let the core assign the IO TAG. */
+ status = scic_controller_start_io(&ihost->sci, &idev->sci,
+ &ireq->sci,
+ SCI_CONTROLLER_INVALID_IO_TAG);
+ }
if (status != SCI_SUCCESS &&
status != SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) {
dev_warn(&ihost->pdev->dev,
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index 7c8b59a..9130f22 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -687,4 +687,13 @@
void
scic_stp_io_request_set_ncq_tag(struct scic_sds_request *sci_req, u16 ncq_tag);
void scic_sds_smp_request_copy_response(struct scic_sds_request *sci_req);
+
+static inline int isci_task_is_ncq_recovery(struct sas_task *task)
+{
+ return (sas_protocol_ata(task->task_proto) &&
+ task->ata_task.fis.command == ATA_CMD_READ_LOG_EXT &&
+ task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ);
+
+}
+
#endif /* !defined(_ISCI_REQUEST_H_) */
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 0835a2c..157e997 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -133,6 +133,15 @@
for (; num > 0; num--,\
task = list_entry(task->list.next, struct sas_task, list))
+
+static inline int isci_device_io_ready(struct isci_remote_device *idev,
+ struct sas_task *task)
+{
+ return idev ? test_bit(IDEV_IO_READY, &idev->flags) ||
+ (test_bit(IDEV_IO_NCQERROR, &idev->flags) &&
+ isci_task_is_ncq_recovery(task))
+ : 0;
+}
/**
* isci_task_execute_task() - This function is one of the SAS Domain Template
* functions. This function is called by libsas to send a task down to
@@ -165,7 +174,7 @@
for_each_sas_task(num, task) {
spin_lock_irqsave(&ihost->scic_lock, flags);
idev = isci_lookup_device(task->dev);
- io_ready = idev ? test_bit(IDEV_IO_READY, &idev->flags) : 0;
+ io_ready = isci_device_io_ready(idev, task);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
dev_dbg(&ihost->pdev->dev,
@@ -178,6 +187,7 @@
SAS_DEVICE_UNKNOWN);
isci_host_can_dequeue(ihost, 1);
} else if (!io_ready) {
+
/* Indicate QUEUE_FULL so that the scsi midlayer
* retries.
*/
@@ -299,7 +309,9 @@
/* sanity check, return TMF_RESP_FUNC_FAILED
* if the device is not there and ready.
*/
- if (!isci_device || !test_bit(IDEV_IO_READY, &isci_device->flags)) {
+ if (!isci_device ||
+ (!test_bit(IDEV_IO_READY, &isci_device->flags) &&
+ !test_bit(IDEV_IO_NCQERROR, &isci_device->flags))) {
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p not ready (%#lx)\n",
__func__,