mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 12:52:30 +00:00
37126399da
The bfa driver is full of state machines and a generic abstraction layer for them. This relies on casting function pointers, but that is no longer allowed when CONFIG_CFI_CLANG is enabled and causes a huge number of warnings like: drivers/scsi/bfa/bfad.c:169:3: error: cast from 'void (*)(struct bfad_s *, enum bfad_sm_event)' to 'bfa_sm_t' (aka 'void (*)(void *, int)') converts to incompatible function type [-Werror,-Wcast-function-type-strict] bfa_sm_set_state(bfad, bfad_sm_created); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rework the mechanism to no longer require the function pointer casts, by having separate types for each individual state machine. This in turn requires moving the enum definitions for each state machine into the header files in order to define the typedef. Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Link: https://lore.kernel.org/r/20240222124433.2046570-2-arnd@kernel.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
851 lines
22 KiB
C
851 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
|
|
* Copyright (c) 2014- QLogic Corporation.
|
|
* All rights reserved
|
|
* www.qlogic.com
|
|
*
|
|
* Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
|
|
*/
|
|
|
|
/*
|
|
* fcpim.c - FCP initiator mode i-t nexus state machine
|
|
*/
|
|
|
|
#include "bfad_drv.h"
|
|
#include "bfa_fcs.h"
|
|
#include "bfa_fcbuild.h"
|
|
#include "bfad_im.h"
|
|
#include "bfa_fcpim.h"
|
|
|
|
BFA_TRC_FILE(FCS, FCPIM);
|
|
|
|
/*
|
|
* forward declarations
|
|
*/
|
|
static void bfa_fcs_itnim_timeout(void *arg);
|
|
static void bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim);
|
|
static void bfa_fcs_itnim_send_prli(void *itnim_cbarg,
|
|
struct bfa_fcxp_s *fcxp_alloced);
|
|
static void bfa_fcs_itnim_prli_response(void *fcsarg,
|
|
struct bfa_fcxp_s *fcxp, void *cbarg,
|
|
bfa_status_t req_status, u32 rsp_len,
|
|
u32 resid_len, struct fchs_s *rsp_fchs);
|
|
static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_itnim_aen_event event);
|
|
|
|
static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event);
|
|
|
|
struct bfa_fcs_itnim_sm_table_s {
|
|
bfa_fcs_itnim_sm_t sm; /* state machine function */
|
|
enum bfa_itnim_state state; /* state machine encoding */
|
|
char *name; /* state name for display */
|
|
};
|
|
|
|
static inline enum bfa_itnim_state
|
|
bfa_fcs_itnim_sm_to_state(struct bfa_fcs_itnim_sm_table_s *smt, bfa_fcs_itnim_sm_t sm)
|
|
{
|
|
int i = 0;
|
|
|
|
while (smt[i].sm && smt[i].sm != sm)
|
|
i++;
|
|
return smt[i].state;
|
|
}
|
|
|
|
static struct bfa_fcs_itnim_sm_table_s itnim_sm_table[] = {
|
|
{BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE},
|
|
{BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND},
|
|
{BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT},
|
|
{BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY},
|
|
{BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE},
|
|
{BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE},
|
|
{BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE},
|
|
{BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR},
|
|
};
|
|
|
|
/*
|
|
* fcs_itnim_sm FCS itnim state machine
|
|
*/
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_FCS_ONLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send);
|
|
itnim->prli_retries = 0;
|
|
bfa_fcs_itnim_send_prli(itnim, NULL);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_INITIATOR:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_FRMSENT:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_INITIATOR:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
|
|
bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_RSP_OK:
|
|
if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR)
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
|
|
else
|
|
bfa_sm_set_state(itnim,
|
|
bfa_fcs_itnim_sm_hal_rport_online);
|
|
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_RSP_ERROR:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry);
|
|
bfa_timer_start(itnim->fcs->bfa, &itnim->timer,
|
|
bfa_fcs_itnim_timeout, itnim,
|
|
BFA_FCS_RETRY_TIMEOUT);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_RSP_NOT_SUPP:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcxp_discard(itnim->fcxp);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_INITIATOR:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
|
|
bfa_fcxp_discard(itnim->fcxp);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcxp_discard(itnim->fcxp);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_HAL_ONLINE:
|
|
if (!itnim->bfa_itnim)
|
|
itnim->bfa_itnim = bfa_itnim_create(itnim->fcs->bfa,
|
|
itnim->rport->bfa_rport, itnim);
|
|
|
|
if (itnim->bfa_itnim) {
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online);
|
|
bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec);
|
|
} else {
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_DELETE);
|
|
}
|
|
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_TIMEOUT:
|
|
if (itnim->prli_retries < BFA_FCS_RPORT_MAX_RETRIES) {
|
|
itnim->prli_retries++;
|
|
bfa_trc(itnim->fcs, itnim->prli_retries);
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send);
|
|
bfa_fcs_itnim_send_prli(itnim, NULL);
|
|
} else {
|
|
/* invoke target offline */
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP);
|
|
}
|
|
break;
|
|
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_timer_stop(&itnim->timer);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_INITIATOR:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
|
|
bfa_timer_stop(&itnim->timer);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_timer_stop(&itnim->timer);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
|
|
char lpwwn_buf[BFA_STRING_32];
|
|
char rpwwn_buf[BFA_STRING_32];
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_HCB_ONLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online);
|
|
bfa_fcb_itnim_online(itnim->itnim_drv);
|
|
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
|
|
wwn2str(rpwwn_buf, itnim->rport->pwwn);
|
|
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
|
|
"Target (WWN = %s) is online for initiator (WWN = %s)\n",
|
|
rpwwn_buf, lpwwn_buf);
|
|
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
|
|
bfa_itnim_offline(itnim->bfa_itnim);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
|
|
char lpwwn_buf[BFA_STRING_32];
|
|
char rpwwn_buf[BFA_STRING_32];
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
|
|
bfa_fcb_itnim_offline(itnim->itnim_drv);
|
|
bfa_itnim_offline(itnim->bfa_itnim);
|
|
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
|
|
wwn2str(rpwwn_buf, itnim->rport->pwwn);
|
|
if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE) {
|
|
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
|
|
"Target (WWN = %s) connectivity lost for "
|
|
"initiator (WWN = %s)\n", rpwwn_buf, lpwwn_buf);
|
|
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
|
|
} else {
|
|
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
|
|
"Target (WWN = %s) offlined by initiator (WWN = %s)\n",
|
|
rpwwn_buf, lpwwn_buf);
|
|
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
|
|
}
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_HCB_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This state is set when a discovered rport is also in intiator mode.
|
|
* This ITN is marked as no_op and is not active and will not be truned into
|
|
* online state.
|
|
*/
|
|
static void
|
|
bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_fcs_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_trc(itnim->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
|
|
break;
|
|
|
|
/*
|
|
* fcs_online is expected here for well known initiator ports
|
|
*/
|
|
case BFA_FCS_ITNIM_SM_FCS_ONLINE:
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_RSP_ERROR:
|
|
case BFA_FCS_ITNIM_SM_INITIATOR:
|
|
break;
|
|
|
|
case BFA_FCS_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
bfa_fcs_itnim_free(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(itnim->fcs, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
|
|
enum bfa_itnim_aen_event event)
|
|
{
|
|
struct bfa_fcs_rport_s *rport = itnim->rport;
|
|
struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
|
|
struct bfa_aen_entry_s *aen_entry;
|
|
|
|
/* Don't post events for well known addresses */
|
|
if (BFA_FCS_PID_IS_WKA(rport->pid))
|
|
return;
|
|
|
|
bfad_get_aen_entry(bfad, aen_entry);
|
|
if (!aen_entry)
|
|
return;
|
|
|
|
aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id;
|
|
aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn(
|
|
bfa_fcs_get_base_port(itnim->fcs));
|
|
aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
|
|
aen_entry->aen_data.itnim.rpwwn = rport->pwwn;
|
|
|
|
/* Send the AEN notification */
|
|
bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
|
|
BFA_AEN_CAT_ITNIM, event);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = itnim_cbarg;
|
|
struct bfa_fcs_rport_s *rport = itnim->rport;
|
|
struct bfa_fcs_lport_s *port = rport->port;
|
|
struct fchs_s fchs;
|
|
struct bfa_fcxp_s *fcxp;
|
|
int len;
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
|
|
fcxp = fcxp_alloced ? fcxp_alloced :
|
|
bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
|
|
if (!fcxp) {
|
|
itnim->stats.fcxp_alloc_wait++;
|
|
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe,
|
|
bfa_fcs_itnim_send_prli, itnim, BFA_TRUE);
|
|
return;
|
|
}
|
|
itnim->fcxp = fcxp;
|
|
|
|
len = fc_prli_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
|
|
itnim->rport->pid, bfa_fcs_lport_get_fcid(port), 0);
|
|
|
|
bfa_fcxp_send(fcxp, rport->bfa_rport, port->fabric->vf_id, port->lp_tag,
|
|
BFA_FALSE, FC_CLASS_3, len, &fchs,
|
|
bfa_fcs_itnim_prli_response, (void *)itnim,
|
|
FC_MAX_PDUSZ, FC_ELS_TOV);
|
|
|
|
itnim->stats.prli_sent++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
|
|
bfa_status_t req_status, u32 rsp_len,
|
|
u32 resid_len, struct fchs_s *rsp_fchs)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg;
|
|
struct fc_els_cmd_s *els_cmd;
|
|
struct fc_prli_s *prli_resp;
|
|
struct fc_ls_rjt_s *ls_rjt;
|
|
struct fc_prli_params_s *sparams;
|
|
|
|
bfa_trc(itnim->fcs, req_status);
|
|
|
|
/*
|
|
* Sanity Checks
|
|
*/
|
|
if (req_status != BFA_STATUS_OK) {
|
|
itnim->stats.prli_rsp_err++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR);
|
|
return;
|
|
}
|
|
|
|
els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
|
|
|
|
if (els_cmd->els_code == FC_ELS_ACC) {
|
|
prli_resp = (struct fc_prli_s *) els_cmd;
|
|
|
|
if (fc_prli_rsp_parse(prli_resp, rsp_len) != FC_PARSE_OK) {
|
|
bfa_trc(itnim->fcs, rsp_len);
|
|
/*
|
|
* Check if this r-port is also in Initiator mode.
|
|
* If so, we need to set this ITN as a no-op.
|
|
*/
|
|
if (prli_resp->parampage.servparams.initiator) {
|
|
bfa_trc(itnim->fcs, prli_resp->parampage.type);
|
|
itnim->rport->scsi_function =
|
|
BFA_RPORT_INITIATOR;
|
|
itnim->stats.prli_rsp_acc++;
|
|
itnim->stats.initiator++;
|
|
bfa_sm_send_event(itnim,
|
|
BFA_FCS_ITNIM_SM_RSP_OK);
|
|
return;
|
|
}
|
|
|
|
itnim->stats.prli_rsp_parse_err++;
|
|
return;
|
|
}
|
|
itnim->rport->scsi_function = BFA_RPORT_TARGET;
|
|
|
|
sparams = &prli_resp->parampage.servparams;
|
|
itnim->seq_rec = sparams->retry;
|
|
itnim->rec_support = sparams->rec_support;
|
|
itnim->task_retry_id = sparams->task_retry_id;
|
|
itnim->conf_comp = sparams->confirm;
|
|
|
|
itnim->stats.prli_rsp_acc++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK);
|
|
} else {
|
|
ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp);
|
|
|
|
bfa_trc(itnim->fcs, ls_rjt->reason_code);
|
|
bfa_trc(itnim->fcs, ls_rjt->reason_code_expl);
|
|
|
|
itnim->stats.prli_rsp_rjt++;
|
|
if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_NOT_SUPP);
|
|
return;
|
|
}
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_timeout(void *arg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) arg;
|
|
|
|
itnim->stats.timeout++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
if (itnim->bfa_itnim) {
|
|
bfa_itnim_delete(itnim->bfa_itnim);
|
|
itnim->bfa_itnim = NULL;
|
|
}
|
|
|
|
bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* itnim_public FCS ITNIM public interfaces
|
|
*/
|
|
|
|
/*
|
|
* Called by rport when a new rport is created.
|
|
*
|
|
* @param[in] rport - remote port.
|
|
*/
|
|
struct bfa_fcs_itnim_s *
|
|
bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport)
|
|
{
|
|
struct bfa_fcs_lport_s *port = rport->port;
|
|
struct bfa_fcs_itnim_s *itnim;
|
|
struct bfad_itnim_s *itnim_drv;
|
|
int ret;
|
|
|
|
/*
|
|
* call bfad to allocate the itnim
|
|
*/
|
|
ret = bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv);
|
|
if (ret) {
|
|
bfa_trc(port->fcs, rport->pwwn);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Initialize itnim
|
|
*/
|
|
itnim->rport = rport;
|
|
itnim->fcs = rport->fcs;
|
|
itnim->itnim_drv = itnim_drv;
|
|
|
|
itnim->bfa_itnim = NULL;
|
|
itnim->seq_rec = BFA_FALSE;
|
|
itnim->rec_support = BFA_FALSE;
|
|
itnim->conf_comp = BFA_FALSE;
|
|
itnim->task_retry_id = BFA_FALSE;
|
|
|
|
/*
|
|
* Set State machine
|
|
*/
|
|
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
|
|
|
|
return itnim;
|
|
}
|
|
|
|
/*
|
|
* Called by rport to delete the instance of FCPIM.
|
|
*
|
|
* @param[in] rport - remote port.
|
|
*/
|
|
void
|
|
bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pid);
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE);
|
|
}
|
|
|
|
/*
|
|
* Notification from rport that PLOGI is complete to initiate FC-4 session.
|
|
*/
|
|
void
|
|
bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
itnim->stats.onlines++;
|
|
|
|
if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid))
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HAL_ONLINE);
|
|
}
|
|
|
|
/*
|
|
* Called by rport to handle a remote device offline.
|
|
*/
|
|
void
|
|
bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
itnim->stats.offlines++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE);
|
|
}
|
|
|
|
/*
|
|
* Called by rport when remote port is known to be an initiator from
|
|
* PRLI received.
|
|
*/
|
|
void
|
|
bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pid);
|
|
itnim->stats.initiator++;
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR);
|
|
}
|
|
|
|
/*
|
|
* Called by rport to check if the itnim is online.
|
|
*/
|
|
bfa_status_t
|
|
bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim)
|
|
{
|
|
bfa_trc(itnim->fcs, itnim->rport->pid);
|
|
switch (bfa_fcs_itnim_sm_to_state(itnim_sm_table, itnim->sm)) {
|
|
case BFA_ITNIM_ONLINE:
|
|
case BFA_ITNIM_INITIATIOR:
|
|
return BFA_STATUS_OK;
|
|
|
|
default:
|
|
return BFA_STATUS_NO_FCPIM_NEXUS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BFA completion callback for bfa_itnim_online().
|
|
*/
|
|
void
|
|
bfa_cb_itnim_online(void *cbarg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg;
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE);
|
|
}
|
|
|
|
/*
|
|
* BFA completion callback for bfa_itnim_offline().
|
|
*/
|
|
void
|
|
bfa_cb_itnim_offline(void *cb_arg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE);
|
|
}
|
|
|
|
/*
|
|
* Mark the beginning of PATH TOV handling. IO completion callbacks
|
|
* are still pending.
|
|
*/
|
|
void
|
|
bfa_cb_itnim_tov_begin(void *cb_arg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
}
|
|
|
|
/*
|
|
* Mark the end of PATH TOV handling. All pending IOs are already cleaned up.
|
|
*/
|
|
void
|
|
bfa_cb_itnim_tov(void *cb_arg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
|
|
struct bfad_itnim_s *itnim_drv = itnim->itnim_drv;
|
|
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
itnim_drv->state = ITNIM_STATE_TIMEOUT;
|
|
}
|
|
|
|
/*
|
|
* BFA notification to FCS/driver for second level error recovery.
|
|
*
|
|
* Atleast one I/O request has timedout and target is unresponsive to
|
|
* repeated abort requests. Second level error recovery should be initiated
|
|
* by starting implicit logout and recovery procedures.
|
|
*/
|
|
void
|
|
bfa_cb_itnim_sler(void *cb_arg)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
|
|
|
|
itnim->stats.sler++;
|
|
bfa_trc(itnim->fcs, itnim->rport->pwwn);
|
|
bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP);
|
|
}
|
|
|
|
struct bfa_fcs_itnim_s *
|
|
bfa_fcs_itnim_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
|
|
{
|
|
struct bfa_fcs_rport_s *rport;
|
|
rport = bfa_fcs_rport_lookup(port, rpwwn);
|
|
|
|
if (!rport)
|
|
return NULL;
|
|
|
|
WARN_ON(rport->itnim == NULL);
|
|
return rport->itnim;
|
|
}
|
|
|
|
bfa_status_t
|
|
bfa_fcs_itnim_attr_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn,
|
|
struct bfa_itnim_attr_s *attr)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = NULL;
|
|
|
|
itnim = bfa_fcs_itnim_lookup(port, rpwwn);
|
|
|
|
if (itnim == NULL)
|
|
return BFA_STATUS_NO_FCPIM_NEXUS;
|
|
|
|
attr->state = bfa_fcs_itnim_sm_to_state(itnim_sm_table, itnim->sm);
|
|
attr->retry = itnim->seq_rec;
|
|
attr->rec_support = itnim->rec_support;
|
|
attr->conf_comp = itnim->conf_comp;
|
|
attr->task_retry_id = itnim->task_retry_id;
|
|
return BFA_STATUS_OK;
|
|
}
|
|
|
|
bfa_status_t
|
|
bfa_fcs_itnim_stats_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn,
|
|
struct bfa_itnim_stats_s *stats)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = NULL;
|
|
|
|
WARN_ON(port == NULL);
|
|
|
|
itnim = bfa_fcs_itnim_lookup(port, rpwwn);
|
|
|
|
if (itnim == NULL)
|
|
return BFA_STATUS_NO_FCPIM_NEXUS;
|
|
|
|
memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s));
|
|
|
|
return BFA_STATUS_OK;
|
|
}
|
|
|
|
bfa_status_t
|
|
bfa_fcs_itnim_stats_clear(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
|
|
{
|
|
struct bfa_fcs_itnim_s *itnim = NULL;
|
|
|
|
WARN_ON(port == NULL);
|
|
|
|
itnim = bfa_fcs_itnim_lookup(port, rpwwn);
|
|
|
|
if (itnim == NULL)
|
|
return BFA_STATUS_NO_FCPIM_NEXUS;
|
|
|
|
memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s));
|
|
return BFA_STATUS_OK;
|
|
}
|
|
|
|
void
|
|
bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim,
|
|
struct fchs_s *fchs, u16 len)
|
|
{
|
|
struct fc_els_cmd_s *els_cmd;
|
|
|
|
bfa_trc(itnim->fcs, fchs->type);
|
|
|
|
if (fchs->type != FC_TYPE_ELS)
|
|
return;
|
|
|
|
els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
|
|
|
|
bfa_trc(itnim->fcs, els_cmd->els_code);
|
|
|
|
switch (els_cmd->els_code) {
|
|
case FC_ELS_PRLO:
|
|
bfa_fcs_rport_prlo(itnim->rport, fchs->ox_id);
|
|
break;
|
|
|
|
default:
|
|
WARN_ON(1);
|
|
}
|
|
}
|