]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - drivers/scsi/qla2xxx/qla_gs.c
scsi: qla2xxx: Prevent relogin trigger from sending too many commands
[mirror_ubuntu-hirsute-kernel.git] / drivers / scsi / qla2xxx / qla_gs.c
index bc3db6abc9a0ba3985befc378ae8dd2cb452652a..546011b1a7659b5a26882c9b7f2a0d8a01421c9f 100644 (file)
@@ -14,6 +14,10 @@ static int qla2x00_sns_gpn_id(scsi_qla_host_t *, sw_info_t *);
 static int qla2x00_sns_gnn_id(scsi_qla_host_t *, sw_info_t *);
 static int qla2x00_sns_rft_id(scsi_qla_host_t *);
 static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
+static int qla_async_rftid(scsi_qla_host_t *, port_id_t *);
+static int qla_async_rffid(scsi_qla_host_t *, port_id_t *, u8, u8);
+static int qla_async_rnnid(scsi_qla_host_t *, port_id_t *, u8*);
+static int qla_async_rsnn_nn(scsi_qla_host_t *);
 
 /**
  * qla2x00_prep_ms_iocb() - Prepare common MS/CT IOCB fields for SNS CT query.
@@ -175,6 +179,9 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
                                set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
                        }
                        break;
+               case CS_TIMEOUT:
+                       rval = QLA_FUNCTION_TIMEOUT;
+                       /* fall through */
                default:
                        ql_dbg(ql_dbg_disc, vha, 0x2033,
                            "%s failed, completion status (%x) on port_id: "
@@ -508,6 +515,72 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
        return (rval);
 }
 
+static void qla2x00_async_sns_sp_done(void *s, int rc)
+{
+       struct srb *sp = s;
+       struct scsi_qla_host *vha = sp->vha;
+       struct ct_sns_pkt *ct_sns;
+       struct qla_work_evt *e;
+
+       sp->rc = rc;
+       if (rc == QLA_SUCCESS) {
+               ql_dbg(ql_dbg_disc, vha, 0x204f,
+                   "Async done-%s exiting normally.\n",
+                   sp->name);
+       } else if (rc == QLA_FUNCTION_TIMEOUT) {
+               ql_dbg(ql_dbg_disc, vha, 0x204f,
+                   "Async done-%s timeout\n", sp->name);
+       } else {
+               ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
+               memset(ct_sns, 0, sizeof(*ct_sns));
+               sp->retry_count++;
+               if (sp->retry_count > 3)
+                       goto err;
+
+               ql_dbg(ql_dbg_disc, vha, 0x204f,
+                   "Async done-%s fail rc %x.  Retry count %d\n",
+                   sp->name, rc, sp->retry_count);
+
+               e = qla2x00_alloc_work(vha, QLA_EVT_SP_RETRY);
+               if (!e)
+                       goto err2;
+
+               del_timer(&sp->u.iocb_cmd.timer);
+               e->u.iosb.sp = sp;
+               qla2x00_post_work(vha, e);
+               return;
+       }
+
+err:
+       e = qla2x00_alloc_work(vha, QLA_EVT_UNMAP);
+err2:
+       if (!e) {
+               /* please ignore kernel warning. otherwise, we have mem leak. */
+               if (sp->u.iocb_cmd.u.ctarg.req) {
+                       dma_free_coherent(&vha->hw->pdev->dev,
+                           sizeof(struct ct_sns_pkt),
+                           sp->u.iocb_cmd.u.ctarg.req,
+                           sp->u.iocb_cmd.u.ctarg.req_dma);
+                       sp->u.iocb_cmd.u.ctarg.req = NULL;
+               }
+
+               if (sp->u.iocb_cmd.u.ctarg.rsp) {
+                       dma_free_coherent(&vha->hw->pdev->dev,
+                           sizeof(struct ct_sns_pkt),
+                           sp->u.iocb_cmd.u.ctarg.rsp,
+                           sp->u.iocb_cmd.u.ctarg.rsp_dma);
+                       sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+               }
+
+               sp->free(sp);
+
+               return;
+       }
+
+       e->u.iosb.sp = sp;
+       qla2x00_post_work(vha, e);
+}
+
 /**
  * qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
  * @ha: HA context
@@ -517,57 +590,87 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
 int
 qla2x00_rft_id(scsi_qla_host_t *vha)
 {
-       int             rval;
        struct qla_hw_data *ha = vha->hw;
-       ms_iocb_entry_t *ms_pkt;
-       struct ct_sns_req       *ct_req;
-       struct ct_sns_rsp       *ct_rsp;
-       struct ct_arg arg;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha))
                return qla2x00_sns_rft_id(vha);
 
-       arg.iocb = ha->ms_iocb;
-       arg.req_dma = ha->ct_sns_dma;
-       arg.rsp_dma = ha->ct_sns_dma;
-       arg.req_size = RFT_ID_REQ_SIZE;
-       arg.rsp_size = RFT_ID_RSP_SIZE;
-       arg.nport_handle = NPH_SNS;
+       return qla_async_rftid(vha, &vha->d_id);
+}
 
-       /* Issue RFT_ID */
-       /* Prepare common MS IOCB */
-       ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
+static int qla_async_rftid(scsi_qla_host_t *vha, port_id_t *d_id)
+{
+       int rval = QLA_MEMORY_ALLOC_FAILED;
+       struct ct_sns_req *ct_req;
+       srb_t *sp;
+       struct ct_sns_pkt *ct_sns;
+
+       if (!vha->flags.online)
+               goto done;
+
+       sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+       if (!sp)
+               goto done;
+
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "rft_id";
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.req) {
+               ql_log(ql_log_warn, vha, 0xd041,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+
+       sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xd042,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
+       memset(ct_sns, 0, sizeof(*ct_sns));
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
 
        /* Prepare CT request */
-       ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD,
-           RFT_ID_RSP_SIZE);
-       ct_rsp = &ha->ct_sns->p.rsp;
+       ct_req = qla2x00_prep_ct_req(ct_sns, RFT_ID_CMD, RFT_ID_RSP_SIZE);
 
        /* Prepare CT arguments -- port_id, FC-4 types */
        ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain;
        ct_req->req.rft_id.port_id[1] = vha->d_id.b.area;
        ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa;
-
        ct_req->req.rft_id.fc4_types[2] = 0x01;         /* FCP-3 */
 
        if (vha->flags.nvme_enabled)
                ct_req->req.rft_id.fc4_types[6] = 1;    /* NVMe type 28h */
-       /* Execute MS IOCB */
-       rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
-           sizeof(ms_iocb_entry_t));
+
+       sp->u.iocb_cmd.u.ctarg.req_size = RFT_ID_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = RFT_ID_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_sns_sp_done;
+
+       rval = qla2x00_start_sp(sp);
        if (rval != QLA_SUCCESS) {
-               /*EMPTY*/
                ql_dbg(ql_dbg_disc, vha, 0x2043,
                    "RFT_ID issue IOCB failed (%d).\n", rval);
-       } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") !=
-           QLA_SUCCESS) {
-               rval = QLA_FUNCTION_FAILED;
-       } else {
-               ql_dbg(ql_dbg_disc, vha, 0x2044,
-                   "RFT_ID exiting normally.\n");
+               goto done_free_sp;
        }
