1
0
Fork 0

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: Quinn Tran <quinn.tran@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
hifive-unleashed-5.1
Quinn Tran 2018-08-02 13:16:44 -07:00 committed by Martin K. Petersen
parent 4ae5716b41
commit 48acad0990
7 changed files with 182 additions and 109 deletions

View File

@ -377,6 +377,7 @@ struct srb_iocb {
#define SRB_LOGIN_COND_PLOGI BIT_1
#define SRB_LOGIN_SKIP_PRLI BIT_2
#define SRB_LOGIN_NVME_PRLI BIT_3
#define SRB_LOGIN_PRLI_ONLY BIT_4
uint16_t data[2];
u32 iop[2];
} logio;
@ -4236,7 +4237,7 @@ typedef struct scsi_qla_host {
#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */
#define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */
#define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */
#define FREE_BIT 21
#define N2N_LINK_RESET 21
#define PORT_UPDATE_NEEDED 22
#define FX00_RESET_RECOVERY 23
#define FX00_TARGET_SCAN 24

View File

@ -160,6 +160,22 @@ qla2x00_async_login_sp_done(void *ptr, int res)
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
qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data)
@ -189,13 +205,18 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->done = qla2x00_async_login_sp_done;
lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
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;
if (fcport->fc4f_nvme)
lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI;
if (fcport->fc4f_nvme)
lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI;
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED;
}
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
fcport->flags |= FCF_LOGIN_NEEDED;
@ -497,16 +518,19 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
wwn = wwn_to_u64(e->port_name);
if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE))
continue;
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;
if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE))
continue;
if (IS_SW_RESV_ADDR(id))
continue;
found = 1;
loop_id = le16_to_cpu(e->nport_handle);
loop_id = (loop_id & 0x7fff);
@ -518,14 +542,18 @@ 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.al_pa, loop_id, fcport->loop_id);
if ((id.b24 != fcport->d_id.b24) ||
((fcport->loop_id != FC_NO_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);
return;
switch (fcport->disc_state) {
case DSC_DELETE_PEND:
case DSC_DELETED:
break;
default:
if ((id.b24 != fcport->d_id.b24) ||
((fcport->loop_id != FC_NO_LOOP_ID) &&
(fcport->loop_id != loop_id))) {
qlt_schedule_sess_for_deletion(fcport);
return;
}
break;
}
fcport->loop_id = loop_id;
@ -544,66 +572,122 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
fcport->login_pause = 1;
}
if (fcport->fc4f_nvme)
if (fcport->fc4f_nvme)
current_login_state = e->current_login_state >> 4;
else
current_login_state = e->current_login_state & 0xf;
switch (current_login_state) {
case DSC_LS_PRLI_COMP:
ql_dbg(ql_dbg_disc, vha, 0x20e4,
"%s %d %8phC post gpdb\n",
__func__, __LINE__, fcport->port_name);
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_PORT_UNAVAIL:
switch (vha->hw->current_topology) {
default:
if (fcport->loop_id == FC_NO_LOOP_ID) {
qla2x00_find_new_loop_id(vha, fcport);
switch (current_login_state) {
case DSC_LS_PRLI_COMP:
ql_dbg(ql_dbg_disc + ql_dbg_verbose,
vha, 0x20e4, "%s %d %8phC post gpdb\n",
__func__, __LINE__, fcport->port_name);
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_PORT_UNAVAIL:
default:
if (fcport->loop_id != FC_NO_LOOP_ID)
qla2x00_clear_loop_id(fcport);
fcport->loop_id = loop_id;
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
qla24xx_fcport_handle_login(vha, fcport);
break;
}
ql_dbg(ql_dbg_disc, vha, 0x20e5,
"%s %d %8phC\n",
__func__, __LINE__, fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport);
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) {
/* fw has no record of this port */
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
id.b.domain = e->port_id[0];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[2];
id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(e->nport_handle);
switch (vha->hw->current_topology) {
case ISP_CFG_F:
case ISP_CFG_FL:
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
id.b.domain = e->port_id[0];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[2];
id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(e->nport_handle);
if (fcport->d_id.b24 == id.b24) {
conflict_fcport =
qla2x00_find_fcport_by_wwpn(vha,
e->port_name, 0);
ql_dbg(ql_dbg_disc, vha, 0x20e6,
"%s %d %8phC post del sess\n",
__func__, __LINE__,
conflict_fcport->port_name);
qlt_schedule_sess_for_deletion
(conflict_fcport);
if (fcport->d_id.b24 == id.b24) {
conflict_fcport =
qla2x00_find_fcport_by_wwpn(vha,
e->port_name, 0);
ql_dbg(ql_dbg_disc + ql_dbg_verbose,
vha, 0x20e5,
"%s %d %8phC post del sess\n",
__func__, __LINE__,
conflict_fcport->port_name);
qlt_schedule_sess_for_deletion
(conflict_fcport);
}
/*
* FW already picked this loop id for
* another fcport
*/
if (fcport->loop_id == loop_id)
fcport->loop_id = FC_NO_LOOP_ID;
}
/* FW already picked this loop id for another fcport */
if (fcport->loop_id == loop_id)
fcport->loop_id = FC_NO_LOOP_ID;
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;
}
qla24xx_fcport_handle_login(vha, fcport);
}
} /* gnl_event */
@ -4590,20 +4674,10 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
} else if (ha->current_topology == ISP_CFG_N) {
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().
*/
if (qla_tgt_mode_enabled(vha)) {
/* allow the other side to start the login */
clear_bit(LOCAL_LOOP_UPDATE, &flags);
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
} else {
if (qla_tgt_mode_enabled(vha)) {
/* allow the other side to start the login */
clear_bit(LOCAL_LOOP_UPDATE, &flags);
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
}
}
} else if (ha->current_topology == ISP_CFG_NL) {
clear_bit(RSCN_UPDATE, &flags);
@ -7929,8 +8003,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
}
/* 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)) {
icb->firmware_options_3 |= BIT_8;

View File

@ -2240,12 +2240,15 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
struct srb_iocb *lio = &sp->u.iocb_cmd;
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI);
if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI)
logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI)
logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
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);
if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI)
logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI)
logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
}
logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
logio->port_id[0] = sp->fcport->d_id.b.al_pa;
logio->port_id[1] = sp->fcport->d_id.b.area;

View File

@ -908,7 +908,8 @@ skip_rio:
if (!atomic_read(&vha->loop_down_timer))
atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(vha, 1);
if (!N2N_TOPO(ha))
qla2x00_mark_all_devices_lost(vha, 1);
}
if (vha->vp_idx) {

View File

@ -2177,7 +2177,10 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
mcp->out_mb = MBX_2|MBX_1|MBX_0;
} else if (IS_FWI2_CAPABLE(vha->hw)) {
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[3] = vha->hw->loop_reset_delay;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@ -3911,28 +3914,6 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (fcport) {
fcport->plogi_nack_done_deadline = jiffies + HZ;
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);
}
}
}

View File

@ -6186,6 +6186,11 @@ intr_on_check:
if (!IS_QLAFX00(ha))
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;
end_loop:
set_current_state(TASK_INTERRUPTIBLE);

View File

@ -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) {
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);
memcpy(&pla->iocb, iocb, sizeof(pla->iocb));
return pla;
@ -1073,6 +1077,7 @@ void qlt_free_session_done(struct work_struct *work)
struct qlt_plogi_ack_t *con =
sess->plogi_link[QLT_PLOGI_LINK_CONFLICT];
struct imm_ntfy_from_isp *iocb;
own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN];
if (con) {
iocb = &con->iocb;
@ -4716,6 +4721,10 @@ static int qlt_handle_login(struct scsi_qla_host *vha,
pla = qlt_plogi_ack_find_add(vha, &port_id, iocb);
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);
goto out;
}