]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
qla2xxx: Relogin to target port on a cable swap
authorQuinn Tran <quinn.tran@cavium.com>
Wed, 13 Dec 2017 19:01:30 +0000 (11:01 -0800)
committerSomasundaram Krishnasamy <somasundaram.krishnasamy@oracle.com>
Fri, 5 Jan 2018 21:05:16 +0000 (13:05 -0800)
Orabug: 27235104

If user swaps one target port for another target port for same
switch port, the new target port is not being recognized by the
driver. Current code assumes that old Target port has recovered
from link down. The fix will ask switch what is the WWPN of a
specific NportID (GPNID) rather than assuming it's the same Target
port which has came back.

Fixes: 726b85487067d ("qla2xxx: Add framework for async fabric discovery")
Cc: <stable@vger.kernel.org> # 4.10+
Signed-off-by: Quinn Tran <quinn.tran@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>'
[ Upstream commit 5ef696aa9f3ccf999552d924c4e21a348f2bbea9 ]
Signed-off-by: Somasundaram Krishnasamy <somasundaram.krishnasamy@oracle.com>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_target.c

index 1957f4e0dadaf26a2ac06d1121ef298bcfc0f4e6..c9233211824d8db2437729d6910cb52d9701508f 100644 (file)
@@ -3185,43 +3185,132 @@ void qla24xx_async_gpnid_done(scsi_qla_host_t *vha, srb_t *sp)
 
 void qla24xx_handle_gpnid_event(scsi_qla_host_t *vha, struct event_arg *ea)
 {
-       fc_port_t *fcport;
-       unsigned long flags;
+       fc_port_t *fcport, *conflict, *t;
 
-       spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
-       fcport = qla2x00_find_fcport_by_wwpn(vha, ea->port_name, 1);
-       spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "%s %d port_id: %06x\n",
+           __func__, __LINE__, ea->id.b24);
 
-       if (fcport) {
-               /* cable moved. just plugged in */
-               fcport->rscn_gen++;
-               fcport->d_id = ea->id;
-               fcport->scan_state = QLA_FCPORT_FOUND;
-               fcport->flags |= FCF_FABRIC_DEVICE;
-
-               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:
-                       ql_dbg(ql_dbg_disc, vha, 0x2064,
-                           "%s %d %8phC post del sess\n",
-                           __func__, __LINE__, fcport->port_name);
-                       qlt_schedule_sess_for_deletion_lock(fcport);
-                       break;
+       if (ea->rc) {
+               /* cable is disconnected */
+               list_for_each_entry_safe(fcport, t, &vha->vp_fcports, list) {
+                       if (fcport->d_id.b24 == ea->id.b24) {
+                               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                   "%s %d %8phC DS %d\n",
+                                   __func__, __LINE__,
+                                   fcport->port_name,
+                                   fcport->disc_state);
+                               fcport->scan_state = QLA_FCPORT_SCAN;
+                               switch (fcport->disc_state) {
+                               case DSC_DELETED:
+                               case DSC_DELETE_PEND:
+                                       break;
+                               default:
+                                       ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                           "%s %d %8phC post del sess\n",
+                                           __func__, __LINE__,
+                                           fcport->port_name);
+                                       qlt_schedule_sess_for_deletion_lock(fcport);
+                                       break;
+                               }
+                       }
                }
        } else {
-               /* create new fcport */
-               ql_dbg(ql_dbg_disc, vha, 0x2065,
-                   "%s %d %8phC post new sess\n",
-                   __func__, __LINE__, ea->port_name);
+               /* cable is connected */
+               fcport = qla2x00_find_fcport_by_wwpn(vha, ea->port_name, 1);
+               if (fcport) {
+                       list_for_each_entry_safe(conflict, t, &vha->vp_fcports,
+                           list) {
+                               if ((conflict->d_id.b24 == ea->id.b24) &&
+                                   (fcport != conflict)) {
+                                       /* 2 fcports with conflict Nport ID or
+                                        * an existing fcport is having nport ID
+                                        * conflict with new fcport.
+                                        */
+
+                                       ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                           "%s %d %8phC DS %d\n",
+                                           __func__, __LINE__,
+                                           conflict->port_name,
+                                           conflict->disc_state);
+                                       conflict->scan_state = QLA_FCPORT_SCAN;
+                                       switch (conflict->disc_state) {
+                                       case DSC_DELETED:
+                                       case DSC_DELETE_PEND:
+                                               break;
+                                       default:
+                                               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                                   "%s %d %8phC post del sess\n",
+                                                   __func__, __LINE__,
+                                                   conflict->port_name);
+                                               qlt_schedule_sess_for_deletion_lock(conflict);
+                                               break;
+                                       }
+                               }
+                       }
 
