OSDN Git Service

scsi: qla2xxx: Add error handling for PLOGI ELS passthrough
authorQuinn Tran <qutran@marvell.com>
Thu, 12 Sep 2019 18:09:13 +0000 (11:09 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 10 Oct 2019 02:26:34 +0000 (22:26 -0400)
Add error handling logic to ELS Passthrough relating to NVME devices.
Current code does not parse error code to take proper recovery action,
instead it re-logins with the same login parameters that encountered the
error. Ex: nport handle collision.

Link: https://lore.kernel.org/r/20190912180918.6436-10-hmadhani@marvell.com
Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Himanshu Madhani <hmadhani@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_iocb.c

index 518eb95..eeb5264 100644 (file)
@@ -2740,6 +2740,10 @@ static void qla2x00_els_dcmd2_sp_done(srb_t *sp, int res)
        struct scsi_qla_host *vha = sp->vha;
        struct event_arg ea;
        struct qla_work_evt *e;
+       struct fc_port *conflict_fcport;
+       port_id_t cid;  /* conflict Nport id */
+       u32 *fw_status = sp->u.iocb_cmd.u.els_plogi.fw_status;
+       u16 lid;
 
        ql_dbg(ql_dbg_disc, vha, 0x3072,
            "%s ELS done rc %d hdl=%x, portid=%06x %8phC\n",
@@ -2751,14 +2755,99 @@ static void qla2x00_els_dcmd2_sp_done(srb_t *sp, int res)
        if (sp->flags & SRB_WAKEUP_ON_COMP)
                complete(&lio->u.els_plogi.comp);
        else {
-               if (res) {
-                       set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
-               } else {
+               switch (fw_status[0]) {
+               case CS_DATA_UNDERRUN:
+               case CS_COMPLETE:
                        memset(&ea, 0, sizeof(ea));
                        ea.fcport = fcport;
                        ea.data[0] = MBS_COMMAND_COMPLETE;
                        ea.sp = sp;
                        qla24xx_handle_plogi_done_event(vha, &ea);
+                       break;
+               case CS_IOCB_ERROR:
+                       switch (fw_status[1]) {
+                       case LSC_SCODE_PORTID_USED:
+                               lid = fw_status[2] & 0xffff;
+                               qlt_find_sess_invalidate_other(vha,
+                                   wwn_to_u64(fcport->port_name),
+                                   fcport->d_id, lid, &conflict_fcport);
+                               if (conflict_fcport) {
+                                       /*
+                                        * Another fcport shares the same
+                                        * loop_id & nport id; conflict
+                                        * fcport needs to finish cleanup
+                                        * before this fcport can proceed
+                                        * to login.
+                                        */
+                                       conflict_fcport->conflict = fcport;
+                                       fcport->login_pause = 1;
+                                       ql_dbg(ql_dbg_disc, vha, 0x20ed,
+                                           "%s %d %8phC pid %06x inuse with lid %#x post gidpn\n",
+                                           __func__, __LINE__,
+                                           fcport->port_name,
+                                           fcport->d_id.b24, lid);
+                               } else {
+                                       ql_dbg(ql_dbg_disc, vha, 0x20ed,
+                                           "%s %d %8phC pid %06x inuse with lid %#x sched del\n",
+                                           __func__, __LINE__,
+                                           fcport->port_name,
+                                           fcport->d_id.b24, lid);
+                                       qla2x00_clear_loop_id(fcport);
+                                       set_bit(lid, vha->hw->loop_id_map);
+                                       fcport->loop_id = lid;
+                                       fcport->keep_nport_handle = 0;
+                                       qlt_schedule_sess_for_deletion(fcport);
+                               }
+                               break;
+
+                       case LSC_SCODE_NPORT_USED:
+                               cid.b.domain = (fw_status[2] >> 16) & 0xff;
+                               cid.b.area   = (fw_status[2] >>  8) & 0xff;
+                               cid.b.al_pa  = fw_status[2] & 0xff;
+                               cid.b.rsvd_1 = 0;
+
+                               ql_dbg(ql_dbg_disc, vha, 0x20ec,
+                                   "%s %d %8phC lid %#x in use with pid %06x post gnl\n",
+                                   __func__, __LINE__, fcport->port_name,
+                                   fcport->loop_id, cid.b24);
+                               set_bit(fcport->loop_id,
+                                   vha->hw->loop_id_map);
+                               fcport->loop_id = FC_NO_LOOP_ID;
+                               qla24xx_post_gnl_work(vha, fcport);
+                               break;
+
+                       case LSC_SCODE_NOXCB:
+                               vha->hw->exch_starvation++;
+                               if (vha->hw->exch_starvation > 5) {
+                                       ql_log(ql_log_warn, vha, 0xd046,
+                                           "Exchange starvation. Resetting RISC\n");
+                                       vha->hw->exch_starvation = 0;
+                                       set_bit(ISP_ABORT_NEEDED,
+                                           &vha->dpc_flags);
+                                       qla2xxx_wake_dpc(vha);
+                               }
+                               /* fall through */
+                       default:
+                               ql_dbg(ql_dbg_disc, vha, 0x20eb,
+                                   "%s %8phC cmd error fw_status 0x%x 0x%x 0x%x\n",
+                                   __func__, sp->fcport->port_name,
+                                   fw_status[0], fw_status[1], fw_status[2]);
+
+                               fcport->flags &= ~FCF_ASYNC_SENT;
+                               set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+                               break;
+                       }
+                       break;
+
+               default:
+                       ql_dbg(ql_dbg_disc, vha, 0x20eb,
+                           "%s %8phC cmd error 2 fw_status 0x%x 0x%x 0x%x\n",
+                           __func__, sp->fcport->port_name,
+                           fw_status[0], fw_status[1], fw_status[2]);
+
+                       sp->fcport->flags &= ~FCF_ASYNC_SENT;
+                       set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+                       break;
                }
 
                e = qla2x00_alloc_work(vha, QLA_EVT_UNMAP);