-
-       return (rval);
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - hdl=%x portid %06x.\n",
+           sp->name, sp->handle, d_id->b24);
+       return rval;
+done_free_sp:
+       sp->free(sp);
+done:
+       return rval;
 }
 
 /**
@@ -579,12 +682,7 @@ qla2x00_rft_id(scsi_qla_host_t *vha)
 int
 qla2x00_rff_id(scsi_qla_host_t *vha, u8 type)
 {
-       int             rval;
        struct qla_hw_data *ha = vha->hw;
-       ms_iocb_entry_t *ms_pkt;
-       struct ct_sns_req       *ct_req;
-       struct ct_sns_rsp       *ct_rsp;
-       struct ct_arg arg;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
                ql_dbg(ql_dbg_disc, vha, 0x2046,
@@ -592,47 +690,81 @@ qla2x00_rff_id(scsi_qla_host_t *vha, u8 type)
                return (QLA_SUCCESS);
        }
 
-       arg.iocb = ha->ms_iocb;
-       arg.req_dma = ha->ct_sns_dma;
-       arg.rsp_dma = ha->ct_sns_dma;
-       arg.req_size = RFF_ID_REQ_SIZE;
-       arg.rsp_size = RFF_ID_RSP_SIZE;
-       arg.nport_handle = NPH_SNS;
+       return qla_async_rffid(vha, &vha->d_id, qlt_rff_id(vha),
+           FC4_TYPE_FCP_SCSI);
+}
 
-       /* Issue RFF_ID */
-       /* Prepare common MS IOCB */
-       ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
+static int qla_async_rffid(scsi_qla_host_t *vha, port_id_t *d_id,
+    u8 fc4feature, u8 fc4type)
+{
+       int rval = QLA_MEMORY_ALLOC_FAILED;
+       struct ct_sns_req *ct_req;
+       srb_t *sp;
+       struct ct_sns_pkt *ct_sns;
 
-       /* Prepare CT request */
-       ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD,
-           RFF_ID_RSP_SIZE);
-       ct_rsp = &ha->ct_sns->p.rsp;
+       sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+       if (!sp)
+               goto done;
 
-       /* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
-       ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain;
-       ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
-       ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "rff_id";
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.req) {
+               ql_log(ql_log_warn, vha, 0xd041,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+
+       sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xd042,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
+       memset(ct_sns, 0, sizeof(*ct_sns));
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
 
-       qlt_rff_id(vha, ct_req);
+       /* Prepare CT request */
+       ct_req = qla2x00_prep_ct_req(ct_sns, RFF_ID_CMD, RFF_ID_RSP_SIZE);
 
-       ct_req->req.rff_id.fc4_type = type;             /* SCSI - FCP */
+       /* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
+       ct_req->req.rff_id.port_id[0] = d_id->b.domain;
+       ct_req->req.rff_id.port_id[1] = d_id->b.area;
+       ct_req->req.rff_id.port_id[2] = d_id->b.al_pa;
+       ct_req->req.rff_id.fc4_feature = fc4feature;
+       ct_req->req.rff_id.fc4_type = fc4type;          /* SCSI - FCP */
+
+       sp->u.iocb_cmd.u.ctarg.req_size = RFF_ID_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = RFF_ID_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_sns_sp_done;
 
-       /* Execute MS IOCB */
-       rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
-           sizeof(ms_iocb_entry_t));
+       rval = qla2x00_start_sp(sp);
        if (rval != QLA_SUCCESS) {
-               /*EMPTY*/
                ql_dbg(ql_dbg_disc, vha, 0x2047,
                    "RFF_ID issue IOCB failed (%d).\n", rval);
-       } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") !=
-           QLA_SUCCESS) {
-               rval = QLA_FUNCTION_FAILED;
-       } else {
-               ql_dbg(ql_dbg_disc, vha, 0x2048,
-                   "RFF_ID exiting normally.\n");
+               goto done_free_sp;
        }
 
-       return (rval);
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - hdl=%x portid %06x feature %x type %x.\n",
+           sp->name, sp->handle, d_id->b24, fc4feature, fc4type);
+       return rval;
+
+done_free_sp:
+       sp->free(sp);
+done:
+       return rval;
 }
 
 /**
@@ -644,54 +776,85 @@ qla2x00_rff_id(scsi_qla_host_t *vha, u8 type)
 int
 qla2x00_rnn_id(scsi_qla_host_t *vha)
 {
-       int             rval;
        struct qla_hw_data *ha = vha->hw;
-       ms_iocb_entry_t *ms_pkt;
-       struct ct_sns_req       *ct_req;
-       struct ct_sns_rsp       *ct_rsp;
-       struct ct_arg arg;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha))
                return qla2x00_sns_rnn_id(vha);
 
-       arg.iocb = ha->ms_iocb;
-       arg.req_dma = ha->ct_sns_dma;
-       arg.rsp_dma = ha->ct_sns_dma;
-       arg.req_size = RNN_ID_REQ_SIZE;
-       arg.rsp_size = RNN_ID_RSP_SIZE;
-       arg.nport_handle = NPH_SNS;
+       return  qla_async_rnnid(vha, &vha->d_id, vha->node_name);
+}
 
-       /* Issue RNN_ID */
-       /* Prepare common MS IOCB */
-       ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
+static int qla_async_rnnid(scsi_qla_host_t *vha, port_id_t *d_id,
+       u8 *node_name)
+{
+       int rval = QLA_MEMORY_ALLOC_FAILED;
+       struct ct_sns_req *ct_req;
+       srb_t *sp;
+       struct ct_sns_pkt *ct_sns;
+
+       sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+       if (!sp)
+               goto done;
+
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "rnid";
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.req) {
+               ql_log(ql_log_warn, vha, 0xd041,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+
+       sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xd042,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
+       memset(ct_sns, 0, sizeof(*ct_sns));
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
 
        /* Prepare CT request */
-       ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE);
-       ct_rsp = &ha->ct_sns->p.rsp;
+       ct_req = qla2x00_prep_ct_req(ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE);
 
        /* Prepare CT arguments -- port_id, node_name */
        ct_req->req.rnn_id.port_id[0] = vha->d_id.b.domain;
        ct_req->req.rnn_id.port_id[1] = vha->d_id.b.area;
        ct_req->req.rnn_id.port_id[2] = vha->d_id.b.al_pa;
-
        memcpy(ct_req->req.rnn_id.node_name, vha->node_name, WWN_SIZE);
 
-       /* Execute MS IOCB */
-       rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
-           sizeof(ms_iocb_entry_t));
+       sp->u.iocb_cmd.u.ctarg.req_size = RNN_ID_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = RNN_ID_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_sns_sp_done;
+
+       rval = qla2x00_start_sp(sp);
        if (rval != QLA_SUCCESS) {
-               /*EMPTY*/
                ql_dbg(ql_dbg_disc, vha, 0x204d,
                    "RNN_ID issue IOCB failed (%d).\n", rval);
-       } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") !=
-           QLA_SUCCESS) {
-               rval = QLA_FUNCTION_FAILED;
-       } else {
-               ql_dbg(ql_dbg_disc, vha, 0x204e,
-                   "RNN_ID exiting normally.\n");
+               goto done_free_sp;
        }
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - hdl=%x portid %06x\n",
+           sp->name, sp->handle, d_id->b24);
 