-               qla24xx_post_newsess_work(vha, &ea->id, ea->port_name, NULL);
+                       fcport->rscn_gen++;
+                       fcport->scan_state = QLA_FCPORT_FOUND;
+                       fcport->flags |= FCF_FABRIC_DEVICE;
+                       switch (fcport->disc_state) {
+                       case DSC_LOGIN_COMPLETE:
+                               /* recheck session is still intact. */
+                               ql_dbg(ql_dbg_disc, vha, 0x210d,
+                                   "%s %d %8phC revalidate session with ADISC\n", __func__, __LINE__,
+                                   fcport->port_name);
+                               qla24xx_post_gpdb_work(vha, fcport, PDO_FORCE_ADISC);
+                               break;
+                       case DSC_DELETED:
+                               ql_dbg(ql_dbg_disc, vha, 0x210d,
+                                   "%s %d %8phC login\n", __func__, __LINE__,
+                                   fcport->port_name);
+                               fcport->d_id = ea->id;
+                               qla24xx_fcport_handle_login(vha, fcport);
+                               break;
+                       case DSC_DELETE_PEND:
+                               fcport->d_id = ea->id;
+                               break;
+                       default:
+                               fcport->d_id = ea->id;
+                               break;
+                       }
+               } else {
+                       list_for_each_entry_safe(conflict, t, &vha->vp_fcports,
+                           list) {
+                               if (conflict->d_id.b24 == ea->id.b24) {
+                                       /* 2 fcports with conflict Nport ID or
+                                        * an existing fcport is having nport ID
+                                        * conflict with new fcport.
+                                        */
+                                       ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                           "%s %d %8phC DS %d\n",
+                                           __func__, __LINE__,
+                                           conflict->port_name,
+                                           conflict->disc_state);
+
+                                       conflict->scan_state = QLA_FCPORT_SCAN;
+                                       switch (conflict->disc_state) {
+                                       case DSC_DELETED:
+                                       case DSC_DELETE_PEND:
+                                               break;
+                                       default:
+                                               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                                   "%s %d %8phC post del sess\n",
+                                                   __func__, __LINE__,
+                                                   conflict->port_name);
+                                               qlt_schedule_sess_for_deletion_lock(conflict);
+                                               break;
+                                       }
+                               }
+                       }
+
+                       /* create new fcport */
+                       ql_dbg(ql_dbg_disc, vha, 0x2065,
+                           "%s %d %8phC post new sess\n",
+                           __func__, __LINE__, ea->port_name);
+                       qla24xx_post_newsess_work(vha, &ea->id,
+                           ea->port_name, NULL);
+               }
        }
 }
 
@@ -3263,9 +3352,10 @@ static void qla2x00_async_gpnid_sp_done(void *s, int res)
        if (res) {
                if (res == QLA_FUNCTION_TIMEOUT) {
                        qla24xx_post_gpnid_work(sp->vha, &ea.id);
+                       sp->free(sp);
+                       return;
                }
-               sp->free(sp);
-               return;
+               /* let event handler make decision on error. */
        } else if (sp->gen1) {
                /* There was anoter RSNC for this Nport ID */
                qla24xx_post_gpnid_work(sp->vha, &ea.id);
index 8aa2ba41da36afe800a55a08727ba701f91d9fbc..ad53fa098f3069fb46170accb493d736805714a9 100644 (file)
@@ -816,6 +816,9 @@ void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea)
                 * have triggered the session to be re-validate.
                 * Session is still valid.
                 */
