diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2011-10-28 00:05:01 +0200 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-10-31 10:16:04 +0100 |
commit | d6891682220c18c01bf6838f30e37342c38fde44 (patch) | |
tree | ad0c61215cbc9de6c82e95825923f95ebc2532a1 /drivers/scsi/isci/task.c | |
parent | [SCSI] isci: Immediately fail I/O to removed devices. (diff) | |
download | linux-d6891682220c18c01bf6838f30e37342c38fde44.tar.xz linux-d6891682220c18c01bf6838f30e37342c38fde44.zip |
[SCSI] isci: Fix tag leak in tasks and terminated requests.
Make sure terminated requests and completed task tags are freed.
Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to '')
-rw-r--r-- | drivers/scsi/isci/task.c | 51 |
1 files changed, 31 insertions, 20 deletions
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index e8cf17b024a4..7180b048c34b 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -552,11 +552,27 @@ static void isci_request_cleanup_completed_loiterer( if (isci_request != NULL) { spin_lock_irqsave(&isci_host->scic_lock, flags); + isci_free_tag(isci_host, isci_request->io_tag); + isci_request_change_state(isci_request, unallocated); list_del_init(&isci_request->dev_node); spin_unlock_irqrestore(&isci_host->scic_lock, flags); } } +static int isci_request_is_dealloc_managed(enum isci_request_status stat) +{ + switch (stat) { + case aborted: + case aborting: + case terminating: + case completed: + case dead: + return true; + default: + return false; + } +} + /** * isci_terminate_request_core() - This function will terminate the given * request, and wait for it to complete. This function must only be called @@ -574,7 +590,6 @@ static void isci_terminate_request_core(struct isci_host *ihost, enum sci_status status = SCI_SUCCESS; bool was_terminated = false; bool needs_cleanup_handling = false; - enum isci_request_status request_status; unsigned long flags; unsigned long termination_completed = 1; struct completion *io_request_completion; @@ -702,23 +717,11 @@ static void isci_terminate_request_core(struct isci_host *ihost, * needs to be detached and freed here. */ spin_lock_irqsave(&isci_request->state_lock, flags); - request_status = isci_request->status; - - if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ - && ((request_status == aborted) - || (request_status == aborting) - || (request_status == terminating) - || (request_status == completed) - || (request_status == dead) - ) - ) { - - /* The completion routine won't free a request in - * the aborted/aborting/etc. states, so we do - * it here. - */ - needs_cleanup_handling = true; - } + + needs_cleanup_handling + = isci_request_is_dealloc_managed( + isci_request->status); + spin_unlock_irqrestore(&isci_request->state_lock, flags); } @@ -1329,8 +1332,16 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, unallocated); - list_del_init(&ireq->dev_node); + /* As soon as something is in the terminate path, deallocation is + * managed there. Note that the final non-managed state of a task + * request is "completed". + */ + if ((ireq->status == completed) || + !isci_request_is_dealloc_managed(ireq->status)) { + isci_request_change_state(ireq, unallocated); + isci_free_tag(ihost, ireq->io_tag); + list_del_init(&ireq->dev_node); + } /* The task management part completes last. */ complete(tmf_complete); |