-       return (rval);
+       return rval;
+
+done_free_sp:
+       sp->free(sp);
+done:
+       return rval;
 }
 
 void
@@ -718,12 +881,7 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn, size_t size)
 int
 qla2x00_rsnn_nn(scsi_qla_host_t *vha)
 {
-       int             rval;
        struct qla_hw_data *ha = vha->hw;
-       ms_iocb_entry_t *ms_pkt;
-       struct ct_sns_req       *ct_req;
-       struct ct_sns_rsp       *ct_rsp;
-       struct ct_arg arg;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
                ql_dbg(ql_dbg_disc, vha, 0x2050,
@@ -731,22 +889,49 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha)
                return (QLA_SUCCESS);
        }
 
-       arg.iocb = ha->ms_iocb;
-       arg.req_dma = ha->ct_sns_dma;
-       arg.rsp_dma = ha->ct_sns_dma;
-       arg.req_size = 0;
-       arg.rsp_size = RSNN_NN_RSP_SIZE;
-       arg.nport_handle = NPH_SNS;
+       return qla_async_rsnn_nn(vha);
+}
 
-       /* Issue RSNN_NN */
-       /* Prepare common MS IOCB */
-       /*   Request size adjusted after CT preparation */
-       ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
+static int qla_async_rsnn_nn(scsi_qla_host_t *vha)
+{
+       int rval = QLA_MEMORY_ALLOC_FAILED;
+       struct ct_sns_req *ct_req;
+       srb_t *sp;
+       struct ct_sns_pkt *ct_sns;
+
+       sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+       if (!sp)
+               goto done;
+
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "rsnn_nn";
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.req) {
+               ql_log(ql_log_warn, vha, 0xd041,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+
+       sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xd042,
+                   "%s: Failed to allocate ct_sns request.\n",
+                   __func__);
+               goto done_free_sp;
+       }
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
+       memset(ct_sns, 0, sizeof(*ct_sns));
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
 
        /* Prepare CT request */
-       ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD,
-           RSNN_NN_RSP_SIZE);
-       ct_rsp = &ha->ct_sns->p.rsp;
+       ct_req = qla2x00_prep_ct_req(ct_sns, RSNN_NN_CMD, RSNN_NN_RSP_SIZE);
 
        /* Prepare CT arguments -- node_name, symbolic node_name, size */
        memcpy(ct_req->req.rsnn_nn.node_name, vha->node_name, WWN_SIZE);
@@ -754,32 +939,33 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha)
        /* Prepare the Symbolic Node Name */
        qla2x00_get_sym_node_name(vha, ct_req->req.rsnn_nn.sym_node_name,
            sizeof(ct_req->req.rsnn_nn.sym_node_name));
-
-       /* Calculate SNN length */
        ct_req->req.rsnn_nn.name_len =
            (uint8_t)strlen(ct_req->req.rsnn_nn.sym_node_name);
 
-       /* Update MS IOCB request */
-       ms_pkt->req_bytecount =
-           cpu_to_le32(24 + 1 + ct_req->req.rsnn_nn.name_len);
-       ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
 
-       /* Execute MS IOCB */
-       rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
-           sizeof(ms_iocb_entry_t));
+       sp->u.iocb_cmd.u.ctarg.req_size = 24 + 1 + ct_req->req.rsnn_nn.name_len;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = RSNN_NN_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_sns_sp_done;
+
+       rval = qla2x00_start_sp(sp);
        if (rval != QLA_SUCCESS) {
-               /*EMPTY*/
-               ql_dbg(ql_dbg_disc, vha, 0x2051,
-                   "RSNN_NN issue IOCB failed (%d).\n", rval);
-       } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") !=
-           QLA_SUCCESS) {
-               rval = QLA_FUNCTION_FAILED;
-       } else {
-               ql_dbg(ql_dbg_disc, vha, 0x2052,
-                   "RSNN_NN exiting normally.\n");
+               ql_dbg(ql_dbg_disc, vha, 0x2043,
+                   "RFT_ID issue IOCB failed (%d).\n", rval);
+               goto done_free_sp;
        }
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - hdl=%x.\n",
+           sp->name, sp->handle);
 
-       return (rval);
+       return rval;
+
+done_free_sp:
+       sp->free(sp);
+done:
+       return rval;
 }
 
 /**
@@ -2790,15 +2976,20 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea)
        fc_port_t *fcport = ea->fcport;
 
        ql_dbg(ql_dbg_disc, vha, 0x201d,
-           "%s %8phC login state %d\n",
-           __func__, fcport->port_name, fcport->fw_login_state);
+           "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d lid %d\n",
+           __func__, fcport->port_name, fcport->disc_state,
+           fcport->fw_login_state, ea->rc, fcport->login_gen, ea->sp->gen2,
+           fcport->rscn_gen, ea->sp->gen1, fcport->loop_id);
+
+       if (fcport->disc_state == DSC_DELETE_PEND)
+               return;
 
        if (ea->sp->gen2 != fcport->login_gen) {
                /* PLOGI/PRLI/LOGO came in while cmd was out.*/
                ql_dbg(ql_dbg_disc, vha, 0x201e,
-                   "%s %8phC generation changed rscn %d|%d login %d|%d \n",
+                   "%s %8phC generation changed rscn %d|%d n",
                    __func__, fcport->port_name, fcport->last_rscn_gen,
-                   fcport->rscn_gen, fcport->last_login_gen, fcport->login_gen);
+                   fcport->rscn_gen);
                return;
        }
 