+               ql_dbg(ql_dbg_disc, vha, 0x20d6,
+                   "%s %d %8phC session revalidate success\n",
+                   __func__, __LINE__, fcport->port_name);
                fcport->disc_state = DSC_LOGIN_COMPLETE;
        }
        spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
@@ -940,9 +943,8 @@ void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea)
        switch (fcport->disc_state) {
        case DSC_DELETED:
        case DSC_LOGIN_COMPLETE:
-               qla24xx_post_gidpn_work(fcport->vha, fcport);
+               qla24xx_post_gpnid_work(fcport->vha, &ea->id);
                break;
-
        default:
                break;
        }
index 4ad96ecac93ee0de41018782dd67500949515fb2..2e4da7000101cdd56a07ad3e2294bc9a4b9c9223 100644 (file)
@@ -4347,8 +4347,35 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
        if (fcport) {
                if (pla)
                        qlt_plogi_ack_unref(vha, pla);
-               else
+               else {
+                       spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+                       tfcp = qla2x00_find_fcport_by_nportid(vha,
+                           &e->u.new_sess.id, 1);
+                       if (tfcp && (tfcp != fcport)) {
+                               /* We have a conflict fcport with same NportID.
+                                  */
+                               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                                   "%s %8phC found conflict b4 add. DS %d LS %d\n",
+                                   __func__, tfcp->port_name, tfcp->disc_state,
+                                   tfcp->fw_login_state);
+
+                               switch (tfcp->disc_state) {
+                               case DSC_DELETED:
+                                       break;
+                               case DSC_DELETE_PEND:
+                                       fcport->login_pause = 1;
+                                       tfcp->conflict = fcport;
+                                       break;
+                               default:
+                                       fcport->login_pause = 1;
+                                       tfcp->conflict = fcport;
+                                       qlt_schedule_sess_for_deletion_lock(tfcp);
+                                       break;
+                               }
+                       }
+                       spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
                        qla24xx_async_gnl(vha, fcport);
+               }
        }
 
        if (free_fcport) {
index 0b3ea1406c2886583c5699a8e286a7c65123cbc4..64419ba8c1284b9ea436c7986e0bdebb4c2a4f86 100644 (file)
@@ -851,6 +851,17 @@ qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla,
                iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
                pla->ref_count, pla, link);
 
+       if (link == QLT_PLOGI_LINK_CONFLICT) {
+               switch (sess->disc_state) {
+               case DSC_DELETED:
+               case DSC_DELETE_PEND:
+                       pla->ref_count--;
+                       return;
+               default:
+                       break;
+               }
+       }
+
        if (sess->plogi_link[link])
                qlt_plogi_ack_unref(vha, sess->plogi_link[link]);
 
@@ -4518,6 +4529,10 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
                sess->d_id = port_id;
                sess->login_gen++;
 
+               ql_dbg(ql_dbg_disc, vha, 0x20f9,
+                   "%s %d %8phC  DS %d\n",
+                   __func__, __LINE__, sess->port_name, sess->disc_state);
+
                switch (sess->disc_state) {
                case DSC_DELETED:
                        qlt_plogi_ack_unref(vha, pla);
@@ -4567,12 +4582,19 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
                }
 
                if (conflict_sess) {
-                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b,
-                           "PRLI with conflicting sess %p port %8phC\n",
-                           conflict_sess, conflict_sess->port_name);
-                       qlt_send_term_imm_notif(vha, iocb, 1);
-                       res = 0;
-                       break;
+                       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) {