in qla2xxx/qla_target.c [4979:5277]
static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *iocb)
{
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
struct qla_hw_data *ha = vha->hw;
struct fc_port *sess = NULL, *conflict_sess = NULL;
uint64_t wwn;
port_id_t port_id;
uint16_t loop_id;
uint16_t wd3_lo;
int res = 0;
unsigned long flags;
lockdep_assert_held(&ha->hardware_lock);
wwn = wwn_to_u64(iocb->u.isp24.port_name);
port_id.b.domain = iocb->u.isp24.port_id[2];
port_id.b.area = iocb->u.isp24.port_id[1];
port_id.b.al_pa = iocb->u.isp24.port_id[0];
port_id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
ql_dbg(ql_dbg_disc, vha, 0xf026,
"qla_target(%d): Port ID: %02x:%02x:%02x ELS opcode: 0x%02x lid %d %8phC\n",
vha->vp_idx, iocb->u.isp24.port_id[2],
iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
iocb->u.isp24.status_subcode, loop_id,
iocb->u.isp24.port_name);
/* res = 1 means ack at the end of thread
* res = 0 means ack async/later.
*/
switch (iocb->u.isp24.status_subcode) {
case ELS_PLOGI:
res = qlt_handle_login(vha, iocb);
break;
case ELS_PRLI:
if (N2N_TOPO(ha)) {
sess = qla2x00_find_fcport_by_wwpn(vha,
iocb->u.isp24.port_name, 1);
if (vha->hw->flags.edif_enabled && sess &&
(!(sess->flags & FCF_FCSP_DEVICE) ||
!sess->edif.authok)) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC Term PRLI due to unauthorize PRLI\n",
__func__, __LINE__, iocb->u.isp24.port_name);
qlt_send_term_imm_notif(vha, iocb, 1);
break;
}
if (sess && sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC Term PRLI due to PLOGI ACK not completed\n",
__func__, __LINE__,
iocb->u.isp24.port_name);
qlt_send_term_imm_notif(vha, iocb, 1);
break;
}
res = qlt_handle_login(vha, iocb);
break;
}
if (IS_SW_RESV_ADDR(port_id)) {
res = 1;
break;
}
wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);
if (wwn) {
spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
sess = qlt_find_sess_invalidate_other(vha, wwn, port_id,
loop_id, &conflict_sess);
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
}
if (conflict_sess) {
switch (conflict_sess->disc_state) {
case DSC_DELETED:
case DSC_DELETE_PEND:
break;
default:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b,
"PRLI with conflicting sess %p port %8phC\n",
conflict_sess, conflict_sess->port_name);
conflict_sess->fw_login_state =
DSC_LS_PORT_UNAVAIL;
qlt_send_term_imm_notif(vha, iocb, 1);
res = 0;
break;
}
}
if (sess != NULL) {
bool delete = false;
int sec;
if (vha->hw->flags.edif_enabled && sess &&
(!(sess->flags & FCF_FCSP_DEVICE) ||
!sess->edif.authok)) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC Term PRLI due to unauthorize prli\n",
__func__, __LINE__, iocb->u.isp24.port_name);
qlt_send_term_imm_notif(vha, iocb, 1);
break;
}
spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
switch (sess->fw_login_state) {
case DSC_LS_PLOGI_PEND:
case DSC_LS_PLOGI_COMP:
case DSC_LS_PRLI_COMP:
break;
default:
delete = true;
break;
}
switch (sess->disc_state) {
case DSC_UPD_FCPORT:
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
flags);
sec = jiffies_to_msecs(jiffies -
sess->jiffies_at_registration)/1000;
if (sess->sec_since_registration < sec && sec &&
!(sec % 5)) {
sess->sec_since_registration = sec;
ql_dbg(ql_dbg_disc, sess->vha, 0xffff,
"%s %8phC : Slow Rport registration(%d Sec)\n",
__func__, sess->port_name, sec);
}
qlt_send_term_imm_notif(vha, iocb, 1);
return 0;
case DSC_LOGIN_PEND:
case DSC_GPDB:
case DSC_LOGIN_COMPLETE:
case DSC_ADISC:
delete = false;
break;
default:
break;
}
if (delete) {
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
flags);
/*
* Impatient initiator sent PRLI before last
* PLOGI could finish. Will force him to re-try,
* while last one finishes.
*/
ql_log(ql_log_warn, sess->vha, 0xf095,
"sess %p PRLI received, before plogi ack.\n",
sess);
qlt_send_term_imm_notif(vha, iocb, 1);
res = 0;
break;
}
/*
* This shouldn't happen under normal circumstances,
* since we have deleted the old session during PLOGI
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096,
"PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
sess->loop_id, sess, iocb->u.isp24.nport_handle);
sess->local = 0;
sess->loop_id = loop_id;
sess->d_id = port_id;
sess->fw_login_state = DSC_LS_PRLI_PEND;
if (wd3_lo & BIT_7)
sess->conf_compl_supported = 1;
if ((wd3_lo & BIT_4) == 0)
sess->port_type = FCT_INITIATOR;
else
sess->port_type = FCT_TARGET;
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
}
res = 1; /* send notify ack */
/* Make session global (not used in fabric mode) */
if (ha->current_topology != ISP_CFG_F) {
if (sess) {
ql_dbg(ql_dbg_disc, vha, 0x20fa,
"%s %d %8phC post nack\n",
__func__, __LINE__, sess->port_name);
qla24xx_post_nack_work(vha, sess, iocb,
SRB_NACK_PRLI);
res = 0;
} else {
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
} else {
if (sess) {
ql_dbg(ql_dbg_disc, vha, 0x20fb,
"%s %d %8phC post nack\n",
__func__, __LINE__, sess->port_name);
qla24xx_post_nack_work(vha, sess, iocb,
SRB_NACK_PRLI);
res = 0;
}
}
break;
case ELS_TPRLO:
if (le16_to_cpu(iocb->u.isp24.flags) &
NOTIFY24XX_FLAGS_GLOBAL_TPRLO) {
loop_id = 0xFFFF;
qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS);
res = 1;
break;
}
fallthrough;
case ELS_LOGO:
case ELS_PRLO:
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
sess = qla2x00_find_fcport_by_loopid(vha, loop_id);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
if (sess) {
sess->login_gen++;
sess->fw_login_state = DSC_LS_LOGO_PEND;
sess->logo_ack_needed = 1;
memcpy(sess->iocb, iocb, IOCB_SIZE);
}
res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
ql_dbg(ql_dbg_disc, vha, 0x20fc,
"%s: logo %llx res %d sess %p ",
__func__, wwn, res, sess);
if (res == 0) {
/*
* cmd went upper layer, look for qlt_xmit_tm_rsp()
* for LOGO_ACK & sess delete
*/
BUG_ON(!sess);
res = 0;
} else {
/* cmd did not go to upper layer. */
if (sess) {
qlt_schedule_sess_for_deletion(sess);
res = 0;
}
/* else logo will be ack */
}
break;
case ELS_PDISC:
case ELS_ADISC:
{
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
if (tgt->link_reinit_iocb_pending) {
qlt_send_notify_ack(ha->base_qpair,
&tgt->link_reinit_iocb, 0, 0, 0, 0, 0, 0);
tgt->link_reinit_iocb_pending = 0;
}
sess = qla2x00_find_fcport_by_wwpn(vha,
iocb->u.isp24.port_name, 1);
if (sess) {
ql_dbg(ql_dbg_disc, vha, 0x20fd,
"sess %p lid %d|%d DS %d LS %d\n",
sess, sess->loop_id, loop_id,
sess->disc_state, sess->fw_login_state);
}
res = 1; /* send notify ack */
break;
}
case ELS_FLOGI: /* should never happen */
default:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
"qla_target(%d): Unsupported ELS command %x "
"received\n", vha->vp_idx, iocb->u.isp24.status_subcode);
res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
break;
}
ql_dbg(ql_dbg_disc, vha, 0xf026,
"qla_target(%d): Exit ELS opcode: 0x%02x res %d\n",
vha->vp_idx, iocb->u.isp24.status_subcode, res);
return res;
}