/* * 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 <scsi/sas.h> #include <linux/bitops.h> #include"isci.h" #include"port.h" #include"remote_device.h" #include"request.h" #include"remote_node_context.h" #include"scu_event_codes.h" #include"task.h"
/** * isci_remote_device_ready() - This function is called by the ihost when the * remote device is ready. We mark the isci device as ready and signal the * waiting proccess. * @ihost: our valid isci_host * @idev: remote device *
*/ staticvoid isci_remote_device_ready(struct isci_host *ihost, struct isci_remote_device *idev)
{
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);
}
/* Check for a change in the suspend count, or the RNC * being destroyed.
*/ return (localcount != idev->rnc.suspend_count)
|| sci_remote_node_context_is_being_destroyed(&idev->rnc);
}
#define MAX_SUSPEND_MSECS 10000 if (ireq) { /* Terminate a specific TC. */
set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags);
sci_remote_device_terminate_req(ihost, idev, 0, ireq);
spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!wait_event_timeout(ihost->eventq,
isci_check_reqterm(ihost, idev, ireq,
rnc_suspend_count),
msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n",
__func__, ihost->id);
dev_dbg(&ihost->pdev->dev, "%s: ******* Timeout waiting for " "suspend; idev=%p, current state %s; " "started_request_count=%d, flags=%lx\n\t" "rnc_suspend_count=%d, rnc.suspend_count=%d " "RNC: current state %s, current " "suspend_type %x dest state %d;\n" "ireq=%p, ireq->flags = %lx\n",
__func__, idev,
dev_state_name(idev->sm.current_state_id),
idev->started_request_count, idev->flags,
rnc_suspend_count, idev->rnc.suspend_count,
rnc_state_name(idev->rnc.sm.current_state_id),
idev->rnc.suspend_type,
idev->rnc.destination_state,
ireq, ireq->flags);
}
spin_lock_irqsave(&ihost->scic_lock, flags);
clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
isci_free_tag(ihost, ireq->io_tag);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
} else { /* Terminate all TCs. */
sci_remote_device_terminate_requests(idev);
spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!wait_event_timeout(ihost->eventq,
isci_check_devempty(ihost, idev,
rnc_suspend_count),
msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n",
__func__, ihost->id);
dev_dbg(&ihost->pdev->dev, "%s: ******* Timeout waiting for " "suspend; idev=%p, current state %s; " "started_request_count=%d, flags=%lx\n\t" "rnc_suspend_count=%d, " "RNC: current state %s, " "rnc.suspend_count=%d, current " "suspend_type %x dest state %d\n",
__func__, idev,
dev_state_name(idev->sm.current_state_id),
idev->started_request_count, idev->flags,
rnc_suspend_count,
rnc_state_name(idev->rnc.sm.current_state_id),
idev->rnc.suspend_count,
idev->rnc.suspend_type,
idev->rnc.destination_state);
}
}
dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n",
__func__, idev);
isci_put_device(idev);
} return status;
}
/** * isci_remote_device_not_ready() - This function is called by the ihost when * the remote device is not ready. We mark the isci device as ready (not * "ready_for_io") and signal the waiting proccess. * @ihost: This parameter specifies the isci host object. * @idev: This parameter specifies the remote device * @reason: Reason to switch on * * sci_lock is held on entrance to this function.
*/ staticvoid isci_remote_device_not_ready(struct isci_host *ihost, struct isci_remote_device *idev,
u32 reason)
{
dev_dbg(&ihost->pdev->dev, "%s: isci_device = %p; reason = %d\n", __func__, idev, reason);
switch (reason) { case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
set_bit(IDEV_IO_NCQERROR, &idev->flags);
/* Suspend the remote device so the I/O can be terminated. */
sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
/* Kill all outstanding requests for the device. */
sci_remote_device_terminate_requests(idev);
fallthrough; /* into the default case */ default:
clear_bit(IDEV_IO_READY, &idev->flags); break;
}
}
/* called once the remote node context is ready to be freed. * The remote device can now report that its stop operation is complete. none
*/ staticvoid rnc_destruct_done(void *_dev)
{ struct isci_remote_device *idev = _dev;
switch (state) { case SCI_DEV_INITIAL: case SCI_DEV_FAILED: case SCI_DEV_FINAL: default:
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE; case SCI_DEV_STOPPED: return SCI_SUCCESS; case SCI_DEV_STARTING: /* device not started so there had better be no requests */
BUG_ON(idev->started_request_count != 0);
sci_remote_node_context_destruct(&idev->rnc,
rnc_destruct_done, idev); /* Transition to the stopping state and wait for the * remote node to complete being posted and invalidated.
*/
sci_change_state(sm, SCI_DEV_STOPPING); return SCI_SUCCESS; case SCI_DEV_READY: case SCI_STP_DEV_IDLE: case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_AWAIT_RESET: case SCI_SMP_DEV_IDLE: case SCI_SMP_DEV_CMD:
sci_change_state(sm, SCI_DEV_STOPPING); if (idev->started_request_count == 0)
sci_remote_node_context_destruct(&idev->rnc,
rnc_destruct_done,
idev); else {
sci_remote_device_suspend(
idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
sci_remote_device_terminate_requests(idev);
} return SCI_SUCCESS; case SCI_DEV_STOPPING: /* All requests should have been terminated, but if there is an * attempt to stop a device already in the stopping state, then * try again to terminate.
*/ return sci_remote_device_terminate_requests(idev); case SCI_DEV_RESETTING:
sci_change_state(sm, SCI_DEV_STOPPING); return SCI_SUCCESS;
}
}
switch (state) { case SCI_DEV_INITIAL: case SCI_DEV_STOPPED: case SCI_DEV_STARTING: case SCI_STP_DEV_IDLE: case SCI_SMP_DEV_IDLE: case SCI_DEV_FINAL: default:
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); /* Return the frame back to the controller */
sci_controller_release_frame(ihost, frame_index); return SCI_FAILURE_INVALID_STATE; case SCI_DEV_READY: case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_AWAIT_RESET: case SCI_DEV_STOPPING: case SCI_DEV_FAILED: case SCI_DEV_RESETTING: { struct isci_request *ireq; struct ssp_frame_hdr hdr; void *frame_header;
ssize_t word_cnt;
status = sci_unsolicited_frame_control_get_header(&ihost->uf_control,
frame_index,
&frame_header); if (status != SCI_SUCCESS) return status;
ireq = sci_request_by_tag(ihost, be16_to_cpu(hdr.tag)); if (ireq && ireq->target_device == idev) { /* The IO request is now in charge of releasing the frame */
status = sci_io_request_frame_handler(ireq, frame_index);
} else { /* We could not map this tag to a valid IO * request Just toss the frame and continue
*/
sci_controller_release_frame(ihost, frame_index);
} break;
} case SCI_STP_DEV_NCQ: { struct dev_to_host_fis *hdr;
status = sci_unsolicited_frame_control_get_header(&ihost->uf_control,
frame_index,
(void **)&hdr); if (status != SCI_SUCCESS) return status;
/* TODO Check sactive and complete associated IO if any. */
sci_change_state(sm, SCI_STP_DEV_NCQ_ERROR);
} elseif (hdr->fis_type == FIS_REGD2H &&
(hdr->status & ATA_ERR)) { /* * Some devices return D2H FIS when an NCQ error is detected. * Treat this like an SDB error FIS ready reason.
*/
idev->not_ready_reason = SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED;
sci_change_state(&idev->sm, SCI_STP_DEV_NCQ_ERROR);
} else
status = SCI_FAILURE;
sci_controller_release_frame(ihost, frame_index); break;
} case SCI_STP_DEV_CMD: case SCI_SMP_DEV_CMD: /* The device does not process any UF received from the hardware while * in this state. All unsolicited frames are forwarded to the io request * object.
*/
status = sci_io_request_frame_handler(idev->working_request, frame_index); break;
}
struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id;
switch (state) { case SCI_DEV_READY: case SCI_STP_DEV_IDLE: case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_AWAIT_RESET: case SCI_SMP_DEV_IDLE: case SCI_SMP_DEV_CMD: returntrue; default: returnfalse;
}
}
/* * called once the remote node context has transisitioned to a ready * state (after suspending RX and/or TX due to early D2H fis)
*/ staticvoid atapi_remote_device_resume_done(void *_dev)
{ struct isci_remote_device *idev = _dev; struct isci_request *ireq = idev->working_request;
switch (scu_get_event_type(event_code)) { case SCU_EVENT_TYPE_RNC_OPS_MISC: case SCU_EVENT_TYPE_RNC_SUSPEND_TX: case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
status = sci_remote_node_context_event_handler(&idev->rnc, event_code); break; case SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT: if (scu_get_event_code(event_code) == SCU_EVENT_IT_NEXUS_TIMEOUT) {
status = SCI_SUCCESS;
/* Suspend the associated RNC */
sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
break;
}
fallthrough; /* and treat as unhandled */ default:
dev_dbg(scirdev_to_dev(idev), "%s: device: %p event code: %x: %s\n",
__func__, idev, event_code,
is_remote_device_ready(idev)
? "unexpected event"
: "unexpected event in wrong state");
status = SCI_FAILURE_INVALID_STATE; break;
}
if (status != SCI_SUCCESS) return status;
/* Decode device-specific states that may require an RNC resume during * normal operation. When the abort path is active, these resumes are * managed when the abort path exits.
*/ if (state == SCI_STP_DEV_ATAPI_ERROR) { /* For ATAPI error state resume the RNC right away. */ if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { return sci_remote_node_context_resume(&idev->rnc,
atapi_remote_device_resume_done,
idev);
}
}
if (state == SCI_STP_DEV_IDLE) {
/* We pick up suspension events to handle specifically to this * state. We resume the RNC right away.
*/ if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX)
status = sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
}
/* cleanup requests that failed after starting on the port */ if (status != SCI_SUCCESS)
sci_port_complete_io(iport, idev, ireq); else {
kref_get(&idev->kref);
idev->started_request_count++;
}
}
switch (state) { case SCI_DEV_INITIAL: case SCI_DEV_STOPPED: case SCI_DEV_STARTING: case SCI_STP_DEV_NCQ_ERROR: case SCI_DEV_STOPPING: case SCI_DEV_FAILED: case SCI_DEV_RESETTING: case SCI_DEV_FINAL: default:
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE; case SCI_DEV_READY: /* attempt to start an io request for this device object. The remote * device object will issue the start request for the io and if * successful it will start the request for the port object then * increment its own request count.
*/
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
status = sci_remote_node_context_start_io(&idev->rnc, ireq); if (status != SCI_SUCCESS) break;
status = sci_request_start(ireq); break; case SCI_STP_DEV_IDLE: { /* handle the start io operation for a sata device that is in * the command idle state. - Evalute the type of IO request to * be started - If its an NCQ request change to NCQ substate - * If its any other command change to the CMD substate * * If this is a softreset we may want to have a different * substate.
*/ enum sci_remote_device_states new_state; struct sas_task *task = isci_request_access_task(ireq);
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
status = sci_remote_node_context_start_io(&idev->rnc, ireq); if (status != SCI_SUCCESS) break;
status = sci_request_start(ireq); if (status != SCI_SUCCESS) break;
if (task->ata_task.use_ncq) {
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
status = sci_remote_node_context_start_io(&idev->rnc, ireq); if (status != SCI_SUCCESS) break;
status = sci_request_start(ireq);
} else return SCI_FAILURE_INVALID_STATE; break;
} case SCI_STP_DEV_AWAIT_RESET: return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; case SCI_SMP_DEV_IDLE:
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
status = sci_remote_node_context_start_io(&idev->rnc, ireq); if (status != SCI_SUCCESS) break;
status = sci_request_start(ireq); if (status != SCI_SUCCESS) break;
idev->working_request = ireq;
sci_change_state(&idev->sm, SCI_SMP_DEV_CMD); break; case SCI_STP_DEV_CMD: case SCI_SMP_DEV_CMD: /* device is already handling a command it can not accept new commands * until this one is complete.
*/ return SCI_FAILURE_INVALID_STATE;
}
switch (state) { case SCI_DEV_INITIAL: case SCI_DEV_STOPPED: case SCI_DEV_STARTING: case SCI_STP_DEV_IDLE: case SCI_SMP_DEV_IDLE: case SCI_DEV_FAILED: case SCI_DEV_FINAL: default:
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE; case SCI_DEV_READY: case SCI_STP_DEV_AWAIT_RESET: case SCI_DEV_RESETTING:
status = common_complete_io(iport, idev, ireq); break; case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_ATAPI_ERROR:
status = common_complete_io(iport, idev, ireq); if (status != SCI_SUCCESS) break;
if (ireq->sci_status == SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { /* This request causes hardware error, device needs to be Lun Reset. * So here we force the state machine to IDLE state so the rest IOs * can reach RNC state handler, these IOs will be completed by RNC with * status of "DEVICE_RESET_REQUIRED", instead of "INVALID STATE".
*/
sci_change_state(sm, SCI_STP_DEV_AWAIT_RESET);
} elseif (idev->started_request_count == 0)
sci_change_state(sm, SCI_STP_DEV_IDLE); break; case SCI_SMP_DEV_CMD:
status = common_complete_io(iport, idev, ireq); if (status != SCI_SUCCESS) break;
sci_change_state(sm, SCI_SMP_DEV_IDLE); break; case SCI_DEV_STOPPING:
status = common_complete_io(iport, idev, ireq); if (status != SCI_SUCCESS) break;
if (idev->started_request_count == 0)
sci_remote_node_context_destruct(&idev->rnc,
rnc_destruct_done,
idev); break;
}
switch (state) { case SCI_DEV_INITIAL: case SCI_DEV_STOPPED: case SCI_DEV_STARTING: case SCI_SMP_DEV_IDLE: case SCI_SMP_DEV_CMD: case SCI_DEV_STOPPING: case SCI_DEV_FAILED: case SCI_DEV_RESETTING: case SCI_DEV_FINAL: default:
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE; case SCI_STP_DEV_IDLE: case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: case SCI_STP_DEV_AWAIT_RESET:
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
status = sci_request_start(ireq); if (status != SCI_SUCCESS) goto out;
/* Note: If the remote device state is not IDLE this will * replace the request that probably resulted in the task * management request.
*/
idev->working_request = ireq;
sci_change_state(sm, SCI_STP_DEV_CMD);
/* The remote node context must cleanup the TCi to NCQ mapping * table. The only way to do this correctly is to either write * to the TLCR register or to invalidate and repost the RNC. In * either case the remote node context state machine will take * the correct action when the remote node context is suspended * and later resumed.
*/
sci_remote_device_suspend(idev,
SCI_SW_SUSPEND_LINKHANG_DETECT);
status = sci_remote_node_context_start_task(&idev->rnc, ireq,
sci_remote_device_continue_request, idev);
out:
sci_remote_device_start_request(idev, ireq, status); /* We need to let the controller start request handler know that * it can't post TC yet. We will provide a callback function to * post TC when RNC gets resumed.
*/ return SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS; case SCI_DEV_READY:
status = sci_port_start_io(iport, idev, ireq); if (status != SCI_SUCCESS) return status;
/* Resume the RNC as needed: */
status = sci_remote_node_context_start_task(&idev->rnc, ireq,
NULL, NULL); if (status != SCI_SUCCESS) break;
status = sci_request_start(ireq); break;
}
sci_remote_device_start_request(idev, ireq, status);
/* called once the remote node context has transisitioned to a * ready state. This is the indication that the remote device object can also * transition to ready.
*/ staticvoid remote_device_resume_done(void *_dev)
{ struct isci_remote_device *idev = _dev;
if (is_remote_device_ready(idev)) return;
/* go 'ready' if we are not already in a ready state */
sci_change_state(&idev->sm, SCI_DEV_READY);
}
/* For NCQ operation we do not issue a isci_remote_device_not_ready(). * As a result, avoid sending the ready notification.
*/ if (idev->sm.previous_state_id != SCI_STP_DEV_NCQ)
isci_remote_device_ready(ihost, idev);
}
/* Initial state is a transitional state to the stopped state */
sci_change_state(&idev->sm, SCI_DEV_STOPPED);
}
/** * sci_remote_device_destruct() - free remote node context and destruct * @idev: This parameter specifies the remote device to be destructed. * * Remote device objects are a limited resource. As such, they must be * protected. Thus calls to construct and destruct are mutually exclusive and * non-reentrant. The return value shall indicate if the device was * successfully destructed or if some failure occurred. enum sci_status This value * is returned if the device is successfully destructed. * SCI_FAILURE_INVALID_REMOTE_DEVICE This value is returned if the supplied * device isn't valid (e.g. it's already been destoryed, the handle isn't * valid, etc.).
*/ staticenum sci_status sci_remote_device_destruct(struct isci_remote_device *idev)
{ struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id; struct isci_host *ihost;
if (state != SCI_DEV_STOPPED) {
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE;
}
/** * isci_remote_device_deconstruct() - This function frees an isci_remote_device. * @ihost: This parameter specifies the isci host object. * @idev: This parameter specifies the remote device to be freed. *
*/ staticvoid isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_remote_device *idev)
{
dev_dbg(&ihost->pdev->dev, "%s: isci_device = %p\n", __func__, idev);
/* There should not be any outstanding io's. All paths to * here should go through isci_remote_device_nuke_requests. * If we hit this condition, we will need a way to complete
* io requests in process */
BUG_ON(idev->started_request_count > 0);
/* If we are entering from the stopping state let the SCI User know that * the stop operation has completed.
*/
prev_state = idev->sm.previous_state_id; if (prev_state == SCI_DEV_STOPPING)
isci_remote_device_deconstruct(ihost, idev);
idev->working_request = NULL; if (sci_remote_node_context_is_ready(&idev->rnc)) { /* * Since the RNC is ready, it's alright to finish completion
* processing (e.g. signal the remote device is ready). */
sci_stp_remote_device_ready_idle_substate_resume_complete_handler(idev);
} else {
sci_remote_node_context_resume(&idev->rnc,
sci_stp_remote_device_ready_idle_substate_resume_complete_handler,
idev);
}
}
/** * sci_remote_device_construct() - common construction * @iport: SAS/SATA port through which this device is accessed. * @idev: remote device to construct * * This routine just performs benign initialization and does not * allocate the remote_node_context which is left to * sci_remote_device_[de]a_construct(). sci_remote_device_destruct() * frees the remote_node_context(s) for the device.
*/ staticvoid sci_remote_device_construct(struct isci_port *iport, struct isci_remote_device *idev)
{
idev->owning_port = iport;
idev->started_request_count = 0;
/* * sci_remote_device_da_construct() - construct direct attached device. * * The information (e.g. IAF, Signature FIS, etc.) necessary to build * the device is known to the SCI Core since it is contained in the * sci_phy object. Remote node context(s) is/are a global resource * allocated by this routine, freed by sci_remote_device_destruct(). * * Returns: * SCI_FAILURE_DEVICE_EXISTS - device has already been constructed. * SCI_FAILURE_UNSUPPORTED_PROTOCOL - e.g. sas device attached to * sata-only controller instance. * SCI_FAILURE_INSUFFICIENT_RESOURCES - remote node contexts exhausted.
*/ staticenum sci_status sci_remote_device_da_construct(struct isci_port *iport, struct isci_remote_device *idev)
{ enum sci_status status; struct sci_port_properties properties;
sci_remote_device_construct(iport, idev);
sci_port_get_properties(iport, &properties); /* Get accurate port width from port's phy mask for a DA device. */
idev->device_port_width = hweight32(properties.phy_mask);
status = sci_controller_allocate_remote_node_context(iport->owning_controller,
idev,
&idev->rnc.remote_node_index);
/* * sci_remote_device_ea_construct() - construct expander attached device * * Remote node context(s) is/are a global resource allocated by this * routine, freed by sci_remote_device_destruct(). * * Returns: * SCI_FAILURE_DEVICE_EXISTS - device has already been constructed. * SCI_FAILURE_UNSUPPORTED_PROTOCOL - e.g. sas device attached to * sata-only controller instance. * SCI_FAILURE_INSUFFICIENT_RESOURCES - remote node contexts exhausted.
*/ staticenum sci_status sci_remote_device_ea_construct(struct isci_port *iport, struct isci_remote_device *idev)
{ struct domain_device *dev = idev->domain_dev; enum sci_status status;
sci_remote_device_construct(iport, idev);
status = sci_controller_allocate_remote_node_context(iport->owning_controller,
idev,
&idev->rnc.remote_node_index); if (status != SCI_SUCCESS) return status;
/* For SAS-2 the physical link rate is actually a logical link * rate that incorporates multiplexing. The SCU doesn't * incorporate multiplexing and for the purposes of the * connection the logical link rate is that same as the * physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay * one another, so this code works for both situations.
*/
idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport),
dev->linkrate);
/* / @todo Should I assign the port width by reading all of the phys on the port? */
idev->device_port_width = 1;
enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, struct isci_remote_device *idev)
{ unsignedlong flags; enum sci_status status = SCI_SUCCESS; int destroyed;
spin_lock_irqsave(&ihost->scic_lock, flags); /* Preserve any current resume callbacks, for instance from other * resumptions.
*/
idev->abort_resume_cb = idev->rnc.user_callback;
idev->abort_resume_cbparam = idev->rnc.user_cookie;
set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc); if (!destroyed)
status = sci_remote_device_resume(
idev, isci_remote_device_resume_from_abort_complete,
idev);
spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!destroyed && (status == SCI_SUCCESS))
isci_remote_device_wait_for_resume_from_abort(ihost, idev); else
clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
return status;
}
/** * sci_remote_device_start() - This method will start the supplied remote * device. This method enables normal IO requests to flow through to the * remote device. * @idev: This parameter specifies the device to be started. * @timeout: This parameter specifies the number of milliseconds in which the * start operation should complete. * * An indication of whether the device was successfully started. SCI_SUCCESS * This value is returned if the device was successfully started. * SCI_FAILURE_INVALID_PHY This value is returned if the user attempts to start * the device when there have been no phys added to it.
*/ staticenum sci_status sci_remote_device_start(struct isci_remote_device *idev,
u32 timeout)
{ struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id; enum sci_status status;
if (state != SCI_DEV_STOPPED) {
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state)); return SCI_FAILURE_INVALID_STATE;
}
status = sci_remote_device_resume(idev, remote_device_resume_done,
idev); if (status != SCI_SUCCESS) return status;
if (dev->parent && dev_is_expander(dev->parent->dev_type))
status = sci_remote_device_ea_construct(iport, idev); else
status = sci_remote_device_da_construct(iport, idev);
/** * isci_remote_device_alloc() * This function builds the isci_remote_device when a libsas dev_found message * is received. * @ihost: This parameter specifies the isci host object. * @iport: This parameter specifies the isci_port connected to this device. * * pointer to new isci_remote_device.
*/ staticstruct isci_remote_device *
isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)
{ struct isci_remote_device *idev; int i;
for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
idev = &ihost->devices[i]; if (!test_and_set_bit(IDEV_ALLOCATED, &idev->flags)) break;
}
if (i >= SCI_MAX_REMOTE_DEVICES) {
dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__); return NULL;
} if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n")) return NULL;
/** * isci_remote_device_stop() - This function is called internally to stop the * remote device. * @ihost: This parameter specifies the isci host object. * @idev: This parameter specifies the remote device. * * The status of the ihost request to stop.
*/ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_remote_device *idev)
{ enum sci_status status; unsignedlong flags;
set_bit(IDEV_STOP_PENDING, &idev->flags);
status = sci_remote_device_stop(idev, 50);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
/* Wait for the stop complete callback. */ if (WARN_ONCE(status != SCI_SUCCESS, "failed to stop device\n")) /* nothing to wait for */; else
wait_for_device_stop(ihost, idev);
/** * isci_remote_device_gone() - This function is called by libsas when a domain * device is removed. * @dev: This parameter specifies the libsas domain device.
*/ void isci_remote_device_gone(struct domain_device *dev)
{ struct isci_host *ihost = dev_to_ihost(dev); struct isci_remote_device *idev = dev->lldd_dev;
/** * isci_remote_device_found() - This function is called by libsas when a remote * device is discovered. A remote device object is created and started. the * function then sleeps until the sci core device started message is * received. * @dev: This parameter specifies the libsas domain device. * * status, zero indicates success.
*/ int isci_remote_device_found(struct domain_device *dev)
{ struct isci_host *isci_host = dev_to_ihost(dev); struct isci_port *isci_port = dev->port->lldd_port; struct isci_remote_device *isci_device; enum sci_status status;
if (status == SCI_SUCCESS) { /* device came up, advertise it to the world */
dev->lldd_dev = isci_device;
} else
isci_put_device(isci_device);
spin_unlock_irq(&isci_host->scic_lock);
/* wait for the device ready callback. */
wait_for_device_start(isci_host, isci_device);
/* Put the device into suspension. */
spin_lock_irqsave(&ihost->scic_lock, flags);
set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
/* Terminate and wait for the completions. */
status = isci_remote_device_terminate_requests(ihost, idev, ireq); if (status != SCI_SUCCESS)
dev_dbg(&ihost->pdev->dev, "%s: isci_remote_device_terminate_requests(%p) " "returned %d!\n",
__func__, idev, status);
/* NOTE: RNC resumption is left to the caller! */ return status;
}
int isci_remote_device_is_safe_to_abort( struct isci_remote_device *idev)
{ return sci_remote_node_context_is_safe_to_abort(&idev->rnc);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.