diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2013-07-11 20:18:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-04 04:50:40 -0400 |
commit | b01c4c2c2e5b7ecf14e10efabf65183d9565ff2f (patch) | |
tree | 21595aeea289c742a1895e14147836569aa4f4cc /drivers/scsi/isci/task.c | |
parent | 7ca674af75d75aa6755746327f1fd3222e54b338 (diff) |
SCSI: isci: Fix a race condition in the SSP task management path
commit 96f15f29038e58e1b0a96483e2b369ff446becf1 upstream.
This commit fixes a race condition in the isci driver abort task and SSP
device task management path. The race is caused when an I/O termination
in the SCU hardware is necessary because of an SSP target timeout condition,
and the check of the I/O end state races against the HW-termination-driven
end state. The failure of the race meant that no TMF was sent to the device
to clean-up the pending I/O.
Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Reviewed-by: Lukasz Dorau <lukasz.dorau@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/scsi/isci/task.c')
-rw-r--r-- | drivers/scsi/isci/task.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 9bb020ac089c..0d30ca849e8f 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -491,6 +491,7 @@ int isci_task_abort_task(struct sas_task *task) | |||
491 | struct isci_tmf tmf; | 491 | struct isci_tmf tmf; |
492 | int ret = TMF_RESP_FUNC_FAILED; | 492 | int ret = TMF_RESP_FUNC_FAILED; |
493 | unsigned long flags; | 493 | unsigned long flags; |
494 | int target_done_already = 0; | ||
494 | 495 | ||
495 | /* Get the isci_request reference from the task. Note that | 496 | /* Get the isci_request reference from the task. Note that |
496 | * this check does not depend on the pending request list | 497 | * this check does not depend on the pending request list |
@@ -505,9 +506,11 @@ int isci_task_abort_task(struct sas_task *task) | |||
505 | /* If task is already done, the request isn't valid */ | 506 | /* If task is already done, the request isn't valid */ |
506 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && | 507 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && |
507 | (task->task_state_flags & SAS_TASK_AT_INITIATOR) && | 508 | (task->task_state_flags & SAS_TASK_AT_INITIATOR) && |
508 | old_request) | 509 | old_request) { |
509 | idev = isci_get_device(task->dev->lldd_dev); | 510 | idev = isci_get_device(task->dev->lldd_dev); |
510 | 511 | target_done_already = test_bit(IREQ_COMPLETE_IN_TARGET, | |
512 | &old_request->flags); | ||
513 | } | ||
511 | spin_unlock(&task->task_state_lock); | 514 | spin_unlock(&task->task_state_lock); |
512 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 515 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
513 | 516 | ||
@@ -561,7 +564,7 @@ int isci_task_abort_task(struct sas_task *task) | |||
561 | 564 | ||
562 | if (task->task_proto == SAS_PROTOCOL_SMP || | 565 | if (task->task_proto == SAS_PROTOCOL_SMP || |
563 | sas_protocol_ata(task->task_proto) || | 566 | sas_protocol_ata(task->task_proto) || |
564 | test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) || | 567 | target_done_already || |
565 | test_bit(IDEV_GONE, &idev->flags)) { | 568 | test_bit(IDEV_GONE, &idev->flags)) { |
566 | 569 | ||
567 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 570 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |