805 lines · 25.1 KB · C
RawDownload
/* * 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_ata.h>#include "host.h"#include "isci.h"#include "remote_device.h"#include "remote_node_context.h"#include "scu_event_codes.h"#include "scu_task_context.h" #undef C#define C(a) (#a)const char *rnc_state_name(enum scis_sds_remote_node_context_states state){	static const char * const strings[] = RNC_STATES; 	if (state >= ARRAY_SIZE(strings))		return "UNKNOWN"; 	return strings[state];}#undef C /** * sci_remote_node_context_is_ready() * @sci_rnc: The state of the remote node context object to check. * * This method will return true if the remote node context is in a READY state * otherwise it will return false bool true if the remote node context is in * the ready state. false if the remote node context is not in the ready state. */bool sci_remote_node_context_is_ready(	struct sci_remote_node_context *sci_rnc){	u32 current_state = sci_rnc->sm.current_state_id; 	if (current_state == SCI_RNC_READY) {		return true;	} 	return false;} bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc){	u32 current_state = sci_rnc->sm.current_state_id; 	if (current_state == SCI_RNC_TX_RX_SUSPENDED)		return true;	return false;} static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id){	if (id < ihost->remote_node_entries &&	    ihost->device_table[id])		return &ihost->remote_node_context_table[id]; 	return NULL;} static void sci_remote_node_context_construct_buffer(struct sci_remote_node_context *sci_rnc){	struct isci_remote_device *idev = rnc_to_dev(sci_rnc);	struct domain_device *dev = idev->domain_dev;	int rni = sci_rnc->remote_node_index;	union scu_remote_node_context *rnc;	struct isci_host *ihost;	__le64 sas_addr; 	ihost = idev->owning_port->owning_controller;	rnc = sci_rnc_by_id(ihost, rni); 	memset(rnc, 0, sizeof(union scu_remote_node_context)		* sci_remote_device_node_count(idev)); 	rnc->ssp.remote_node_index = rni;	rnc->ssp.remote_node_port_width = idev->device_port_width;	rnc->ssp.logical_port_index = idev->owning_port->physical_port_index; 	/* sas address is __be64, context ram format is __le64 */	sas_addr = cpu_to_le64(SAS_ADDR(dev->sas_addr));	rnc->ssp.remote_sas_address_hi = upper_32_bits(sas_addr);	rnc->ssp.remote_sas_address_lo = lower_32_bits(sas_addr); 	rnc->ssp.nexus_loss_timer_enable = true;	rnc->ssp.check_bit               = false;	rnc->ssp.is_valid                = false;	rnc->ssp.is_remote_node_context  = true;	rnc->ssp.function_number         = 0; 	rnc->ssp.arbitration_wait_time = 0; 	if (dev_is_sata(dev)) {		rnc->ssp.connection_occupancy_timeout =			ihost->user_parameters.stp_max_occupancy_timeout;		rnc->ssp.connection_inactivity_timeout =			ihost->user_parameters.stp_inactivity_timeout;	} else {		rnc->ssp.connection_occupancy_timeout  =			ihost->user_parameters.ssp_max_occupancy_timeout;		rnc->ssp.connection_inactivity_timeout =			ihost->user_parameters.ssp_inactivity_timeout;	} 	rnc->ssp.initial_arbitration_wait_time = 0; 	/* Open Address Frame Parameters */	rnc->ssp.oaf_connection_rate = idev->connection_rate;	rnc->ssp.oaf_features = 0;	rnc->ssp.oaf_source_zone_group = 0;	rnc->ssp.oaf_more_compatibility_features = 0;}/* * This method will setup the remote node context object so it will transition * to its ready state.  If the remote node context is already setup to * transition to its final state then this function does nothing. none */static void sci_remote_node_context_setup_to_resume(	struct sci_remote_node_context *sci_rnc,	scics_sds_remote_node_context_callback callback,	void *callback_parameter,	enum sci_remote_node_context_destination_state dest_param){	if (sci_rnc->destination_state != RNC_DEST_FINAL) {		sci_rnc->destination_state = dest_param;		if (callback != NULL) {			sci_rnc->user_callback = callback;			sci_rnc->user_cookie   = callback_parameter;		}	}} static void sci_remote_node_context_setup_to_destroy(	struct sci_remote_node_context *sci_rnc,	scics_sds_remote_node_context_callback callback,	void *callback_parameter){	struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc)); 	sci_rnc->destination_state = RNC_DEST_FINAL;	sci_rnc->user_callback     = callback;	sci_rnc->user_cookie       = callback_parameter; 	wake_up(&ihost->eventq);} /* * This method just calls the user callback function and then resets the * callback. */static void sci_remote_node_context_notify_user(	struct sci_remote_node_context *rnc){	if (rnc->user_callback != NULL) {		(*rnc->user_callback)(rnc->user_cookie); 		rnc->user_callback = NULL;		rnc->user_cookie = NULL;	}} static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc){	switch (rnc->destination_state) {	case RNC_DEST_READY:	case RNC_DEST_SUSPENDED_RESUME:		rnc->destination_state = RNC_DEST_READY;		fallthrough;	case RNC_DEST_FINAL:		sci_remote_node_context_resume(rnc, rnc->user_callback,					       rnc->user_cookie);		break;	default:		rnc->destination_state = RNC_DEST_UNSPECIFIED;		break;	}} static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc){	union scu_remote_node_context *rnc_buffer;	struct isci_remote_device *idev = rnc_to_dev(sci_rnc);	struct domain_device *dev = idev->domain_dev;	struct isci_host *ihost = idev->owning_port->owning_controller; 	rnc_buffer = sci_rnc_by_id(ihost, sci_rnc->remote_node_index); 	rnc_buffer->ssp.is_valid = true; 	if (dev_is_sata(dev) && dev->parent) {		sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96);	} else {		sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32); 		if (!dev->parent)			sci_port_setup_transports(idev->owning_port,						  sci_rnc->remote_node_index);	}} static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_node_context *sci_rnc){	union scu_remote_node_context *rnc_buffer;	struct isci_remote_device *idev = rnc_to_dev(sci_rnc);	struct isci_host *ihost = idev->owning_port->owning_controller; 	rnc_buffer = sci_rnc_by_id(ihost, sci_rnc->remote_node_index); 	rnc_buffer->ssp.is_valid = false; 	sci_remote_device_post_request(rnc_to_dev(sci_rnc),				       SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE);} static void sci_remote_node_context_initial_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);	struct isci_remote_device *idev = rnc_to_dev(rnc);	struct isci_host *ihost = idev->owning_port->owning_controller; 	/* Check to see if we have gotten back to the initial state because	 * someone requested to destroy the remote node context object.	 */	if (sm->previous_state_id == SCI_RNC_INVALIDATING) {		rnc->destination_state = RNC_DEST_UNSPECIFIED;		sci_remote_node_context_notify_user(rnc); 		smp_wmb();		wake_up(&ihost->eventq);	}} static void sci_remote_node_context_posting_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *sci_rnc = container_of(sm, typeof(*sci_rnc), sm); 	sci_remote_node_context_validate_context_buffer(sci_rnc);} static void sci_remote_node_context_invalidating_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); 	/* Terminate all outstanding requests. */	sci_remote_device_terminate_requests(rnc_to_dev(rnc));	sci_remote_node_context_invalidate_context_buffer(rnc);} static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);	struct isci_remote_device *idev;	struct domain_device *dev; 	idev = rnc_to_dev(rnc);	dev = idev->domain_dev; 	/*	 * For direct attached SATA devices we need to clear the TLCR	 * NCQ to TCi tag mapping on the phy and in cases where we	 * resume because of a target reset we also need to update	 * the STPTLDARNI register with the RNi of the device	 */	if (dev_is_sata(dev) && !dev->parent)		sci_port_setup_transports(idev->owning_port, rnc->remote_node_index); 	sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);} static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);	enum sci_remote_node_context_destination_state dest_select;	int tell_user = 1; 	dest_select = rnc->destination_state;	rnc->destination_state = RNC_DEST_UNSPECIFIED; 	if ((dest_select == RNC_DEST_SUSPENDED) ||	    (dest_select == RNC_DEST_SUSPENDED_RESUME)) {		sci_remote_node_context_suspend(			rnc, rnc->suspend_reason,			SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); 		if (dest_select == RNC_DEST_SUSPENDED_RESUME)			tell_user = 0;  /* Wait until ready again. */	}	if (tell_user)		sci_remote_node_context_notify_user(rnc);} static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); 	sci_remote_node_context_continue_state_transitions(rnc);} static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);	struct isci_remote_device *idev = rnc_to_dev(rnc);	struct isci_host *ihost = idev->owning_port->owning_controller;	u32 new_count = rnc->suspend_count + 1; 	if (new_count == 0)		rnc->suspend_count = 1;	else		rnc->suspend_count = new_count;	smp_wmb(); 	/* Terminate outstanding requests pending abort. */	sci_remote_device_abort_requests_pending_abort(idev); 	wake_up(&ihost->eventq);	sci_remote_node_context_continue_state_transitions(rnc);} static void sci_remote_node_context_await_suspend_state_exit(	struct sci_base_state_machine *sm){	struct sci_remote_node_context *rnc		= container_of(sm, typeof(*rnc), sm);	struct isci_remote_device *idev = rnc_to_dev(rnc); 	if (dev_is_sata(idev->domain_dev))		isci_dev_set_hang_detection_timeout(idev, 0);} static const struct sci_base_state sci_remote_node_context_state_table[] = {	[SCI_RNC_INITIAL] = {		.enter_state = sci_remote_node_context_initial_state_enter,	},	[SCI_RNC_POSTING] = {		.enter_state = sci_remote_node_context_posting_state_enter,	},	[SCI_RNC_INVALIDATING] = {		.enter_state = sci_remote_node_context_invalidating_state_enter,	},	[SCI_RNC_RESUMING] = {		.enter_state = sci_remote_node_context_resuming_state_enter,	},	[SCI_RNC_READY] = {		.enter_state = sci_remote_node_context_ready_state_enter,	},	[SCI_RNC_TX_SUSPENDED] = {		.enter_state = sci_remote_node_context_tx_suspended_state_enter,	},	[SCI_RNC_TX_RX_SUSPENDED] = {		.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,	},	[SCI_RNC_AWAIT_SUSPENSION] = {		.exit_state = sci_remote_node_context_await_suspend_state_exit,	},}; void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,					    u16 remote_node_index){	memset(rnc, 0, sizeof(struct sci_remote_node_context)); 	rnc->remote_node_index = remote_node_index;	rnc->destination_state = RNC_DEST_UNSPECIFIED; 	sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL);} enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc,							   u32 event_code){	enum scis_sds_remote_node_context_states state;	u32 next_state; 	state = sci_rnc->sm.current_state_id;	switch (state) {	case SCI_RNC_POSTING:		switch (scu_get_event_code(event_code)) {		case SCU_EVENT_POST_RNC_COMPLETE:			sci_change_state(&sci_rnc->sm, SCI_RNC_READY);			break;		default:			goto out;		}		break;	case SCI_RNC_INVALIDATING:		if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {			if (sci_rnc->destination_state == RNC_DEST_FINAL)				next_state = SCI_RNC_INITIAL;			else				next_state = SCI_RNC_POSTING;			sci_change_state(&sci_rnc->sm, next_state);		} else {			switch (scu_get_event_type(event_code)) {			case SCU_EVENT_TYPE_RNC_SUSPEND_TX:			case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:				/* We really dont care if the hardware is going to suspend				 * the device since it's being invalidated anyway */				dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),					"%s: SCIC Remote Node Context 0x%p was "					"suspended by hardware while being "					"invalidated.\n", __func__, sci_rnc);				break;			default:				goto out;			}		}		break;	case SCI_RNC_RESUMING:		if (scu_get_event_code(event_code) == SCU_EVENT_POST_RCN_RELEASE) {			sci_change_state(&sci_rnc->sm, SCI_RNC_READY);		} else {			switch (scu_get_event_type(event_code)) {			case SCU_EVENT_TYPE_RNC_SUSPEND_TX:			case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:				/* We really dont care if the hardware is going to suspend				 * the device since it's being resumed anyway */				dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),					"%s: SCIC Remote Node Context 0x%p was "					"suspended by hardware while being resumed.\n",					__func__, sci_rnc);				break;			default:				goto out;			}		}		break;	case SCI_RNC_READY:		switch (scu_get_event_type(event_code)) {		case SCU_EVENT_TL_RNC_SUSPEND_TX:			sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);			sci_rnc->suspend_type = scu_get_event_type(event_code);			break;		case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:			sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);			sci_rnc->suspend_type = scu_get_event_type(event_code);			break;		default:			goto out;		}		break;	case SCI_RNC_AWAIT_SUSPENSION:		switch (scu_get_event_type(event_code)) {		case SCU_EVENT_TL_RNC_SUSPEND_TX:			next_state = SCI_RNC_TX_SUSPENDED;			break;		case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:			next_state = SCI_RNC_TX_RX_SUSPENDED;

Lines 1–500 of 805.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t