@@ -2811,7 +3002,21 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea)
                                /* cable plugged into the same place */
                                switch (vha->host->active_mode) {
                                case MODE_TARGET:
-                                       /* NOOP. let the other guy login to us.*/
+                                       if (fcport->fw_login_state ==
+                                           DSC_LS_PRLI_COMP) {
+                                               u16 data[2];
+                                               /*
+                                                * Late RSCN was delivered.
+                                                * Remote port already login'ed.
+                                                */
+                                               ql_dbg(ql_dbg_disc, vha, 0x201f,
+                                                   "%s %d %8phC post adisc\n",
+                                                   __func__, __LINE__,
+                                                   fcport->port_name);
+                                               data[0] = data[1] = 0;
+                                               qla2x00_post_async_adisc_work(
+                                                   vha, fcport, data);
+                                       }
                                        break;
                                case MODE_INITIATOR:
                                case MODE_DUAL:
@@ -2820,20 +3025,25 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea)
                                            "%s %d %8phC post %s\n", __func__,
                                            __LINE__, fcport->port_name,
                                            (atomic_read(&fcport->state) ==
-                                           FCS_ONLINE) ? "gpdb" : "gnl");
+                                           FCS_ONLINE) ? "adisc" : "gnl");
 
                                        if (atomic_read(&fcport->state) ==
-                                           FCS_ONLINE)
-                                               qla24xx_post_gpdb_work(vha,
-                                                   fcport, PDO_FORCE_ADISC);
-                                       else
+                                           FCS_ONLINE) {
+                                               u16 data[2];
+
+                                               data[0] = data[1] = 0;
+                                               qla2x00_post_async_adisc_work(
+                                                   vha, fcport, data);
+                                       } else {
                                                qla24xx_post_gnl_work(vha,
                                                    fcport);
+                                       }
                                        break;
                                }
                        } else { /* fcport->d_id.b24 != ea->id.b24 */
                                fcport->d_id.b24 = ea->id.b24;
-                               if (fcport->deleted == QLA_SESS_DELETED) {
+                               fcport->id_changed = 1;
+                               if (fcport->deleted != QLA_SESS_DELETED) {
                                        ql_dbg(ql_dbg_disc, vha, 0x2021,
                                            "%s %d %8phC post del sess\n",
                                            __func__, __LINE__, fcport->port_name);
@@ -2878,7 +3088,7 @@ static void qla2x00_async_gidpn_sp_done(void *s, int res)
        u8 *id = fcport->ct_desc.ct_sns->p.rsp.rsp.gid_pn.port_id;
        struct event_arg ea;
 
-       fcport->flags &= ~FCF_ASYNC_SENT;
+       fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 
        memset(&ea, 0, sizeof(ea));
        ea.fcport = fcport;
@@ -2889,9 +3099,22 @@ static void qla2x00_async_gidpn_sp_done(void *s, int res)
        ea.rc = res;
        ea.event = FCME_GIDPN_DONE;
 
-       ql_dbg(ql_dbg_disc, vha, 0x204f,
-           "Async done-%s res %x, WWPN %8phC ID %3phC \n",
-           sp->name, res, fcport->port_name, id);
+       if (res == QLA_FUNCTION_TIMEOUT) {
+               ql_dbg(ql_dbg_disc, sp->vha, 0xffff,
+                   "Async done-%s WWPN %8phC timed out.\n",
+                   sp->name, fcport->port_name);
+               qla24xx_post_gidpn_work(sp->vha, fcport);
+               sp->free(sp);
+               return;
+       } else if (res) {
+               ql_dbg(ql_dbg_disc, sp->vha, 0xffff,
+                   "Async done-%s fail res %x, WWPN %8phC\n",
+                   sp->name, res, fcport->port_name);
+       } else {
+               ql_dbg(ql_dbg_disc, vha, 0x204f,
+                   "Async done-%s good WWPN %8phC ID %3phC\n",
+                   sp->name, fcport->port_name, id);
+       }
 
        qla2x00_fcport_event_handler(vha, &ea);
 
@@ -2904,16 +3127,16 @@ int qla24xx_async_gidpn(scsi_qla_host_t *vha, fc_port_t *fcport)
        struct ct_sns_req       *ct_req;
        srb_t *sp;
 
-       if (!vha->flags.online)
-               goto done;
+       if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+               return rval;
 
-       fcport->flags |= FCF_ASYNC_SENT;
        fcport->disc_state = DSC_GID_PN;
        fcport->scan_state = QLA_FCPORT_SCAN;
        sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
        if (!sp)
                goto done;
 
+       fcport->flags |= FCF_ASYNC_SENT;
        sp->type = SRB_CT_PTHRU_CMD;
        sp->name = "gidpn";
        sp->gen1 = fcport->rscn_gen;
@@ -2954,8 +3177,8 @@ int qla24xx_async_gidpn(scsi_qla_host_t *vha, fc_port_t *fcport)
 
 done_free_sp:
        sp->free(sp);
-done:
        fcport->flags &= ~FCF_ASYNC_SENT;
+done:
        return rval;
 }
 
@@ -2974,6 +3197,7 @@ int qla24xx_post_gidpn_work(struct scsi_qla_host *vha, fc_port_t *fcport)
                return QLA_FUNCTION_FAILED;
 
        e->u.fcport.fcport = fcport;
+       fcport->flags |= FCF_ASYNC_ACTIVE;
        return qla2x00_post_work(vha, e);
 }
 
@@ -2986,9 +3210,39 @@ int qla24xx_post_gpsc_work(struct scsi_qla_host *vha, fc_port_t *fcport)
                return QLA_FUNCTION_FAILED;
 
        e->u.fcport.fcport = fcport;
+       fcport->flags |= FCF_ASYNC_ACTIVE;
        return qla2x00_post_work(vha, e);
 }
 
+void qla24xx_handle_gpsc_event(scsi_qla_host_t *vha, struct event_arg *ea)
+{
+       struct fc_port *fcport = ea->fcport;
+
+       ql_dbg(ql_dbg_disc, vha, 0x20d8,
+           "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d lid %d\n",
+           __func__, fcport->port_name, fcport->disc_state,
+           fcport->fw_login_state, ea->rc, ea->sp->gen2, fcport->login_gen,
+           ea->sp->gen2, fcport->rscn_gen|ea->sp->gen1, fcport->loop_id);
+
+       if (fcport->disc_state == DSC_DELETE_PEND)
+               return;
+
+       if (ea->sp->gen2 != fcport->login_gen) {
+               /* target side must have changed it. */
+               ql_dbg(ql_dbg_disc, vha, 0x20d3,
+                   "%s %8phC generation changed\n",
+                   __func__, fcport->port_name);
+               return;
+       } else if (ea->sp->gen1 != fcport->rscn_gen) {
+               ql_dbg(ql_dbg_disc, vha, 0x20d4, "%s %d %8phC post gidpn\n",
+                   __func__, __LINE__, fcport->port_name);
+               qla24xx_post_gidpn_work(vha, fcport);
+               return;
+       }
+
+       qla24xx_post_upd_fcport_work(vha, ea->fcport);
+}
+
 static void qla24xx_async_gpsc_sp_done(void *s, int res)
 {
        struct srb *sp = s;
@@ -3004,7 +3258,7 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res)
            "Async done-%s res %x, WWPN %8phC \n",
            sp->name, res, fcport->port_name);
 
-       fcport->flags &= ~FCF_ASYNC_SENT;
+       fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 
        if (res == (DID_ERROR << 16)) {
                /* entry status error */
@@ -3055,6 +3309,7 @@ done:
        ea.event = FCME_GPSC_DONE;
        ea.rc = res;
        ea.fcport = fcport;
+       ea.sp = sp;
        qla2x00_fcport_event_handler(vha, &ea);
 
        sp->free(sp);
@@ -3066,14 +3321,14 @@ int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport)
        struct ct_sns_req       *ct_req;
        srb_t *sp;
 
-       if (!vha->flags.online)
-               goto done;
+       if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+               return rval;
 
-       fcport->flags |= FCF_ASYNC_SENT;
        sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
        if (!sp)
                goto done;
 
+       fcport->flags |= FCF_ASYNC_SENT;
        sp->type = SRB_CT_PTHRU_CMD;
        sp->name = "gpsc";
        sp->gen1 = fcport->rscn_gen;
@@ -3113,8 +3368,8 @@ int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport)
 
 done_free_sp:
        sp->free(sp);
-done:
        fcport->flags &= ~FCF_ASYNC_SENT;
+done:
        return rval;
 }
 
@@ -3133,7 +3388,7 @@ int qla24xx_post_gpnid_work(struct scsi_qla_host *vha, port_id_t *id)
        return qla2x00_post_work(vha, e);
 }
 
