hifc/hifc/unf_lport.c

1137 lines
30 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Huawei Hifc PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*
*/
#include "unf_log.h"
#include "unf_common.h"
#include "unf_event.h"
#include "unf_lport.h"
#include "unf_rport.h"
#include "unf_exchg.h"
#include "unf_service.h"
#include "unf_portman.h"
#include "unf_npiv.h"
static void unf_lport_timeout(struct work_struct *work);
unsigned int unf_lport_retry_flogi(struct unf_lport_s *v_lport);
int unf_get_port_params(void *v_argin, void *v_argout);
unsigned int unf_lport_name_server_register(
struct unf_lport_s *v_lport,
enum unf_lport_login_state_e states);
unsigned int unf_lport_enter_sns_logo(struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport);
void unf_cmmark_dirty_mem(struct unf_lport_s *v_lport,
enum unf_lport_dirty_flag_e v_etype)
{
UNF_CHECK_VALID(0x1801, UNF_TRUE, v_lport, return);
v_lport->dirty_flag |= v_etype;
}
unsigned int unf_init_lport_route(struct unf_lport_s *v_lport)
{
int ret = 0;
UNF_CHECK_VALID(0x1802, UNF_TRUE,
v_lport, return UNF_RETURN_ERROR);
/* Init L_Port route work */
INIT_DELAYED_WORK(&v_lport->route_timer_work, unf_lport_route_work);
/* Delay route work */
ret = queue_delayed_work(
unf_work_queue,
&v_lport->route_timer_work,
(unsigned long)msecs_to_jiffies(UNF_LPORT_POLL_TIMER));
if (unlikely(ret == UNF_FALSE)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
"[warn]Port(0x%x) schedule route work failed",
v_lport->port_id);
return UNF_RETURN_ERROR;
}
return unf_lport_refinc(v_lport);
}
void unf_destroy_lport_route(struct unf_lport_s *v_lport)
{
unsigned int ret = UNF_RETURN_ERROR;
UNF_CHECK_VALID(0x1803, UNF_TRUE, v_lport, return);
/* Cancel (route timer) delay work */
UNF_DELAYED_WORK_SYNC(ret, v_lport->port_id,
&v_lport->route_timer_work,
"Route Timer work");
if (ret == RETURN_OK) {
/* Corresponding to ADD operation */
unf_lport_ref_dec(v_lport);
}
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_2_CLOSE_ROUTE;
}
static void unf_lport_config(struct unf_lport_s *v_lport)
{
UNF_CHECK_VALID(0x1816, UNF_TRUE, v_lport, return);
INIT_DELAYED_WORK(&v_lport->retry_work, unf_lport_timeout);
v_lport->max_retry_count = UNF_MAX_RETRY_COUNT; /* 3 */
v_lport->retries = 0;
}
void unf_init_portparms(struct unf_lport_s *v_lport)
{
INIT_LIST_HEAD(&v_lport->list_vports_head);
INIT_LIST_HEAD(&v_lport->list_intergrad_vports);
INIT_LIST_HEAD(&v_lport->list_destroy_vports);
INIT_LIST_HEAD(&v_lport->entry_lport);
spin_lock_init(&v_lport->lport_state_lock);
v_lport->max_frame_size = max_frame_size;
v_lport->ed_tov = UNF_DEFAULT_EDTOV;
v_lport->ra_tov = UNF_DEFAULT_RATOV;
v_lport->rr_tov = UNF_DEFAULT_RRTOV;
v_lport->fabric_node_name = 0;
v_lport->b_priority = UNF_PRIORITY_DISABLE;
v_lport->b_port_dir_exchange = UNF_FALSE;
/* Delay (retry) work init */
unf_lport_config(v_lport);
unf_set_lport_state(v_lport, UNF_LPORT_ST_ONLINE); /* online */
v_lport->link_up = UNF_PORT_LINK_DOWN;
v_lport->b_port_removing = UNF_FALSE;
v_lport->lport_free_completion = NULL;
v_lport->last_tx_fault_jif = 0;
v_lport->enhanced_features = 0;
v_lport->destroy_step = INVALID_VALUE32;
v_lport->dirty_flag = 0;
v_lport->b_switch_state = UNF_FALSE;
v_lport->b_bbscn_support = UNF_FALSE;
v_lport->en_start_work_state = UNF_START_WORK_STOP;
v_lport->sfp_power_fault_count = 0;
v_lport->sfp_9545_fault_count = 0;
atomic_set(&v_lport->port_no_operater_flag, UNF_LPORT_NORMAL);
atomic_set(&v_lport->lport_ref_cnt, 0);
atomic_set(&v_lport->scsi_session_add_success, 0);
atomic_set(&v_lport->scsi_session_add_failed, 0);
atomic_set(&v_lport->scsi_session_del_success, 0);
atomic_set(&v_lport->scsi_session_del_failed, 0);
atomic_set(&v_lport->add_start_work_failed, 0);
atomic_set(&v_lport->add_closing_work_failed, 0);
atomic_set(&v_lport->alloc_scsi_id, 0);
atomic_set(&v_lport->resume_scsi_id, 0);
atomic_set(&v_lport->reuse_scsi_id, 0);
atomic_set(&v_lport->device_alloc, 0);
atomic_set(&v_lport->device_destroy, 0);
atomic_set(&v_lport->session_loss_tmo, 0);
atomic64_set(&v_lport->exchg_index, 1);
atomic_inc(&v_lport->lport_ref_cnt);
atomic_set(&v_lport->err_code_obtain_freq, 0);
memset(&v_lport->link_service_info, 0,
sizeof(struct unf_link_service_collect_s));
memset(&v_lport->err_code_sum, 0, sizeof(struct unf_err_code_s));
}
void unf_reset_lport_params(struct unf_lport_s *v_lport)
{
struct unf_lport_s *lport = v_lport;
UNF_CHECK_VALID(0x1804, UNF_TRUE, v_lport, return);
lport->link_up = UNF_PORT_LINK_DOWN;
lport->nport_id = 0; /* Need do FLOGI again to clear N_Port_ID */
lport->max_frame_size = max_frame_size;
lport->ed_tov = UNF_DEFAULT_EDTOV;
lport->ra_tov = UNF_DEFAULT_RATOV;
lport->rr_tov = UNF_DEFAULT_RRTOV;
lport->fabric_node_name = 0;
}
static enum unf_lport_login_state_e unf_lport_stat_online(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_LINK_UP:
/* EVENT_LINK_UP --->>> ST_LINK_UP */
next_state = UNF_LPORT_ST_LINK_UP;
break;
case UNF_EVENT_LPORT_NORMAL_ENTER:
/* EVENT_NORMAL_ENTER --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_initial(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_LINK_UP:
/* EVENT_LINK_UP --->>> ST_LINK_UP */
next_state = UNF_LPORT_ST_LINK_UP;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_linkup(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_NORMAL_ENTER:
/* EVENT_NORMAL_ENTER --->>> FLOGI_WAIT */
next_state = UNF_LPORT_ST_FLOGI_WAIT;
break;
case UNF_EVENT_LPORT_READY:
/* EVENT_READY --->>> ST_READY */
next_state = UNF_LPORT_ST_READY;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_flogi_wait(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_REMOTE_ACC:
/* EVENT_REMOTE_ACC --->>> ST_PLOGI_WAIT */
next_state = UNF_LPORT_ST_PLOGI_WAIT;
break;
case UNF_EVENT_LPORT_READY:
/* EVENT_READY --->>> ST_READY */
next_state = UNF_LPORT_ST_READY;
break;
case UNF_EVENT_LPORT_REMOTE_TIMEOUT:
/* EVENT_REMOTE_TIMEOUT --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_plogi_wait(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_REMOTE_ACC:
/* EVENT_REMOTE_ACC --->>> ST_RFT_ID_WAIT */
next_state = UNF_LPORT_ST_RFT_ID_WAIT;
break;
case UNF_EVENT_LPORT_REMOTE_TIMEOUT:
/* EVENT_TIMEOUT --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_rftid_wait(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_REMOTE_ACC:
/* EVENT_REMOTE_ACC --->>> ST_RFF_ID_WAIT */
next_state = UNF_LPORT_ST_RFF_ID_WAIT;
break;
case UNF_EVENT_LPORT_REMOTE_TIMEOUT:
/* EVENT_TIMEOUT --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_stat_rffid_wait(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_REMOTE_ACC:
/* EVENT_REMOTE_ACC --->>> ST_SCR_WAIT */
next_state = UNF_LPORT_ST_SCR_WAIT;
break;
case UNF_EVENT_LPORT_REMOTE_TIMEOUT:
/* EVENT_TIMEOUT --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_state_scr_wait(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_REMOTE_ACC:
/* EVENT_REMOTE_ACC --->>> ST_READY */
next_state = UNF_LPORT_ST_READY;
break;
case UNF_EVENT_LPORT_REMOTE_TIMEOUT:
/* EVENT_TIMEOUT --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_state_logo(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_NORMAL_ENTER:
/* EVENT_NORMAL_ENTER --->>> ST_OFFLINE */
next_state = UNF_LPORT_ST_OFFLINE;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_state_offline(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_ONLINE:
/* EVENT_ONLINE --->>> ST_ONLINE */
next_state = UNF_LPORT_ST_ONLINE;
break;
case UNF_EVENT_LPORT_RESET:
/* EVENT_RESET --->>> ST_RESET */
next_state = UNF_LPORT_ST_RESET;
break;
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_state_reset(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_NORMAL_ENTER:
/* EVENT_NORMAL_ENTER --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
static enum unf_lport_login_state_e unf_lport_state_ready(
enum unf_lport_login_state_e old_state,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
switch (event) {
case UNF_EVENT_LPORT_LINK_DOWN:
/* EVENT_LINK_DOWN --->>> ST_INITIAL */
next_state = UNF_LPORT_ST_INITIAL;
break;
case UNF_EVENT_LPORT_RESET:
/* EVENT_RESET --->>> ST_RESET */
next_state = UNF_LPORT_ST_RESET;
break;
case UNF_EVENT_LPORT_OFFLINE:
/* EVENT_OFFLINE --->>> ST_LOGO */
next_state = UNF_LPORT_ST_LOGO;
break;
default:
next_state = old_state;
break;
}
return next_state;
}
void unf_lport_stat_ma(struct unf_lport_s *v_lport,
enum unf_lport_event_e event)
{
enum unf_lport_login_state_e old_state = UNF_LPORT_ST_ONLINE;
enum unf_lport_login_state_e next_state = UNF_LPORT_ST_ONLINE;
UNF_CHECK_VALID(0x1805, UNF_TRUE, v_lport, return);
old_state = v_lport->en_states;
switch (v_lport->en_states) {
case UNF_LPORT_ST_ONLINE:
next_state = unf_lport_stat_online(old_state, event);
break;
case UNF_LPORT_ST_INITIAL:
next_state = unf_lport_stat_initial(old_state, event);
break;
case UNF_LPORT_ST_LINK_UP:
next_state = unf_lport_stat_linkup(old_state, event);
break;
case UNF_LPORT_ST_FLOGI_WAIT:
next_state = unf_lport_stat_flogi_wait(old_state, event);
break;
case UNF_LPORT_ST_PLOGI_WAIT:
next_state = unf_lport_stat_plogi_wait(old_state, event);
break;
case UNF_LPORT_ST_RFT_ID_WAIT:
next_state = unf_lport_stat_rftid_wait(old_state, event);
break;
case UNF_LPORT_ST_RFF_ID_WAIT:
next_state = unf_lport_stat_rffid_wait(old_state, event);
break;
case UNF_LPORT_ST_SCR_WAIT:
next_state = unf_lport_state_scr_wait(old_state, event);
break;
case UNF_LPORT_ST_LOGO:
next_state = unf_lport_state_logo(old_state, event);
break;
case UNF_LPORT_ST_OFFLINE:
next_state = unf_lport_state_offline(old_state, event);
break;
case UNF_LPORT_ST_RESET:
next_state = unf_lport_state_reset(old_state, event);
break;
case UNF_LPORT_ST_READY:
next_state = unf_lport_state_ready(old_state, event);
break;
default:
next_state = old_state;
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info]Port(0x%x) hold state(0x%x)",
v_lport->port_id, v_lport->en_states);
break;
}
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
"[info]Port(0x%x) with old state(0x%x) event(0x%x) next state(0x%x)",
v_lport->port_id, old_state, event, next_state);
unf_set_lport_state(v_lport, next_state);
}
unsigned int unf_init_lport_mgr_temp(struct unf_lport_s *v_lport)
{
UNF_CHECK_VALID(0x1806, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
v_lport->lport_mgr_temp.pfn_unf_vport_get_free_and_init = NULL;
v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_vp_index =
unf_lookup_vport_by_vp_index;
v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_port_id =
unf_lookup_vport_by_port_id;
v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_did =
unf_lookup_vport_by_did;
v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_wwpn =
unf_lookup_vport_by_wwpn;
v_lport->lport_mgr_temp.pfn_unf_vport_remove = unf_vport_remove;
return RETURN_OK;
}
void unf_release_lport_mgr_temp(struct unf_lport_s *v_lport)
{
UNF_CHECK_VALID(0x1807, UNF_TRUE, v_lport, return);
memset(&v_lport->lport_mgr_temp, 0,
sizeof(struct unf_cm_lport_template_s));
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_9_DESTROY_LPORT_MG_TMP;
}
unsigned int unf_lport_retry_flogi(struct unf_lport_s *v_lport)
{
struct unf_rport_s *rport = NULL;
unsigned int ret = UNF_RETURN_ERROR;
unsigned long flag = 0;
UNF_CHECK_VALID(0x1808, UNF_TRUE,
v_lport, return UNF_RETURN_ERROR);
/* Get (new) R_Port */
rport = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_FLOGI);
rport = unf_get_safe_rport(v_lport, rport,
UNF_RPORT_REUSE_ONLY, UNF_FC_FID_FLOGI);
if (unlikely(!rport)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) allocate RPort failed",
v_lport->port_id);
return UNF_RETURN_ERROR;
}
/* Check L_Port state */
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
if (v_lport->en_states != UNF_LPORT_ST_FLOGI_WAIT) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) no need to retry FLOGI with state(0x%x)",
v_lport->port_id, v_lport->en_states);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
return RETURN_OK;
}
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
spin_lock_irqsave(&rport->rport_state_lock, flag);
rport->nport_id = UNF_FC_FID_FLOGI;
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
/* Send FLOGI or FDISC */
if (v_lport != v_lport->root_lport) {
ret = unf_send_fdisc(v_lport, rport);
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN,
UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]LOGIN: Port(0x%x) send FDISC failed",
v_lport->port_id);
/* Do L_Port recovery */
unf_lport_error_recovery(v_lport);
}
} else {
ret = unf_send_flogi(v_lport, rport);
if (ret != RETURN_OK) {
UNF_TRACE(
UNF_EVTLOG_DRIVER_WARN,
UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]LOGIN: Port(0x%x) send FLOGI failed\n",
v_lport->port_id);
/* Do L_Port recovery */
unf_lport_error_recovery(v_lport);
}
}
return ret;
}
unsigned int unf_lport_name_server_register(
struct unf_lport_s *v_lport,
enum unf_lport_login_state_e states)
{
struct unf_rport_s *rport = NULL;
unsigned long flag = 0;
unsigned int ret = UNF_RETURN_ERROR;
UNF_CHECK_VALID(0x1809, UNF_TRUE,
v_lport, return UNF_RETURN_ERROR);
/* Get (safe) R_Port 0xfffffc */
rport = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_DIR_SERV);
rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY,
UNF_FC_FID_DIR_SERV);
if (!rport) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) allocate RPort failed",
v_lport->port_id);
return UNF_RETURN_ERROR;
}
/* Update R_Port & L_Port state */
spin_lock_irqsave(&rport->rport_state_lock, flag);
rport->nport_id = UNF_FC_FID_DIR_SERV; /* 0xfffffc */
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_NORMAL_ENTER);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
switch (states) {
/* RFT_ID */
case UNF_LPORT_ST_RFT_ID_WAIT:
ret = unf_send_rft_id(v_lport, rport);
break;
/* RFF_ID */
case UNF_LPORT_ST_RFF_ID_WAIT:
ret = unf_send_rff_id(v_lport, rport);
break;
/* SCR */
case UNF_LPORT_ST_SCR_WAIT:
ret = unf_send_scr(v_lport, NULL);
break;
/* PLOGI */
case UNF_LPORT_ST_PLOGI_WAIT:
default:
spin_lock_irqsave(&rport->rport_state_lock, flag);
unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI);
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
ret = unf_send_plogi(v_lport, rport);
break;
}
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]LOGIN: Port(0x%x) register fabric(0xfffffc) failed",
v_lport->nport_id);
/* Do L_Port recovery */
unf_lport_error_recovery(v_lport);
}
return ret;
}
unsigned int unf_lport_enter_sns_logo(struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport)
{
struct unf_rport_s *rport = NULL;
unsigned long flag = 0;
unsigned int ret = UNF_RETURN_ERROR;
UNF_CHECK_VALID(0x1810, UNF_TRUE,
v_lport, return UNF_RETURN_ERROR);
if (!v_rport) {
rport = unf_get_rport_by_nport_id(v_lport,
UNF_FC_FID_DIR_SERV);
} else {
rport = v_rport;
}
if (!rport) {
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_NORMAL_ENTER);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
return RETURN_OK;
}
/* Update L_Port & R_Port state */
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_NORMAL_ENTER);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
spin_lock_irqsave(&rport->rport_state_lock, flag);
unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO);
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
/* Do R_Port LOGO state */
unf_rport_enter_logo(v_lport, rport);
return ret;
}
void unf_lport_enter_sns_plogi(struct unf_lport_s *v_lport)
{
/* Fabric or Public Loop Mode: Login with Name server */
struct unf_lport_s *lport = v_lport;
struct unf_rport_s *rport = NULL;
unsigned long flag = 0;
unsigned int ret = UNF_RETURN_ERROR;
UNF_CHECK_VALID(0x1811, UNF_TRUE, v_lport, return);
/* Get (safe) R_Port 0xfffffc */
rport = unf_get_rport_by_nport_id(lport, UNF_FC_FID_DIR_SERV);
if (rport) {
/* for port swap: Delete old R_Port if necessary */
if (rport->local_nport_id != v_lport->nport_id) {
unf_rport_immediate_linkdown(v_lport, rport);
rport = NULL;
}
}
rport = unf_get_safe_rport(v_lport, rport,
UNF_RPORT_REUSE_ONLY, UNF_FC_FID_DIR_SERV);
if (!rport) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) allocate RPort failed",
v_lport->port_id);
unf_lport_error_recovery(lport);
return;
}
spin_lock_irqsave(&rport->rport_state_lock, flag);
rport->nport_id = UNF_FC_FID_DIR_SERV; /* 0xfffffc */
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
/* Send PLOGI to Fabric(0xfffffc) */
ret = unf_send_plogi(lport, rport);
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]LOGIN: Port(0x%x) send PLOGI to name server failed",
v_lport->port_id);
unf_lport_error_recovery(lport);
}
}
int unf_get_port_params(void *v_argin, void *v_argout)
{
struct unf_lport_s *lport = (struct unf_lport_s *)v_argin;
struct unf_low_level_port_mgr_op_s *port_mg = NULL;
struct unf_port_params_s port_params = { 0 };
int ret = RETURN_OK;
UNF_REFERNCE_VAR(v_argout);
UNF_CHECK_VALID(0x1812, UNF_TRUE,
v_argin, return UNF_RETURN_ERROR);
port_mg = &lport->low_level_func.port_mgr_op;
if (!port_mg->pfn_ll_port_config_get) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
"[warn]Port(0x%x) low level port_config_get function is NULL",
lport->port_id);
return UNF_RETURN_ERROR;
}
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_INFO,
"[warn]Port(0x%x) get parameters with default:R_A_TOV(%d) E_D_TOV(%d)",
lport->port_id, UNF_DEFAULT_FABRIC_RATOV, UNF_DEFAULT_EDTOV);
port_params.ra_tov = UNF_DEFAULT_FABRIC_RATOV;
port_params.ed_tov = UNF_DEFAULT_EDTOV;
/* Update parameters with Fabric mode */
if ((lport->en_act_topo == UNF_ACT_TOP_PUBLIC_LOOP) ||
(lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC)) {
lport->ra_tov = port_params.ra_tov;
lport->ed_tov = port_params.ed_tov;
}
return ret;
}
unsigned int unf_lport_enter_flogi(struct unf_lport_s *v_lport)
{
struct unf_rport_s *rport = NULL;
struct unf_cm_event_report *event = NULL;
unsigned long flag = 0;
unsigned int ret = UNF_RETURN_ERROR;
unsigned int nport_id = 0;
UNF_CHECK_VALID(0x1813, UNF_TRUE,
v_lport, return UNF_RETURN_ERROR);
/* Get (safe) R_Port */
nport_id = UNF_FC_FID_FLOGI; /* 0xfffffe */
rport = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_FLOGI);
rport = unf_get_safe_rport(v_lport, rport,
UNF_RPORT_REUSE_ONLY, nport_id);
if (!rport) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) allocate RPort failed",
v_lport->port_id);
return UNF_RETURN_ERROR;
}
/* Updtae L_Port state */
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
/* LPort: LINK UP --> FLOGI WAIT */
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_NORMAL_ENTER);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
/* Update R_Port N_Port_ID */
spin_lock_irqsave(&rport->rport_state_lock, flag);
rport->nport_id = UNF_FC_FID_FLOGI; /* 0xfffffe */
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
event = unf_get_one_event_node(v_lport);
if (event) {
event->lport = v_lport;
event->event_asy_flag = UNF_EVENT_ASYN;
/* NULL for timer */
event->pfn_unf_event_task = unf_get_port_params;
event->para_in = (void *)v_lport;
unf_post_one_event_node(v_lport, event);
}
if (v_lport != v_lport->root_lport) {
/* for NPIV */
ret = unf_send_fdisc(v_lport, rport);
if (ret != RETURN_OK)
/* Do L_Port recovery */
unf_lport_error_recovery(v_lport);
} else {
/* for Physical Port */
ret = unf_send_flogi(v_lport, rport);
if (ret != RETURN_OK)
/* Do L_Port recovery */
unf_lport_error_recovery(v_lport);
}
return ret;
}
void unf_set_lport_state(struct unf_lport_s *v_lport,
enum unf_lport_login_state_e states)
{
UNF_CHECK_VALID(0x1814, UNF_TRUE, v_lport, return);
if (states != v_lport->en_states) {
/* Reset L_Port retry count */
v_lport->retries = 0;
}
v_lport->en_states = states;
}
static void unf_lport_timeout(struct work_struct *work)
{
struct unf_lport_s *lport = NULL;
enum unf_lport_login_state_e state = UNF_LPORT_ST_READY;
unsigned long flag = 0;
UNF_CHECK_VALID(0x1815, UNF_TRUE, work, return);
lport = container_of(work, struct unf_lport_s, retry_work.work);
spin_lock_irqsave(&lport->lport_state_lock, flag);
state = lport->en_states;
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) is timeout with state(0x%x)",
lport->port_id, state);
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
switch (state) {
/* FLOGI retry */
case UNF_LPORT_ST_FLOGI_WAIT:
(void)unf_lport_retry_flogi(lport);
break;
case UNF_LPORT_ST_PLOGI_WAIT:
case UNF_LPORT_ST_RFT_ID_WAIT:
case UNF_LPORT_ST_RFF_ID_WAIT:
case UNF_LPORT_ST_SCR_WAIT:
(void)unf_lport_name_server_register(lport, state);
break;
/* Send LOGO External */
case UNF_LPORT_ST_LOGO:
break;
/* Do nothing */
case UNF_LPORT_ST_OFFLINE:
case UNF_LPORT_ST_READY:
case UNF_LPORT_ST_RESET:
case UNF_LPORT_ST_ONLINE:
case UNF_LPORT_ST_INITIAL:
case UNF_LPORT_ST_LINK_UP:
lport->retries = 0;
break;
default:
break;
}
unf_lport_ref_dec_to_destroy(lport);
}
void unf_lport_error_recovery(struct unf_lport_s *v_lport)
{
unsigned long delay = 0;
unsigned long flag = 0;
int ret = 0;
UNF_CHECK_VALID(0x1817, UNF_TRUE, v_lport, return);
if (unlikely(unf_lport_refinc(v_lport) != RETURN_OK)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) is removing and no need process",
v_lport->port_id);
return;
}
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
/* Port State: removing */
if (v_lport->b_port_removing == UNF_TRUE) {
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) is removing and no need process",
v_lport->port_id);
unf_lport_ref_dec_to_destroy(v_lport);
return;
}
/* Port State: offline */
if (v_lport->en_states == UNF_LPORT_ST_OFFLINE) {
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) is offline and no need process",
v_lport->port_id);
unf_lport_ref_dec_to_destroy(v_lport);
return;
}
/* Queue work state check */
if (delayed_work_pending(&v_lport->retry_work)) {
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
unf_lport_ref_dec_to_destroy(v_lport);
return;
}
/* Do retry operation */
if (v_lport->retries < v_lport->max_retry_count) {
v_lport->retries++;
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info]Port(0x%x_0x%x) enter recovery and retry %u times",
v_lport->port_id, v_lport->nport_id,
v_lport->retries);
delay = (unsigned long)v_lport->ed_tov;
ret = queue_delayed_work(unf_work_queue,
&v_lport->retry_work,
(unsigned long)msecs_to_jiffies(
(unsigned int)delay));
if (ret) {
atomic_inc(&v_lport->lport_ref_cnt);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO,
UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info]Port(0x%x) queue work success and reference count is %d",
v_lport->port_id,
atomic_read(&v_lport->lport_ref_cnt));
}
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
} else {
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_REMOTE_TIMEOUT);
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) register operation timeout and do LOGO",
v_lport->port_id);
/* Do L_Port LOGO */
(void)unf_lport_enter_sns_logo(v_lport, NULL);
}
unf_lport_ref_dec_to_destroy(v_lport);
}
struct unf_lport_s *unf_cm_lookup_vport_by_vp_index(struct unf_lport_s *v_lport,
unsigned short v_vp_index)
{
UNF_CHECK_VALID(0x1819, UNF_TRUE, v_lport, return NULL);
if (v_vp_index == 0)
return v_lport;
if (!v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_vp_index) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Port(0x%x) function do look up vport by index is NULL",
v_lport->port_id);
return NULL;
}
return v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_vp_index(
v_lport, v_vp_index);
}
struct unf_lport_s *unf_cm_lookup_vport_by_did(struct unf_lport_s *v_lport,
unsigned int v_did)
{
UNF_CHECK_VALID(0x1821, UNF_TRUE, v_lport, return NULL);
if (!v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_did) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Port(0x%x) function do look up vport by D_ID is NULL",
v_lport->port_id);
return NULL;
}
return v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_did(v_lport,
v_did);
}
struct unf_lport_s *unf_cm_lookup_vport_by_wwpn(struct unf_lport_s *v_lport,
unsigned long long v_wwpn)
{
UNF_CHECK_VALID(0x1822, UNF_TRUE, v_lport, return NULL);
if (!v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_wwpn) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Port(0x%x) function do look up vport by WWPN is NULL",
v_lport->port_id);
return NULL;
}
return v_lport->lport_mgr_temp.pfn_unf_lookup_vport_by_wwpn(v_lport,
v_wwpn);
}
void unf_cm_vport_remove(struct unf_lport_s *v_vport)
{
struct unf_lport_s *lport = NULL;
UNF_CHECK_VALID(0x1823, UNF_TRUE, v_vport, return);
lport = v_vport->root_lport;
UNF_CHECK_VALID(0x1824, UNF_TRUE, lport, return);
if (!lport->lport_mgr_temp.pfn_unf_vport_remove) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Port(0x%x) function do vport remove is NULL",
lport->port_id);
return;
}
lport->lport_mgr_temp.pfn_unf_vport_remove(v_vport);
}