#include "request.h"
#include "sata.h"
#include "task.h"
+#include "host.h"
/**
* isci_task_refuse() - complete the request to the upper layer driver in
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
{
struct isci_host *ihost = dev_to_ihost(task->dev);
struct isci_remote_device *idev;
- enum sci_status status;
unsigned long flags;
bool io_ready;
- int ret;
+ u16 tag;
dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num);
- /* Check if we have room for more tasks */
- ret = isci_host_can_queue(ihost, num);
-
- if (ret) {
- dev_warn(&ihost->pdev->dev, "%s: queue full\n", __func__);
- return ret;
- }
-
for_each_sas_task(num, task) {
+ enum sci_status status = SCI_FAILURE;
+
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);
+ tag = isci_alloc_tag(ihost);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
dev_dbg(&ihost->pdev->dev,
if (!idev) {
isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
SAS_DEVICE_UNKNOWN);
- isci_host_can_dequeue(ihost, 1);
- } else if (!io_ready) {
+ } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
/* Indicate QUEUE_FULL so that the scsi midlayer
* retries.
*/
isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
SAS_QUEUE_FULL);
- isci_host_can_dequeue(ihost, 1);
} else {
/* There is a device and it's ready for I/O. */
spin_lock_irqsave(&task->task_state_lock, flags);
isci_task_refuse(ihost, task,
SAS_TASK_UNDELIVERED,
SAM_STAT_TASK_ABORTED);
- isci_host_can_dequeue(ihost, 1);
} else {
task->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock_irqrestore(&task->task_state_lock, flags);
/* build and send the request. */
- status = isci_request_execute(ihost, idev, task, gfp_flags);
+ status = isci_request_execute(ihost, idev, task, tag, gfp_flags);
if (status != SCI_SUCCESS) {
isci_task_refuse(ihost, task,
SAS_TASK_COMPLETE,
SAS_QUEUE_FULL);
- isci_host_can_dequeue(ihost, 1);
}
}
}
+ if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ /* command never hit the device, so just free
+ * the tci and skip the sequence increment
+ */
+ isci_tci_free(ihost, ISCI_TAG_TCI(tag));
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ }
isci_put_device(idev);
}
return 0;
static struct isci_request *isci_task_request_build(struct isci_host *ihost,
struct isci_remote_device *idev,
- struct isci_tmf *isci_tmf)
+ u16 tag, struct isci_tmf *isci_tmf)
{
enum sci_status status = SCI_FAILURE;
struct isci_request *ireq = NULL;
return NULL;
/* let the core do it's construct. */
- status = scic_task_request_construct(&ihost->sci, &idev->sci,
- SCI_CONTROLLER_INVALID_IO_TAG,
+ status = scic_task_request_construct(&ihost->sci, &idev->sci, tag,
&ireq->sci);
if (status != SCI_SUCCESS) {
return ireq;
errout:
isci_request_free(ihost, ireq);
- ireq = NULL;
- return ireq;
+ return NULL;
}
int isci_task_execute_tmf(struct isci_host *ihost,
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
unsigned long timeleft;
+ u16 tag;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ tag = isci_alloc_tag(ihost);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (tag == SCI_CONTROLLER_INVALID_IO_TAG)
+ return ret;
/* 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__,
isci_device, isci_device ? isci_device->flags : 0);
- return TMF_RESP_FUNC_FAILED;
+ goto err_tci;
} else
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p\n",
/* Assign the pointer to the TMF's completion kernel wait structure. */
tmf->complete = &completion;
- ireq = isci_task_request_build(ihost, isci_device, tmf);
- if (!ireq) {
- dev_warn(&ihost->pdev->dev,
- "%s: isci_task_request_build failed\n",
- __func__);
- return TMF_RESP_FUNC_FAILED;
- }
+ ireq = isci_task_request_build(ihost, isci_device, tag, tmf);
+ if (!ireq)
+ goto err_tci;
spin_lock_irqsave(&ihost->scic_lock, flags);
/* start the TMF io. */
- status = scic_controller_start_task(
- &ihost->sci,
- sci_device,
- &ireq->sci,
- SCI_CONTROLLER_INVALID_IO_TAG);
+ status = scic_controller_start_task(&ihost->sci,
+ sci_device,
+ &ireq->sci);
if (status != SCI_TASK_SUCCESS) {
dev_warn(&ihost->pdev->dev,
status,
ireq);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
- goto cleanup_request;
+ goto err_ireq;
}
if (tmf->cb_state_func != NULL)
/* Wait for the TMF to complete, or a timeout. */
timeleft = wait_for_completion_timeout(&completion,
- jiffies + msecs_to_jiffies(timeout_ms));
+ msecs_to_jiffies(timeout_ms));
if (timeleft == 0) {
spin_lock_irqsave(&ihost->scic_lock, flags);
if (tmf->cb_state_func != NULL)
tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data);
- status = scic_controller_terminate_request(&ihost->sci,
- &isci_device->sci,
- &ireq->sci);
+ scic_controller_terminate_request(&ihost->sci,
+ &isci_device->sci,
+ &ireq->sci);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ wait_for_completion(tmf->complete);
}
isci_print_tmf(tmf);
__func__,
ireq);
- if (ireq->io_request_completion != NULL) {
- /* A thread is waiting for this TMF to finish. */
- complete(ireq->io_request_completion);
- }
+ return ret;
- cleanup_request:
+ err_ireq:
isci_request_free(ihost, ireq);
+ err_tci:
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ isci_tci_free(ihost, ISCI_TAG_TCI(tag));
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
return ret;
}
: NULL;
/* Note that we are not going to control
- * the target to abort the request.
- */
- isci_request->complete_in_target = true;
+ * the target to abort the request.
+ */
+ set_bit(IREQ_COMPLETE_IN_TARGET, &isci_request->flags);
/* Make sure the request wasn't just sitting around signalling
* device condition (if the request handle is NULL, then the
* request completed but needed additional handling here).
*/
- if (!isci_request->terminated) {
+ if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
was_terminated = true;
needs_cleanup_handling = true;
status = scic_controller_terminate_request(
flags);
/* Check for state changes. */
- if (!isci_request->terminated) {
+ if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
/* The best we can do is to have the
* request die a silent death if it
ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
- if ((task->task_proto == SAS_PROTOCOL_SMP)
- || old_request->complete_in_target
- ) {
+ if (task->task_proto == SAS_PROTOCOL_SMP ||
+ test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
"%s: SMP request (%d)"
" or complete_in_target (%d), thus no TMF\n",
__func__, (task->task_proto == SAS_PROTOCOL_SMP),
- old_request->complete_in_target);
+ test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));
/* Set the state on the task. */
isci_task_all_done(task);
__func__);
}
if (ret == TMF_RESP_FUNC_COMPLETE) {
- old_request->complete_in_target = true;
+ 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_request_change_state(ireq, completed);
tmf->status = completion_status;
- ireq->complete_in_target = true;
+ set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
if (tmf->proto == SAS_PROTOCOL_SSP) {
memcpy(&tmf->resp.resp_iu,
/* set the 'terminated' flag handle to make sure it cannot be terminated
* or completed again.
*/
- ireq->terminated = true;;
+ set_bit(IREQ_TERMINATED, &ireq->flags);
isci_request_change_state(ireq, unallocated);
list_del_init(&ireq->dev_node);