-void qla24xx_async_gpnid_done(scsi_qla_host_t *vha, srb_t *sp)
+void qla24xx_sp_unmap(scsi_qla_host_t *vha, srb_t *sp)
 {
        if (sp->u.iocb_cmd.u.ctarg.req) {
                dma_free_coherent(&vha->hw->pdev->dev,
@@ -3155,43 +3410,138 @@ 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;
+       u16 data[2];
 
-       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;
+                                       }
+                               }
+                       }
+
+                       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);
+                               data[0] = data[1] = 0;
+                               qla2x00_post_async_adisc_work(vha, fcport,
+                                   data);
+                               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;
+                                       }
+                               }
+                       }
 
-               qla24xx_post_newsess_work(vha, &ea->id, ea->port_name, NULL);
+                       /* 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, NULL, FC4_TYPE_UNKNOWN);
+               }
        }
 }
 
@@ -3205,11 +3555,18 @@ static void qla2x00_async_gpnid_sp_done(void *s, int res)
            (struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp;
        struct event_arg ea;
        struct qla_work_evt *e;
+       unsigned long flags;
 
-       ql_dbg(ql_dbg_disc, vha, 0x2066,
-           "Async done-%s res %x ID %3phC. %8phC\n",
-           sp->name, res, ct_req->req.port_id.port_id,
-           ct_rsp->rsp.gpn_id.port_name);
+       if (res)
+               ql_dbg(ql_dbg_disc, vha, 0x2066,
+                   "Async done-%s fail res %x rscn gen %d ID %3phC. %8phC\n",
+                   sp->name, res, sp->gen1, ct_req->req.port_id.port_id,
+                   ct_rsp->rsp.gpn_id.port_name);
+       else
+               ql_dbg(ql_dbg_disc, vha, 0x2066,
+                   "Async done-%s good rscn gen %d ID %3phC. %8phC\n",
+                   sp->name, sp->gen1, ct_req->req.port_id.port_id,
+                   ct_rsp->rsp.gpn_id.port_name);
 
        memset(&ea, 0, sizeof(ea));
        memcpy(ea.port_name, ct_rsp->rsp.gpn_id.port_name, WWN_SIZE);
@@ -3220,9 +3577,26 @@ static void qla2x00_async_gpnid_sp_done(void *s, int res)
        ea.rc = res;
        ea.event = FCME_GPNID_DONE;
 
+       spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+       list_del(&sp->elem);
+       spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+
+       if (res) {
+               if (res == QLA_FUNCTION_TIMEOUT) {
+                       qla24xx_post_gpnid_work(sp->vha, &ea.id);
+                       sp->free(sp);
+                       return;
+               }
+       } else if (sp->gen1) {
+               /* There was another RSCN for this Nport ID */
+               qla24xx_post_gpnid_work(sp->vha, &ea.id);
+               sp->free(sp);
+               return;
+       }
+
        qla2x00_fcport_event_handler(vha, &ea);
 
-       e = qla2x00_alloc_work(vha, QLA_EVT_GPNID_DONE);
+       e = qla2x00_alloc_work(vha, QLA_EVT_UNMAP);
        if (!e) {
                /* please ignore kernel warning. otherwise, we have mem leak. */
                if (sp->u.iocb_cmd.u.ctarg.req) {
@@ -3253,8 +3627,9 @@ int qla24xx_async_gpnid(scsi_qla_host_t *vha, port_id_t *id)
 {
        int rval = QLA_FUNCTION_FAILED;
        struct ct_sns_req       *ct_req;
-       srb_t *sp;
+       srb_t *sp, *tsp;
        struct ct_sns_pkt *ct_sns;
+       unsigned long flags;
 
        if (!vha->flags.online)
                goto done;
@@ -3265,8 +3640,22 @@ int qla24xx_async_gpnid(scsi_qla_host_t *vha, port_id_t *id)
 
        sp->type = SRB_CT_PTHRU_CMD;
        sp->name = "gpnid";
+       sp->u.iocb_cmd.u.ctarg.id = *id;
+       sp->gen1 = 0;
        qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
 
+       spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+       list_for_each_entry(tsp, &vha->gpnid_list, elem) {
+               if (tsp->u.iocb_cmd.u.ctarg.id.b24 == id->b24) {
+                       tsp->gen1++;
+                       spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+                       sp->free(sp);
+                       goto done;
+               }
+       }
+       list_add_tail(&sp->elem, &vha->gpnid_list);
+       spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+
        sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
                sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
                GFP_KERNEL);
@@ -3393,7 +3782,7 @@ int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport)
        struct ct_sns_req       *ct_req;
        srb_t *sp;
 
-       if (!vha->flags.online)
+       if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
                return rval;
 
        sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
@@ -3441,3 +3830,721 @@ done_free_sp:
        fcport->flags &= ~FCF_ASYNC_SENT;
        return rval;
 }
