Commit 48acad09 authored by Quinn Tran's avatar Quinn Tran Committed by Martin K. Petersen

scsi: qla2xxx: Fix N2N link re-connect

In case of N2N connect, sg_reset for bus/device/host was causing driver and
firmware state to go out of sync.  This patch fixes this link instablity
when reconnect is attempted after link flap.
Signed-off-by: default avatarQuinn Tran <quinn.tran@cavium.com>
Signed-off-by: default avatarHimanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4ae5716b
...@@ -377,6 +377,7 @@ struct srb_iocb { ...@@ -377,6 +377,7 @@ struct srb_iocb {
#define SRB_LOGIN_COND_PLOGI BIT_1 #define SRB_LOGIN_COND_PLOGI BIT_1
#define SRB_LOGIN_SKIP_PRLI BIT_2 #define SRB_LOGIN_SKIP_PRLI BIT_2
#define SRB_LOGIN_NVME_PRLI BIT_3 #define SRB_LOGIN_NVME_PRLI BIT_3
#define SRB_LOGIN_PRLI_ONLY BIT_4
uint16_t data[2]; uint16_t data[2];
u32 iop[2]; u32 iop[2];
} logio; } logio;
...@@ -4236,7 +4237,7 @@ typedef struct scsi_qla_host { ...@@ -4236,7 +4237,7 @@ typedef struct scsi_qla_host {
#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ #define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */
#define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */ #define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */
#define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */ #define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */
#define FREE_BIT 21 #define N2N_LINK_RESET 21
#define PORT_UPDATE_NEEDED 22 #define PORT_UPDATE_NEEDED 22
#define FX00_RESET_RECOVERY 23 #define FX00_RESET_RECOVERY 23
#define FX00_TARGET_SCAN 24 #define FX00_TARGET_SCAN 24
......
...@@ -160,6 +160,22 @@ qla2x00_async_login_sp_done(void *ptr, int res) ...@@ -160,6 +160,22 @@ qla2x00_async_login_sp_done(void *ptr, int res)
sp->free(sp); sp->free(sp);
} }
static inline bool
fcport_is_smaller(fc_port_t *fcport)
{
if (wwn_to_u64(fcport->port_name) <
wwn_to_u64(fcport->vha->port_name))
return true;
else
return false;
}
static inline bool
fcport_is_bigger(fc_port_t *fcport)
{
return !fcport_is_smaller(fcport);
}
int int
qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data) uint16_t *data)
...@@ -189,6 +205,9 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -189,6 +205,9 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->done = qla2x00_async_login_sp_done; sp->done = qla2x00_async_login_sp_done;
if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport)) {
lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY;
} else {
lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
if (fcport->fc4f_nvme) if (fcport->fc4f_nvme)
...@@ -196,6 +215,8 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -196,6 +215,8 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
if (data[1] & QLA_LOGIO_LOGIN_RETRIED) if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED; lio->u.logio.flags |= SRB_LOGIN_RETRIED;
}
rval = qla2x00_start_sp(sp); rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) { if (rval != QLA_SUCCESS) {
fcport->flags |= FCF_LOGIN_NEEDED; fcport->flags |= FCF_LOGIN_NEEDED;
...@@ -497,15 +518,18 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, ...@@ -497,15 +518,18 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
e = &vha->gnl.l[i]; e = &vha->gnl.l[i];
wwn = wwn_to_u64(e->port_name); wwn = wwn_to_u64(e->port_name);
id.b.domain = e->port_id[2];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[0];
id.b.rsvd_1 = 0;
if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE)) if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE))
continue; continue;
if (IS_SW_RESV_ADDR(id))
continue;
found = 1; found = 1;
id.b.domain = e->port_id[2];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[0];
id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(e->nport_handle); loop_id = le16_to_cpu(e->nport_handle);
loop_id = (loop_id & 0x7fff); loop_id = (loop_id & 0x7fff);
...@@ -518,15 +542,19 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, ...@@ -518,15 +542,19 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, loop_id, fcport->loop_id); fcport->d_id.b.al_pa, loop_id, fcport->loop_id);
switch (fcport->disc_state) {
case DSC_DELETE_PEND:
case DSC_DELETED:
break;
default:
if ((id.b24 != fcport->d_id.b24) || if ((id.b24 != fcport->d_id.b24) ||
((fcport->loop_id != FC_NO_LOOP_ID) && ((fcport->loop_id != FC_NO_LOOP_ID) &&
(fcport->loop_id != loop_id))) { (fcport->loop_id != loop_id))) {
ql_dbg(ql_dbg_disc, vha, 0x20e3,
"%s %d %8phC post del sess\n",
__func__, __LINE__, fcport->port_name);
qlt_schedule_sess_for_deletion(fcport); qlt_schedule_sess_for_deletion(fcport);
return; return;
} }
break;
}
fcport->loop_id = loop_id; fcport->loop_id = loop_id;
...@@ -549,36 +577,77 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, ...@@ -549,36 +577,77 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
else else
current_login_state = e->current_login_state & 0xf; current_login_state = e->current_login_state & 0xf;
switch (vha->hw->current_topology) {
default:
switch (current_login_state) { switch (current_login_state) {
case DSC_LS_PRLI_COMP: case DSC_LS_PRLI_COMP:
ql_dbg(ql_dbg_disc, vha, 0x20e4, ql_dbg(ql_dbg_disc + ql_dbg_verbose,
"%s %d %8phC post gpdb\n", vha, 0x20e4, "%s %d %8phC post gpdb\n",
__func__, __LINE__, fcport->port_name); __func__, __LINE__, fcport->port_name);
if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) if ((e->prli_svc_param_word_3[0] & BIT_4) == 0)
fcport->port_type = FCT_INITIATOR; fcport->port_type = FCT_INITIATOR;
else else
fcport->port_type = FCT_TARGET; fcport->port_type = FCT_TARGET;
data[0] = data[1] = 0; data[0] = data[1] = 0;
qla2x00_post_async_adisc_work(vha, fcport, data); qla2x00_post_async_adisc_work(vha, fcport,
data);
break; break;
case DSC_LS_PORT_UNAVAIL: case DSC_LS_PORT_UNAVAIL:
default: default:
if (fcport->loop_id == FC_NO_LOOP_ID) { if (fcport->loop_id != FC_NO_LOOP_ID)
qla2x00_find_new_loop_id(vha, fcport); qla2x00_clear_loop_id(fcport);
fcport->loop_id = loop_id;
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
}
ql_dbg(ql_dbg_disc, vha, 0x20e5,
"%s %d %8phC\n",
__func__, __LINE__, fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport); qla24xx_fcport_handle_login(vha, fcport);
break; break;
} }
break;
case ISP_CFG_N:
switch (current_login_state) {
case DSC_LS_PRLI_COMP:
if ((e->prli_svc_param_word_3[0] & BIT_4) == 0)
fcport->port_type = FCT_INITIATOR;
else
fcport->port_type = FCT_TARGET;
data[0] = data[1] = 0;
qla2x00_post_async_adisc_work(vha, fcport,
data);
break;
case DSC_LS_PLOGI_COMP:
if (fcport_is_bigger(fcport)) {
/* local adapter is smaller */
if (fcport->loop_id != FC_NO_LOOP_ID)
qla2x00_clear_loop_id(fcport);
fcport->loop_id = loop_id;
qla24xx_fcport_handle_login(vha,
fcport);
break;
}
/* drop through */
default:
if (fcport_is_smaller(fcport)) {
/* local adapter is bigger */
if (fcport->loop_id != FC_NO_LOOP_ID)
qla2x00_clear_loop_id(fcport);
fcport->loop_id = loop_id;
qla24xx_fcport_handle_login(vha,
fcport);
}
break;
}
break;
} /* switch (ha->current_topology) */
} }
if (!found) { if (!found) {
/* fw has no record of this port */ switch (vha->hw->current_topology) {
case ISP_CFG_F:
case ISP_CFG_FL:
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
e = &vha->gnl.l[i]; e = &vha->gnl.l[i];
id.b.domain = e->port_id[0]; id.b.domain = e->port_id[0];
...@@ -591,19 +660,34 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, ...@@ -591,19 +660,34 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
conflict_fcport = conflict_fcport =
qla2x00_find_fcport_by_wwpn(vha, qla2x00_find_fcport_by_wwpn(vha,
e->port_name, 0); e->port_name, 0);
ql_dbg(ql_dbg_disc, vha, 0x20e6, ql_dbg(ql_dbg_disc + ql_dbg_verbose,
vha, 0x20e5,
"%s %d %8phC post del sess\n", "%s %d %8phC post del sess\n",
__func__, __LINE__, __func__, __LINE__,
conflict_fcport->port_name); conflict_fcport->port_name);
qlt_schedule_sess_for_deletion qlt_schedule_sess_for_deletion
(conflict_fcport); (conflict_fcport);
} }
/*
/* FW already picked this loop id for another fcport */ * FW already picked this loop id for
* another fcport
*/
if (fcport->loop_id == loop_id) if (fcport->loop_id == loop_id)
fcport->loop_id = FC_NO_LOOP_ID; fcport->loop_id = FC_NO_LOOP_ID;
} }
qla24xx_fcport_handle_login(vha, fcport); qla24xx_fcport_handle_login(vha, fcport);
break;
case ISP_CFG_N:
/*
* FW handles the initial login for n2n.
* Do link reinit to trigger this auto login.
*/
set_bit(N2N_LINK_RESET, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
break;
default:
break;
}
} }
} /* gnl_event */ } /* gnl_event */
...@@ -4590,21 +4674,11 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) ...@@ -4590,21 +4674,11 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
} else if (ha->current_topology == ISP_CFG_N) { } else if (ha->current_topology == ISP_CFG_N) {
clear_bit(RSCN_UPDATE, &flags); clear_bit(RSCN_UPDATE, &flags);
if (ha->flags.rida_fmt2) {
/* With Rida Format 2, the login is already triggered.
* We know who is on the other side of the wire.
* No need to login to do login to find out or drop into
* qla2x00_configure_local_loop().
*/
clear_bit(LOCAL_LOOP_UPDATE, &flags);
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
} else {
if (qla_tgt_mode_enabled(vha)) { if (qla_tgt_mode_enabled(vha)) {
/* allow the other side to start the login */ /* allow the other side to start the login */
clear_bit(LOCAL_LOOP_UPDATE, &flags); clear_bit(LOCAL_LOOP_UPDATE, &flags);
set_bit(RELOGIN_NEEDED, &vha->dpc_flags); set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
} }
}
} else if (ha->current_topology == ISP_CFG_NL) { } else if (ha->current_topology == ISP_CFG_NL) {
clear_bit(RSCN_UPDATE, &flags); clear_bit(RSCN_UPDATE, &flags);
set_bit(LOCAL_LOOP_UPDATE, &flags); set_bit(LOCAL_LOOP_UPDATE, &flags);
...@@ -7929,7 +8003,6 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) ...@@ -7929,7 +8003,6 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
} }
/* enable RIDA Format2 */ /* enable RIDA Format2 */
if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha))
icb->firmware_options_3 |= BIT_0; icb->firmware_options_3 |= BIT_0;
if (IS_QLA27XX(ha)) { if (IS_QLA27XX(ha)) {
......
...@@ -2240,12 +2240,15 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) ...@@ -2240,12 +2240,15 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
struct srb_iocb *lio = &sp->u.iocb_cmd; struct srb_iocb *lio = &sp->u.iocb_cmd;
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
if (lio->u.logio.flags & SRB_LOGIN_PRLI_ONLY) {
logio->control_flags = cpu_to_le16(LCF_COMMAND_PRLI);
} else {
logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI); logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI);
if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI) if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI)
logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI); logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI) if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI)
logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI); logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
}
logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[0] = sp->fcport->d_id.b.al_pa;
logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[1] = sp->fcport->d_id.b.area;
......
...@@ -908,6 +908,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) ...@@ -908,6 +908,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
if (!atomic_read(&vha->loop_down_timer)) if (!atomic_read(&vha->loop_down_timer))
atomic_set(&vha->loop_down_timer, atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME); LOOP_DOWN_TIME);
if (!N2N_TOPO(ha))
qla2x00_mark_all_devices_lost(vha, 1); qla2x00_mark_all_devices_lost(vha, 1);
} }
......
...@@ -2177,7 +2177,10 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) ...@@ -2177,7 +2177,10 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
mcp->out_mb = MBX_2|MBX_1|MBX_0; mcp->out_mb = MBX_2|MBX_1|MBX_0;
} else if (IS_FWI2_CAPABLE(vha->hw)) { } else if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[0] = MBC_LIP_FULL_LOGIN;
mcp->mb[1] = BIT_6; if (N2N_TOPO(vha->hw))
mcp->mb[1] = BIT_4; /* re-init */
else
mcp->mb[1] = BIT_6; /* LIP */
mcp->mb[2] = 0; mcp->mb[2] = 0;
mcp->mb[3] = vha->hw->loop_reset_delay; mcp->mb[3] = vha->hw->loop_reset_delay;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
...@@ -3911,28 +3914,6 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, ...@@ -3911,28 +3914,6 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (fcport) { if (fcport) {
fcport->plogi_nack_done_deadline = jiffies + HZ; fcport->plogi_nack_done_deadline = jiffies + HZ;
fcport->scan_state = QLA_FCPORT_FOUND; fcport->scan_state = QLA_FCPORT_FOUND;
switch (fcport->disc_state) {
case DSC_DELETED:
ql_dbg(ql_dbg_disc, vha, 0x210d,
"%s %d %8phC login\n",
__func__, __LINE__, fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport);
break;
case DSC_DELETE_PEND:
break;
default:
qlt_schedule_sess_for_deletion(fcport);
break;
}
} else {
id.b.al_pa = rptid_entry->u.f2.remote_nport_id[0];
id.b.area = rptid_entry->u.f2.remote_nport_id[1];
id.b.domain = rptid_entry->u.f2.remote_nport_id[2];
qla24xx_post_newsess_work(vha, &id,
rptid_entry->u.f2.port_name,
rptid_entry->u.f2.node_name,
NULL,
FC4_TYPE_UNKNOWN);
} }
} }
} }
......
...@@ -6186,6 +6186,11 @@ qla2x00_do_dpc(void *data) ...@@ -6186,6 +6186,11 @@ qla2x00_do_dpc(void *data)
if (!IS_QLAFX00(ha)) if (!IS_QLAFX00(ha))
qla2x00_do_dpc_all_vps(base_vha); qla2x00_do_dpc_all_vps(base_vha);
if (test_and_clear_bit(N2N_LINK_RESET,
&base_vha->dpc_flags)) {
qla2x00_lip_reset(base_vha);
}
ha->dpc_active = 0; ha->dpc_active = 0;
end_loop: end_loop:
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
......
...@@ -805,6 +805,10 @@ qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id, ...@@ -805,6 +805,10 @@ qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id,
list_for_each_entry(pla, &vha->plogi_ack_list, list) { list_for_each_entry(pla, &vha->plogi_ack_list, list) {
if (pla->id.b24 == id->b24) { if (pla->id.b24 == id->b24) {
ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0x210d,
"%s %d %8phC Term INOT due to new INOT",
__func__, __LINE__,
pla->iocb.u.isp24.port_name);
qlt_send_term_imm_notif(vha, &pla->iocb, 1); qlt_send_term_imm_notif(vha, &pla->iocb, 1);
memcpy(&pla->iocb, iocb, sizeof(pla->iocb)); memcpy(&pla->iocb, iocb, sizeof(pla->iocb));
return pla; return pla;
...@@ -1073,6 +1077,7 @@ void qlt_free_session_done(struct work_struct *work) ...@@ -1073,6 +1077,7 @@ void qlt_free_session_done(struct work_struct *work)
struct qlt_plogi_ack_t *con = struct qlt_plogi_ack_t *con =
sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]; sess->plogi_link[QLT_PLOGI_LINK_CONFLICT];
struct imm_ntfy_from_isp *iocb; struct imm_ntfy_from_isp *iocb;
own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN];
if (con) { if (con) {
iocb = &con->iocb; iocb = &con->iocb;
...@@ -4716,6 +4721,10 @@ static int qlt_handle_login(struct scsi_qla_host *vha, ...@@ -4716,6 +4721,10 @@ static int qlt_handle_login(struct scsi_qla_host *vha,
pla = qlt_plogi_ack_find_add(vha, &port_id, iocb); pla = qlt_plogi_ack_find_add(vha, &port_id, iocb);
if (!pla) { if (!pla) {
ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff,
"%s %d %8phC Term INOT due to mem alloc fail",
__func__, __LINE__,
iocb->u.isp24.port_name);
qlt_send_term_imm_notif(vha, iocb, 1); qlt_send_term_imm_notif(vha, iocb, 1);
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment