Skip to content

Commit

Permalink
scsi: qla2xxx: Fix N2N link re-connect
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Quinn Tran authored and martinkpetersen committed Aug 2, 2018
1 parent 4ae5716 commit 48acad0
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 108 deletions.
3 changes: 2 additions & 1 deletion drivers/scsi/qla2xxx/qla_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
227 changes: 150 additions & 77 deletions drivers/scsi/qla2xxx/qla_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -497,15 +518,18 @@ 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);
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;
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 = (loop_id & 0x7fff);
Expand All @@ -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;
Expand All @@ -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);
switch (vha->hw->current_topology) {
default:
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;
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);

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_find_new_loop_id(vha, 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);

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);
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 + 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 */

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 9 additions & 6 deletions drivers/scsi/qla2xxx/qla_iocb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion drivers/scsi/qla2xxx/qla_isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
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) {
Expand Down
27 changes: 4 additions & 23 deletions drivers/scsi/qla2xxx/qla_mbx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
Loading

0 comments on commit 48acad0

Please sign in to comment.