+
+/* GPN_FT + GNN_FT*/
+static int qla2x00_is_a_vp(scsi_qla_host_t *vha, u64 wwn)
+{
+       struct qla_hw_data *ha = vha->hw;
+       scsi_qla_host_t *vp;
+       unsigned long flags;
+       u64 twwn;
+       int rc = 0;
+
+       if (!ha->num_vhosts)
+               return 0;
+
+       spin_lock_irqsave(&ha->vport_slock, flags);
+       list_for_each_entry(vp, &ha->vp_list, list) {
+               twwn = wwn_to_u64(vp->port_name);
+               if (wwn == twwn) {
+                       rc = 1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+       return rc;
+}
+
+void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
+{
+       fc_port_t *fcport;
+       u32 i, rc;
+       bool found;
+       u8 fc4type = sp->gen2;
+       struct fab_scan_rp *rp;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "%s enter\n", __func__);
+
+       if (sp->gen1 != vha->hw->base_qpair->chip_reset) {
+               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                   "%s scan stop due to chip reset %x/%x\n",
+                   sp->name, sp->gen1, vha->hw->base_qpair->chip_reset);
+               goto out;
+       }
+
+       rc = sp->rc;
+       if (rc) {
+               vha->scan.scan_retry++;
+               if (vha->scan.scan_retry < MAX_SCAN_RETRIES) {
+                       set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+                       set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+               } else {
+                       ql_dbg(ql_dbg_disc, vha, 0xffff,
+                           "Fabric scan failed on all retries.\n");
+               }
+               goto out;
+       }
+       vha->scan.scan_retry = 0;
+
+       list_for_each_entry(fcport, &vha->vp_fcports, list)
+               fcport->scan_state = QLA_FCPORT_SCAN;
+
+       for (i = 0; i < vha->hw->max_fibre_devices; i++) {
+               u64 wwn;
+
+               rp = &vha->scan.l[i];
+               found = false;
+
+               wwn = wwn_to_u64(rp->port_name);
+               if (wwn == 0)
+                       continue;
+
+               if (!memcmp(rp->port_name, vha->port_name, WWN_SIZE))
+                       continue;
+
+               /* Bypass reserved domain fields. */
+               if ((rp->id.b.domain & 0xf0) == 0xf0)
+                       continue;
+
+               /* Bypass virtual ports of the same host. */
+               if (qla2x00_is_a_vp(vha, wwn))
+                       continue;
+
+               list_for_each_entry(fcport, &vha->vp_fcports, list) {
+                       if (memcmp(rp->port_name, fcport->port_name, WWN_SIZE))
+                               continue;
+                       fcport->scan_state = QLA_FCPORT_FOUND;
+                       fcport->d_id.b24 = rp->id.b24;
+                       found = true;
+                       /*
+                        * If device was not a fabric device before.
+                        */
+                       if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) {
+                               qla2x00_clear_loop_id(fcport);
+                               fcport->flags |= FCF_FABRIC_DEVICE;
+                       }
+                       break;
+               }
+
+               if (!found) {
+                       ql_dbg(ql_dbg_disc, vha, 0xffff,
+                           "%s %d %8phC post new sess\n",
+                           __func__, __LINE__, rp->port_name);
+                       qla24xx_post_newsess_work(vha, &rp->id, rp->port_name,
+                           rp->node_name, NULL, fc4type);
+               }
+       }
+
+       /*
+        * Logout all previous fabric dev marked lost, except FCP2 devices.
+        */
+       list_for_each_entry(fcport, &vha->vp_fcports, list) {
+               if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
+                       continue;
+
+               if (fcport->scan_state != QLA_FCPORT_FOUND) {
+                       if ((qla_dual_mode_enabled(vha) ||
+                               qla_ini_mode_enabled(vha)) &&
+                           atomic_read(&fcport->state) == FCS_ONLINE) {
+                               qla2x00_mark_device_lost(vha, fcport,
+                                   ql2xplogiabsentdevice, 0);
+
+                               if (fcport->loop_id != FC_NO_LOOP_ID &&
+                                   (fcport->flags & FCF_FCP2_DEVICE) == 0) {
+                                       ql_dbg(ql_dbg_disc, vha, 0x20f0,
+                                           "%s %d %8phC post del sess\n",
+                                           __func__, __LINE__,
+                                           fcport->port_name);
+
+                                       qlt_schedule_sess_for_deletion_lock
+                                               (fcport);
+                                       continue;
+                               }
+                       }
+               } else
+                       qla24xx_fcport_handle_login(vha, fcport);
+       }
+
+out:
+       qla24xx_sp_unmap(vha, sp);
+       spin_lock_irqsave(&vha->work_lock, flags);
+       vha->scan.scan_flags &= ~SF_SCANNING;
+       spin_unlock_irqrestore(&vha->work_lock, flags);
+}
+
+static void qla2x00_async_gpnft_gnnft_sp_done(void *s, int res)
+{
+       struct srb *sp = s;
+       struct scsi_qla_host *vha = sp->vha;
+       struct qla_work_evt *e;
+       struct ct_sns_req *ct_req =
+               (struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req;
+       struct ct_sns_gpnft_rsp *ct_rsp =
+               (struct ct_sns_gpnft_rsp *)sp->u.iocb_cmd.u.ctarg.rsp;
+       struct ct_sns_gpn_ft_data *d = &ct_rsp->entries[0];
+       struct fab_scan_rp *rp;
+       int i, j, k;
+       u16 cmd = be16_to_cpu(ct_req->command);
+
+       /* gen2 field is holding the fc4type */
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async done-%s res %x FC4Type %x\n",
+           sp->name, res, sp->gen2);
+
+       if (res) {
+               unsigned long flags;
+
+               sp->free(sp);
+               spin_lock_irqsave(&vha->work_lock, flags);
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               vha->scan.scan_retry++;
+               spin_unlock_irqrestore(&vha->work_lock, flags);
+
+               if (vha->scan.scan_retry < MAX_SCAN_RETRIES) {
+                       set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+                       set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+                       qla2xxx_wake_dpc(vha);
+               } else {
+                       ql_dbg(ql_dbg_disc, sp->vha, 0xffff,
+                           "Async done-%s rescan failed on all retries\n",
+                           sp->name);
+               }
+               return;
+       }
+
+       if (!res) {
+               port_id_t id;
+               u64 wwn;
+
+               j = 0;
+               for (i = 0; i < vha->hw->max_fibre_devices; i++) {
+                       d  = &ct_rsp->entries[i];
+
+                       id.b.rsvd_1 = 0;
+                       id.b.domain = d->port_id[0];
+                       id.b.area   = d->port_id[1];
+                       id.b.al_pa  = d->port_id[2];
+                       wwn = wwn_to_u64(d->port_name);
+
+                       if (id.b24 == 0 || wwn == 0)
+                               continue;
+
+                       if (cmd == GPN_FT_CMD) {
+                               rp = &vha->scan.l[j];
+                               rp->id = id;
+                               memcpy(rp->port_name, d->port_name, 8);
+                               j++;
+                       } else {/* GNN_FT_CMD */
+                               for (k = 0; k < vha->hw->max_fibre_devices;
+                                   k++) {
+                                       rp = &vha->scan.l[k];
+                                       if (id.b24 == rp->id.b24) {
+                                               memcpy(rp->node_name,
+                                                   d->port_name, 8);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (cmd == GPN_FT_CMD)
+               e = qla2x00_alloc_work(vha, QLA_EVT_GPNFT_DONE);
+       else
+               e = qla2x00_alloc_work(vha, QLA_EVT_GNNFT_DONE);
+       if (!e) {
+               /* please ignore kernel warning. Otherwise, we have mem leak. */
+               if (sp->u.iocb_cmd.u.ctarg.req) {
+                       dma_free_coherent(&vha->hw->pdev->dev,
+                           sizeof(struct ct_sns_pkt),
+                           sp->u.iocb_cmd.u.ctarg.req,
+                           sp->u.iocb_cmd.u.ctarg.req_dma);
+                       sp->u.iocb_cmd.u.ctarg.req = NULL;
+               }
+               if (sp->u.iocb_cmd.u.ctarg.rsp) {
+                       dma_free_coherent(&vha->hw->pdev->dev,
+                           sizeof(struct ct_sns_pkt),
+                           sp->u.iocb_cmd.u.ctarg.rsp,
+                           sp->u.iocb_cmd.u.ctarg.rsp_dma);
+                       sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+               }
+
+               ql_dbg(ql_dbg_disc, vha, 0xffff,
+                   "Async done-%s unable to alloc work element\n",
+                   sp->name);
+               sp->free(sp);
+               set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+               set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+               return;
+       }
+
+       sp->rc = res;
+       e->u.iosb.sp = sp;
+
+       qla2x00_post_work(vha, e);
+}
+
+/*
+ * Get WWNN list for fc4_type
+ *
+ * It is assumed the same SRB is re-used from GPNFT to avoid
+ * mem free & re-alloc
+ */
+static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
+    u8 fc4_type)
+{
+       int rval = QLA_FUNCTION_FAILED;
+       struct ct_sns_req *ct_req;
+       struct ct_sns_pkt *ct_sns;
+
+       if (!vha->flags.online) {
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               goto done_free_sp;
+       }
+
+       if (!sp->u.iocb_cmd.u.ctarg.req || !sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xffff,
+                   "%s: req %p rsp %p are not setup\n",
+                   __func__, sp->u.iocb_cmd.u.ctarg.req,
+                   sp->u.iocb_cmd.u.ctarg.rsp);
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               WARN_ON(1);
+               goto done_free_sp;
+       }
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "gnnft";
+       sp->gen1 = vha->hw->base_qpair->chip_reset;
+       sp->gen2 = fc4_type;
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       memset(sp->u.iocb_cmd.u.ctarg.rsp, 0, sp->u.iocb_cmd.u.ctarg.rsp_size);
+       memset(sp->u.iocb_cmd.u.ctarg.req, 0, sp->u.iocb_cmd.u.ctarg.req_size);
+
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
+       /* CT_IU preamble  */
+       ct_req = qla2x00_prep_ct_req(ct_sns, GNN_FT_CMD,
+           sp->u.iocb_cmd.u.ctarg.rsp_size);
+
+       /* GPN_FT req */
+       ct_req->req.gpn_ft.port_type = fc4_type;
+
+       sp->u.iocb_cmd.u.ctarg.req_size = GNN_FT_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_gpnft_gnnft_sp_done;
+
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS)
+               goto done_free_sp;
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s hdl=%x FC4Type %x.\n", sp->name,
+           sp->handle, ct_req->req.gpn_ft.port_type);
+       return rval;
+
+done_free_sp:
+       if (sp->u.iocb_cmd.u.ctarg.req) {
+               dma_free_coherent(&vha->hw->pdev->dev,
+                   sizeof(struct ct_sns_pkt),
+                   sp->u.iocb_cmd.u.ctarg.req,
+                   sp->u.iocb_cmd.u.ctarg.req_dma);
+               sp->u.iocb_cmd.u.ctarg.req = NULL;
+       }
+       if (sp->u.iocb_cmd.u.ctarg.rsp) {
+               dma_free_coherent(&vha->hw->pdev->dev,
+                   sizeof(struct ct_sns_pkt),
+                   sp->u.iocb_cmd.u.ctarg.rsp,
+                   sp->u.iocb_cmd.u.ctarg.rsp_dma);
+               sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+       }
+
+       sp->free(sp);
+
+       return rval;
+} /* GNNFT */
+
+void qla24xx_async_gpnft_done(scsi_qla_host_t *vha, srb_t *sp)
+{
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "%s enter\n", __func__);
+       del_timer(&sp->u.iocb_cmd.timer);
+       qla24xx_async_gnnft(vha, sp, sp->gen2);
+}
+
+/* Get WWPN list for certain fc4_type */
+int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type)
+{
+       int rval = QLA_FUNCTION_FAILED;
+       struct ct_sns_req       *ct_req;
+       srb_t *sp;
+       struct ct_sns_pkt *ct_sns;
+       u32 rspsz;
+       unsigned long flags;
+
+       if (!vha->flags.online)
+               return rval;
+
+       spin_lock_irqsave(&vha->work_lock, flags);
+       if (vha->scan.scan_flags & SF_SCANNING) {
+               spin_unlock_irqrestore(&vha->work_lock, flags);
+               ql_dbg(ql_dbg_disc, vha, 0xffff, "scan active\n");
+               return rval;
+       }
+       vha->scan.scan_flags |= SF_SCANNING;
+       spin_unlock_irqrestore(&vha->work_lock, flags);
+
+       sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+       if (!sp) {
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               return rval;
+       }
+
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "gpnft";
+       sp->gen1 = vha->hw->base_qpair->chip_reset;
+       sp->gen2 = fc4_type;
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       sp->u.iocb_cmd.u.ctarg.req = dma_zalloc_coherent(&vha->hw->pdev->dev,
+           sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
+           GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.req) {
+               ql_log(ql_log_warn, vha, 0xffff,
+                   "Failed to allocate ct_sns request.\n");
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               goto done_free_sp;
+       }
+
+       rspsz = sizeof(struct ct_sns_gpnft_rsp) +
+               ((vha->hw->max_fibre_devices - 1) *
+                   sizeof(struct ct_sns_gpn_ft_data));
+
+       sp->u.iocb_cmd.u.ctarg.rsp = dma_zalloc_coherent(&vha->hw->pdev->dev,
+           rspsz, &sp->u.iocb_cmd.u.ctarg.rsp_dma, GFP_KERNEL);
+       if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+               ql_log(ql_log_warn, vha, 0xffff,
+                   "Failed to allocate ct_sns request.\n");
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               goto done_free_sp;
+       }
+
+       memset(vha->scan.l, 0, vha->scan.size);
+
+       ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
+       /* CT_IU preamble  */
+       ct_req = qla2x00_prep_ct_req(ct_sns, GPN_FT_CMD, rspsz);
+
+       /* GPN_FT req */
+       ct_req->req.gpn_ft.port_type = fc4_type;
+
+       sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_gpnft_gnnft_sp_done;
+
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS) {
+               vha->scan.scan_flags &= ~SF_SCANNING;
+               goto done_free_sp;
+       }
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s hdl=%x FC4Type %x.\n", sp->name,
+           sp->handle, ct_req->req.gpn_ft.port_type);
+       return rval;
+
+done_free_sp:
+       if (sp->u.iocb_cmd.u.ctarg.req) {
+               dma_free_coherent(&vha->hw->pdev->dev,
+                   sizeof(struct ct_sns_pkt),
+                   sp->u.iocb_cmd.u.ctarg.req,
+                   sp->u.iocb_cmd.u.ctarg.req_dma);
+               sp->u.iocb_cmd.u.ctarg.req = NULL;
+       }
+       if (sp->u.iocb_cmd.u.ctarg.rsp) {
+               dma_free_coherent(&vha->hw->pdev->dev,
+                   sizeof(struct ct_sns_pkt),
+                   sp->u.iocb_cmd.u.ctarg.rsp,
+                   sp->u.iocb_cmd.u.ctarg.rsp_dma);
+               sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+       }
+
+       sp->free(sp);
+
+       return rval;
+}
+
+void qla_scan_work_fn(struct work_struct *work)
+{
+       struct fab_scan *s = container_of(to_delayed_work(work),
+           struct fab_scan, scan_work);
+       struct scsi_qla_host *vha = container_of(s, struct scsi_qla_host,
+           scan);
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "%s: schedule loop resync\n", __func__);
+       set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+       set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+       qla2xxx_wake_dpc(vha);
+       spin_lock_irqsave(&vha->work_lock, flags);
+       vha->scan.scan_flags &= ~SF_QUEUED;
+       spin_unlock_irqrestore(&vha->work_lock, flags);
+}
+
+/* GNN_ID */
+void qla24xx_handle_gnnid_event(scsi_qla_host_t *vha, struct event_arg *ea)
+{
+       qla24xx_post_gnl_work(vha, ea->fcport);
+}
+
+static void qla2x00_async_gnnid_sp_done(void *s, int res)
+{
+       struct srb *sp = s;
+       struct scsi_qla_host *vha = sp->vha;
+       fc_port_t *fcport = sp->fcport;
+       u8 *node_name = fcport->ct_desc.ct_sns->p.rsp.rsp.gnn_id.node_name;
+       struct event_arg ea;
+       u64 wwnn;
+
+       fcport->flags &= ~FCF_ASYNC_SENT;
+       wwnn = wwn_to_u64(node_name);
+       if (wwnn)
+               memcpy(fcport->node_name, node_name, WWN_SIZE);
+
+       memset(&ea, 0, sizeof(ea));
+       ea.fcport = fcport;
+       ea.sp = sp;
+       ea.rc = res;
+       ea.event = FCME_GNNID_DONE;
+
+       ql_dbg(ql_dbg_disc, vha, 0x204f,
+           "Async done-%s res %x, WWPN %8phC %8phC\n",
+           sp->name, res, fcport->port_name, fcport->node_name);
+
+       qla2x00_fcport_event_handler(vha, &ea);
+
+       sp->free(sp);
+}
+
+int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+       int rval = QLA_FUNCTION_FAILED;
+       struct ct_sns_req       *ct_req;
+       srb_t *sp;
+
+       if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+               return rval;
+
+       fcport->disc_state = DSC_GNN_ID;
+       sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
+       if (!sp)
+               goto done;
+
+       fcport->flags |= FCF_ASYNC_SENT;
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "gnnid";
+       sp->gen1 = fcport->rscn_gen;
+       sp->gen2 = fcport->login_gen;
+
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       /* CT_IU preamble  */
+       ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GNN_ID_CMD,
+           GNN_ID_RSP_SIZE);
+
+       /* GNN_ID req */
+       ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain;
+       ct_req->req.port_id.port_id[1] = fcport->d_id.b.area;
+       ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
+
+
+       /* req & rsp use the same buffer */
+       sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns;
+       sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma;
+       sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns;
+       sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma;
+       sp->u.iocb_cmd.u.ctarg.req_size = GNN_ID_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = GNN_ID_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_gnnid_sp_done;
+
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS)
+               goto done_free_sp;
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
+           sp->name, fcport->port_name,
+           sp->handle, fcport->loop_id, fcport->d_id.b24);
+       return rval;
+
+done_free_sp:
+       sp->free(sp);
+       fcport->flags &= ~FCF_ASYNC_SENT;
+done:
+       return rval;
+}
+
+int qla24xx_post_gnnid_work(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_work_evt *e;
+       int ls;
+
+       ls = atomic_read(&vha->loop_state);
+       if (((ls != LOOP_READY) && (ls != LOOP_UP)) ||
+               test_bit(UNLOADING, &vha->dpc_flags))
+               return 0;
+
+       e = qla2x00_alloc_work(vha, QLA_EVT_GNNID);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.fcport.fcport = fcport;
+       return qla2x00_post_work(vha, e);
+}
+
+/* GPFN_ID */
+void qla24xx_handle_gfpnid_event(scsi_qla_host_t *vha, struct event_arg *ea)
+{
+       fc_port_t *fcport = ea->fcport;
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d fcpcnt %d\n",
+           __func__, fcport->port_name, fcport->disc_state,
+           fcport->fw_login_state, ea->rc, fcport->login_gen, ea->sp->gen2,
+           fcport->rscn_gen, ea->sp->gen1, vha->fcport_count);
+
+       if (fcport->disc_state == DSC_DELETE_PEND)
+               return;
+
+       if (ea->sp->gen2 != fcport->login_gen) {
+               /* target side must have changed it. */
+               ql_dbg(ql_dbg_disc, vha, 0x20d3,
+                   "%s %8phC generation changed\n",
+                   __func__, fcport->port_name);
+               return;
+       } else if (ea->sp->gen1 != fcport->rscn_gen) {
+               ql_dbg(ql_dbg_disc, vha, 0x20d4, "%s %d %8phC post gidpn\n",
+                   __func__, __LINE__, fcport->port_name);
+               qla24xx_post_gidpn_work(vha, fcport);
+               return;
+       }
+
+       qla24xx_post_gpsc_work(vha, fcport);
+}
+
+static void qla2x00_async_gfpnid_sp_done(void *s, int res)
+{
+       struct srb *sp = s;
+       struct scsi_qla_host *vha = sp->vha;
+       fc_port_t *fcport = sp->fcport;
+       u8 *fpn = fcport->ct_desc.ct_sns->p.rsp.rsp.gfpn_id.port_name;
+       struct event_arg ea;
+       u64 wwn;
+
+       fcport->flags &= ~FCF_ASYNC_SENT;
+       wwn = wwn_to_u64(fpn);
+       if (wwn)
+               memcpy(fcport->fabric_port_name, fpn, WWN_SIZE);
+
+       memset(&ea, 0, sizeof(ea));
+       ea.fcport = fcport;
+       ea.sp = sp;
+       ea.rc = res;
+       ea.event = FCME_GFPNID_DONE;
+
+       ql_dbg(ql_dbg_disc, vha, 0x204f,
+           "Async done-%s res %x, WWPN %8phC %8phC\n",
+           sp->name, res, fcport->port_name, fcport->fabric_port_name);
+
+       qla2x00_fcport_event_handler(vha, &ea);
+
+       sp->free(sp);
+}
+
+int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+       int rval = QLA_FUNCTION_FAILED;
+       struct ct_sns_req       *ct_req;
+       srb_t *sp;
+
+       if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+               return rval;
+
+       fcport->disc_state = DSC_GFPN_ID;
+       sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
+       if (!sp)
+               goto done;
+
+       fcport->flags |= FCF_ASYNC_SENT;
+       sp->type = SRB_CT_PTHRU_CMD;
+       sp->name = "gfpnid";
+       sp->gen1 = fcport->rscn_gen;
+       sp->gen2 = fcport->login_gen;
+
+       qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+       /* CT_IU preamble  */
+       ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GFPN_ID_CMD,
+           GFPN_ID_RSP_SIZE);
+
+       /* GFPN_ID req */
+       ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain;
+       ct_req->req.port_id.port_id[1] = fcport->d_id.b.area;
+       ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
+
+
+       /* req & rsp use the same buffer */
+       sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns;
+       sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma;
+       sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns;
+       sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma;
+       sp->u.iocb_cmd.u.ctarg.req_size = GFPN_ID_REQ_SIZE;
+       sp->u.iocb_cmd.u.ctarg.rsp_size = GFPN_ID_RSP_SIZE;
+       sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
+
+       sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+       sp->done = qla2x00_async_gfpnid_sp_done;
+
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS)
+               goto done_free_sp;
+
+       ql_dbg(ql_dbg_disc, vha, 0xffff,
+           "Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
+           sp->name, fcport->port_name,
+           sp->handle, fcport->loop_id, fcport->d_id.b24);
+       return rval;
+
+done_free_sp:
+       sp->free(sp);
+       fcport->flags &= ~FCF_ASYNC_SENT;
+done:
+       return rval;
+}
+
+int qla24xx_post_gfpnid_work(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_work_evt *e;
+       int ls;
+
+       ls = atomic_read(&vha->loop_state);
+       if (((ls != LOOP_READY) && (ls != LOOP_UP)) ||
+               test_bit(UNLOADING, &vha->dpc_flags))
+               return 0;
+
+       e = qla2x00_alloc_work(vha, QLA_EVT_GFPNID);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.fcport.fcport = fcport;
+       return qla2x00_post_work(vha, e);
+}