![Jeff Skirvin](/assets/img/avatar_default.png)
In the link fail path, set IDEV_GONE for every device on the domain when the last link in the port fails. In the abort path functions like isci_reset_device, make sure that there has not already been a detected domain failure with the device by checking IDEV_GONE, before performing any kind of hard reset, SMP phy control, or TMF operation. The check for IDEV_GONE makes sure that the device in the abort path really has control of the port with which it is associated. This prevents starting hard resets at incorrect times and scheduling unnecessary LUN resets for SATA devices. Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
810 lines
24 KiB
C
810 lines
24 KiB
C
/*
|
|
* This file is provided under a dual BSD/GPLv2 license. When using or
|
|
* redistributing this file, you may do so under either license.
|
|
*
|
|
* GPL LICENSE SUMMARY
|
|
*
|
|
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
* The full GNU General Public License is included in this distribution
|
|
* in the file called LICENSE.GPL.
|
|
*
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/irqflags.h>
|
|
#include "sas.h"
|
|
#include <scsi/libsas.h>
|
|
#include "remote_device.h"
|
|
#include "remote_node_context.h"
|
|
#include "isci.h"
|
|
#include "request.h"
|
|
#include "task.h"
|
|
#include "host.h"
|
|
|
|
/**
|
|
* isci_task_refuse() - complete the request to the upper layer driver in
|
|
* the case where an I/O needs to be completed back in the submit path.
|
|
* @ihost: host on which the the request was queued
|
|
* @task: request to complete
|
|
* @response: response code for the completed task.
|
|
* @status: status code for the completed task.
|
|
*
|
|
*/
|
|
static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
|
|
enum service_response response,
|
|
enum exec_status status)
|
|
|
|
{
|
|
unsigned long flags;
|
|
|
|
/* Normal notification (task_done) */
|
|
dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n",
|
|
__func__, task, response, status);
|
|
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
|
|
task->task_status.resp = response;
|
|
task->task_status.stat = status;
|
|
|
|
/* Normal notification (task_done) */
|
|
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
|
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
|
SAS_TASK_STATE_PENDING);
|
|
task->lldd_task = NULL;
|
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
|
|
task->task_done(task);
|
|
}
|
|
|
|
#define for_each_sas_task(num, task) \
|
|
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
|
|
* hardware.
|
|
* @task: This parameter specifies the SAS task to send.
|
|
* @num: This parameter specifies the number of tasks to queue.
|
|
* @gfp_flags: This parameter specifies the context of this call.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
|
|
{
|
|
struct isci_host *ihost = dev_to_ihost(task->dev);
|
|
struct isci_remote_device *idev;
|
|
unsigned long flags;
|
|
bool io_ready;
|
|
u16 tag;
|
|
|
|
dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num);
|
|
|
|
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 = isci_device_io_ready(idev, task);
|
|
tag = isci_alloc_tag(ihost);
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n",
|
|
task, num, task->dev, idev, idev ? idev->flags : 0,
|
|
task->uldd_task);
|
|
|
|
if (!idev) {
|
|
isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
|
|
SAS_DEVICE_UNKNOWN);
|
|
} 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);
|
|
} else {
|
|
/* There is a device and it's ready for I/O. */
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
|
|
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
|
|
/* The I/O was aborted. */
|
|
spin_unlock_irqrestore(&task->task_state_lock,
|
|
flags);
|
|
|
|
isci_task_refuse(ihost, task,
|
|
SAS_TASK_UNDELIVERED,
|
|
SAM_STAT_TASK_ABORTED);
|
|
} 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, tag);
|
|
|
|
if (status != SCI_SUCCESS) {
|
|
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
/* Did not really start this command. */
|
|
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
|
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
|
|
if (test_bit(IDEV_GONE, &idev->flags)) {
|
|
|
|
/* Indicate that the device
|
|
* is gone.
|
|
*/
|
|
isci_task_refuse(ihost, task,
|
|
SAS_TASK_UNDELIVERED,
|
|
SAS_DEVICE_UNKNOWN);
|
|
} else {
|
|
/* Indicate QUEUE_FULL so that
|
|
* the scsi midlayer retries.
|
|
* If the request failed for
|
|
* remote device reasons, it
|
|
* gets returned as
|
|
* SAS_TASK_UNDELIVERED next
|
|
* time through.
|
|
*/
|
|
isci_task_refuse(ihost, task,
|
|
SAS_TASK_COMPLETE,
|
|
SAS_QUEUE_FULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
u16 tag, struct isci_tmf *isci_tmf)
|
|
{
|
|
enum sci_status status = SCI_FAILURE;
|
|
struct isci_request *ireq = NULL;
|
|
struct domain_device *dev;
|
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: isci_tmf = %p\n", __func__, isci_tmf);
|
|
|
|
dev = idev->domain_dev;
|
|
|
|
/* do common allocation and init of request object. */
|
|
ireq = isci_tmf_request_from_tag(ihost, isci_tmf, tag);
|
|
if (!ireq)
|
|
return NULL;
|
|
|
|
/* let the core do it's construct. */
|
|
status = sci_task_request_construct(ihost, idev, tag,
|
|
ireq);
|
|
|
|
if (status != SCI_SUCCESS) {
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: sci_task_request_construct failed - "
|
|
"status = 0x%x\n",
|
|
__func__,
|
|
status);
|
|
return NULL;
|
|
}
|
|
|
|
/* XXX convert to get this from task->tproto like other drivers */
|
|
if (dev->dev_type == SAS_END_DEV) {
|
|
isci_tmf->proto = SAS_PROTOCOL_SSP;
|
|
status = sci_task_request_construct_ssp(ireq);
|
|
if (status != SCI_SUCCESS)
|
|
return NULL;
|
|
}
|
|
|
|
return ireq;
|
|
}
|
|
|
|
static int isci_task_execute_tmf(struct isci_host *ihost,
|
|
struct isci_remote_device *idev,
|
|
struct isci_tmf *tmf, unsigned long timeout_ms)
|
|
{
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
|
enum sci_task_status status = SCI_TASK_FAILURE;
|
|
struct isci_request *ireq;
|
|
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 (!idev ||
|
|
(!test_bit(IDEV_IO_READY, &idev->flags) &&
|
|
!test_bit(IDEV_IO_NCQERROR, &idev->flags))) {
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: idev = %p not ready (%#lx)\n",
|
|
__func__,
|
|
idev, idev ? idev->flags : 0);
|
|
goto err_tci;
|
|
} else
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: idev = %p\n",
|
|
__func__, idev);
|
|
|
|
/* Assign the pointer to the TMF's completion kernel wait structure. */
|
|
tmf->complete = &completion;
|
|
tmf->status = SCI_FAILURE_TIMEOUT;
|
|
|
|
ireq = isci_task_request_build(ihost, idev, tag, tmf);
|
|
if (!ireq)
|
|
goto err_tci;
|
|
|
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
|
|
/* start the TMF io. */
|
|
status = sci_controller_start_task(ihost, idev, ireq);
|
|
|
|
if (status != SCI_TASK_SUCCESS) {
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: start_io failed - status = 0x%x, request = %p\n",
|
|
__func__,
|
|
status,
|
|
ireq);
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
goto err_tci;
|
|
}
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
/* The RNC must be unsuspended before the TMF can get a response. */
|
|
isci_remote_device_resume_from_abort(ihost, idev);
|
|
|
|
/* Wait for the TMF to complete, or a timeout. */
|
|
timeleft = wait_for_completion_timeout(&completion,
|
|
msecs_to_jiffies(timeout_ms));
|
|
|
|
if (timeleft == 0) {
|
|
/* The TMF did not complete - this could be because
|
|
* of an unplug. Terminate the TMF request now.
|
|
*/
|
|
isci_remote_device_suspend_terminate(ihost, idev, ireq);
|
|
}
|
|
|
|
isci_print_tmf(ihost, tmf);
|
|
|
|
if (tmf->status == SCI_SUCCESS)
|
|
ret = TMF_RESP_FUNC_COMPLETE;
|
|
else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) {
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: tmf.status == "
|
|
"SCI_FAILURE_IO_RESPONSE_VALID\n",
|
|
__func__);
|
|
ret = TMF_RESP_FUNC_COMPLETE;
|
|
}
|
|
/* Else - leave the default "failed" status alone. */
|
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: completed request = %p\n",
|
|
__func__,
|
|
ireq);
|
|
|
|
return ret;
|
|
|
|
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;
|
|
}
|
|
|
|
static void isci_task_build_tmf(struct isci_tmf *tmf,
|
|
enum isci_tmf_function_codes code)
|
|
{
|
|
memset(tmf, 0, sizeof(*tmf));
|
|
tmf->tmf_code = code;
|
|
}
|
|
|
|
static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf,
|
|
enum isci_tmf_function_codes code,
|
|
struct isci_request *old_request)
|
|
{
|
|
isci_task_build_tmf(tmf, code);
|
|
tmf->io_tag = old_request->io_tag;
|
|
}
|
|
|
|
/**
|
|
* isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
|
|
* Template functions.
|
|
* @lun: This parameter specifies the lun to be reset.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
static int isci_task_send_lu_reset_sas(
|
|
struct isci_host *isci_host,
|
|
struct isci_remote_device *isci_device,
|
|
u8 *lun)
|
|
{
|
|
struct isci_tmf tmf;
|
|
int ret = TMF_RESP_FUNC_FAILED;
|
|
|
|
dev_dbg(&isci_host->pdev->dev,
|
|
"%s: isci_host = %p, isci_device = %p\n",
|
|
__func__, isci_host, isci_device);
|
|
/* Send the LUN reset to the target. By the time the call returns,
|
|
* the TMF has fully exected in the target (in which case the return
|
|
* value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
|
|
* was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
|
|
*/
|
|
isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset);
|
|
|
|
#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
|
|
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
|
|
|
|
if (ret == TMF_RESP_FUNC_COMPLETE)
|
|
dev_dbg(&isci_host->pdev->dev,
|
|
"%s: %p: TMF_LU_RESET passed\n",
|
|
__func__, isci_device);
|
|
else
|
|
dev_dbg(&isci_host->pdev->dev,
|
|
"%s: %p: TMF_LU_RESET failed (%x)\n",
|
|
__func__, isci_device, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
|
|
{
|
|
struct isci_host *ihost = dev_to_ihost(dev);
|
|
struct isci_remote_device *idev;
|
|
unsigned long flags;
|
|
int ret = TMF_RESP_FUNC_COMPLETE;
|
|
|
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
idev = isci_get_device(dev->lldd_dev);
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
|
|
__func__, dev, ihost, idev);
|
|
|
|
if (!idev) {
|
|
/* If the device is gone, escalate to I_T_Nexus_Reset. */
|
|
dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
|
|
|
|
ret = TMF_RESP_FUNC_FAILED;
|
|
goto out;
|
|
}
|
|
|
|
/* Suspend the RNC, kill all TCs */
|
|
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
|
!= SCI_SUCCESS) {
|
|
/* The suspend/terminate only fails if isci_get_device fails */
|
|
ret = TMF_RESP_FUNC_FAILED;
|
|
goto out;
|
|
}
|
|
/* All pending I/Os have been terminated and cleaned up. */
|
|
if (!test_bit(IDEV_GONE, &idev->flags)) {
|
|
if (dev_is_sata(dev))
|
|
sas_ata_schedule_reset(dev);
|
|
else
|
|
/* Send the task management part of the reset. */
|
|
ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
|
|
}
|
|
out:
|
|
isci_put_device(idev);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
|
|
int isci_task_clear_nexus_port(struct asd_sas_port *port)
|
|
{
|
|
return TMF_RESP_FUNC_FAILED;
|
|
}
|
|
|
|
|
|
|
|
int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
|
|
{
|
|
return TMF_RESP_FUNC_FAILED;
|
|
}
|
|
|
|
/* Task Management Functions. Must be called from process context. */
|
|
|
|
/**
|
|
* isci_task_abort_task() - This function is one of the SAS Domain Template
|
|
* functions. This function is called by libsas to abort a specified task.
|
|
* @task: This parameter specifies the SAS task to abort.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_abort_task(struct sas_task *task)
|
|
{
|
|
struct isci_host *ihost = dev_to_ihost(task->dev);
|
|
DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
|
|
struct isci_request *old_request = NULL;
|
|
struct isci_remote_device *idev = NULL;
|
|
struct isci_tmf tmf;
|
|
int ret = TMF_RESP_FUNC_FAILED;
|
|
unsigned long flags;
|
|
|
|
/* Get the isci_request reference from the task. Note that
|
|
* this check does not depend on the pending request list
|
|
* in the device, because tasks driving resets may land here
|
|
* after completion in the core.
|
|
*/
|
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
spin_lock(&task->task_state_lock);
|
|
|
|
old_request = task->lldd_task;
|
|
|
|
/* If task is already done, the request isn't valid */
|
|
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
|
|
(task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
|
|
old_request)
|
|
idev = isci_get_device(task->dev->lldd_dev);
|
|
|
|
spin_unlock(&task->task_state_lock);
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: dev = %p (%s%s), task = %p, old_request == %p\n",
|
|
__func__, idev,
|
|
(dev_is_sata(task->dev) ? "STP/SATA"
|
|
: ((dev_is_expander(task->dev))
|
|
? "SMP"
|
|
: "SSP")),
|
|
((idev) ? ((test_bit(IDEV_GONE, &idev->flags))
|
|
? " IDEV_GONE"
|
|
: "")
|
|
: " <NULL>"),
|
|
task, old_request);
|
|
|
|
/* Device reset conditions signalled in task_state_flags are the
|
|
* responsbility of libsas to observe at the start of the error
|
|
* handler thread.
|
|
*/
|
|
if (!idev || !old_request) {
|
|
/* The request has already completed and there
|
|
* is nothing to do here other than to set the task
|
|
* done bit, and indicate that the task abort function
|
|
* was sucessful.
|
|
*/
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
|
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
|
SAS_TASK_STATE_PENDING);
|
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
|
|
ret = TMF_RESP_FUNC_COMPLETE;
|
|
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: abort task not needed for %p\n",
|
|
__func__, task);
|
|
goto out;
|
|
}
|
|
/* Suspend the RNC, kill the TC */
|
|
if (isci_remote_device_suspend_terminate(ihost, idev, old_request)
|
|
!= SCI_SUCCESS) {
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: isci_remote_device_reset_terminate(dev=%p, "
|
|
"req=%p, task=%p) failed\n",
|
|
__func__, idev, old_request, task);
|
|
ret = TMF_RESP_FUNC_FAILED;
|
|
goto out;
|
|
}
|
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
|
|
if (task->task_proto == SAS_PROTOCOL_SMP ||
|
|
sas_protocol_ata(task->task_proto) ||
|
|
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) ||
|
|
test_bit(IDEV_GONE, &idev->flags)) {
|
|
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
/* No task to send, so explicitly resume the device here */
|
|
isci_remote_device_resume_from_abort(ihost, idev);
|
|
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: %s request"
|
|
" or complete_in_target (%d), "
|
|
"or IDEV_GONE (%d), thus no TMF\n",
|
|
__func__,
|
|
((task->task_proto == SAS_PROTOCOL_SMP)
|
|
? "SMP"
|
|
: (sas_protocol_ata(task->task_proto)
|
|
? "SATA/STP"
|
|
: "<other>")
|
|
),
|
|
test_bit(IREQ_COMPLETE_IN_TARGET,
|
|
&old_request->flags),
|
|
test_bit(IDEV_GONE, &idev->flags));
|
|
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
|
SAS_TASK_STATE_PENDING);
|
|
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
|
|
ret = TMF_RESP_FUNC_COMPLETE;
|
|
} else {
|
|
/* Fill in the tmf stucture */
|
|
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
|
|
old_request);
|
|
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
/* Send the task management request. */
|
|
#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
|
|
ret = isci_task_execute_tmf(ihost, idev, &tmf,
|
|
ISCI_ABORT_TASK_TIMEOUT_MS);
|
|
}
|
|
out:
|
|
dev_warn(&ihost->pdev->dev,
|
|
"%s: Done; dev = %p, task = %p , old_request == %p\n",
|
|
__func__, idev, task, old_request);
|
|
isci_put_device(idev);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* isci_task_abort_task_set() - This function is one of the SAS Domain Template
|
|
* functions. This is one of the Task Management functoins called by libsas,
|
|
* to abort all task for the given lun.
|
|
* @d_device: This parameter specifies the domain device associated with this
|
|
* request.
|
|
* @lun: This parameter specifies the lun associated with this request.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_abort_task_set(
|
|
struct domain_device *d_device,
|
|
u8 *lun)
|
|
{
|
|
return TMF_RESP_FUNC_FAILED;
|
|
}
|
|
|
|
|
|
/**
|
|
* isci_task_clear_aca() - This function is one of the SAS Domain Template
|
|
* functions. This is one of the Task Management functoins called by libsas.
|
|
* @d_device: This parameter specifies the domain device associated with this
|
|
* request.
|
|
* @lun: This parameter specifies the lun associated with this request.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_clear_aca(
|
|
struct domain_device *d_device,
|
|
u8 *lun)
|
|
{
|
|
return TMF_RESP_FUNC_FAILED;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* isci_task_clear_task_set() - This function is one of the SAS Domain Template
|
|
* functions. This is one of the Task Management functoins called by libsas.
|
|
* @d_device: This parameter specifies the domain device associated with this
|
|
* request.
|
|
* @lun: This parameter specifies the lun associated with this request.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_clear_task_set(
|
|
struct domain_device *d_device,
|
|
u8 *lun)
|
|
{
|
|
return TMF_RESP_FUNC_FAILED;
|
|
}
|
|
|
|
|
|
/**
|
|
* isci_task_query_task() - This function is implemented to cause libsas to
|
|
* correctly escalate the failed abort to a LUN or target reset (this is
|
|
* because sas_scsi_find_task libsas function does not correctly interpret
|
|
* all return codes from the abort task call). When TMF_RESP_FUNC_SUCC is
|
|
* returned, libsas turns this into a LUN reset; when FUNC_FAILED is
|
|
* returned, libsas will turn this into a target reset
|
|
* @task: This parameter specifies the sas task being queried.
|
|
* @lun: This parameter specifies the lun associated with this request.
|
|
*
|
|
* status, zero indicates success.
|
|
*/
|
|
int isci_task_query_task(
|
|
struct sas_task *task)
|
|
{
|
|
/* See if there is a pending device reset for this device. */
|
|
if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
|
|
return TMF_RESP_FUNC_FAILED;
|
|
else
|
|
return TMF_RESP_FUNC_SUCC;
|
|
}
|
|
|
|
/*
|
|
* isci_task_request_complete() - This function is called by the sci core when
|
|
* an task request completes.
|
|
* @ihost: This parameter specifies the ISCI host object
|
|
* @ireq: This parameter is the completed isci_request object.
|
|
* @completion_status: This parameter specifies the completion status from the
|
|
* sci core.
|
|
*
|
|
* none.
|
|
*/
|
|
void
|
|
isci_task_request_complete(struct isci_host *ihost,
|
|
struct isci_request *ireq,
|
|
enum sci_task_status completion_status)
|
|
{
|
|
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
|
|
struct completion *tmf_complete = NULL;
|
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
"%s: request = %p, status=%d\n",
|
|
__func__, ireq, completion_status);
|
|
|
|
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
|
|
|
|
if (tmf) {
|
|
tmf->status = completion_status;
|
|
|
|
if (tmf->proto == SAS_PROTOCOL_SSP) {
|
|
memcpy(&tmf->resp.resp_iu,
|
|
&ireq->ssp.rsp,
|
|
SSP_RESP_IU_MAX_SIZE);
|
|
} else if (tmf->proto == SAS_PROTOCOL_SATA) {
|
|
memcpy(&tmf->resp.d2h_fis,
|
|
&ireq->stp.rsp,
|
|
sizeof(struct dev_to_host_fis));
|
|
}
|
|
/* PRINT_TMF( ((struct isci_tmf *)request->task)); */
|
|
tmf_complete = tmf->complete;
|
|
}
|
|
sci_controller_complete_io(ihost, ireq->target_device, ireq);
|
|
/* set the 'terminated' flag handle to make sure it cannot be terminated
|
|
* or completed again.
|
|
*/
|
|
set_bit(IREQ_TERMINATED, &ireq->flags);
|
|
|
|
if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
|
|
wake_up_all(&ihost->eventq);
|
|
|
|
if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags))
|
|
isci_free_tag(ihost, ireq->io_tag);
|
|
|
|
/* The task management part completes last. */
|
|
if (tmf_complete)
|
|
complete(tmf_complete);
|
|
}
|
|
|
|
static int isci_reset_device(struct isci_host *ihost,
|
|
struct domain_device *dev,
|
|
struct isci_remote_device *idev)
|
|
{
|
|
int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1;
|
|
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);
|
|
|
|
/* Suspend the RNC, terminate all outstanding TCs. */
|
|
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
|
!= SCI_SUCCESS) {
|
|
rc = TMF_RESP_FUNC_FAILED;
|
|
goto out;
|
|
}
|
|
/* Note that since the termination for outstanding requests succeeded,
|
|
* this function will return success. This is because the resets will
|
|
* only fail if the device has been removed (ie. hotplug), and the
|
|
* primary duty of this function is to cleanup tasks, so that is the
|
|
* relevant status.
|
|
*/
|
|
if (!test_bit(IDEV_GONE, &idev->flags)) {
|
|
if (scsi_is_sas_phy_local(phy)) {
|
|
struct isci_phy *iphy = &ihost->phys[phy->number];
|
|
|
|
reset_stat = isci_port_perform_hard_reset(ihost, iport,
|
|
iphy);
|
|
} else
|
|
reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
|
|
}
|
|
/* Explicitly resume the RNC here, since there was no task sent. */
|
|
isci_remote_device_resume_from_abort(ihost, idev);
|
|
|
|
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n",
|
|
__func__, idev, reset_stat);
|
|
out:
|
|
sas_put_local_phy(phy);
|
|
return rc;
|
|
}
|
|
|
|
int isci_task_I_T_nexus_reset(struct domain_device *dev)
|
|
{
|
|
struct isci_host *ihost = dev_to_ihost(dev);
|
|
struct isci_remote_device *idev;
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
idev = isci_get_device(dev->lldd_dev);
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
if (!idev) {
|
|
/* XXX: need to cleanup any ireqs targeting this
|
|
* domain_device
|
|
*/
|
|
ret = TMF_RESP_FUNC_COMPLETE;
|
|
goto out;
|
|
}
|
|
|
|
ret = isci_reset_device(ihost, dev, idev);
|
|
out:
|
|
isci_put_device(idev);
|
|
return ret;
|
|
}
|