OSDN Git Service

scsi: lpfc: Fix NULL pointer access in lpfc_nvme_info_show
authorJames Smart <jsmart2021@gmail.com>
Mon, 9 Apr 2018 21:24:27 +0000 (14:24 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 18 Apr 2018 23:34:04 +0000 (19:34 -0400)
After making remoteport unregister requests, the ndlp nrport pointer was
stale.

Track when waiting for waiting for unregister completion callback and
adjust nldp pointer assignment.  Add a few safety checks for NULL
pointer values.

Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <james.smart@broadcom.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_nvme.c
drivers/scsi/lpfc/lpfc_nvme.h

index 15f921d..fd3b253 100644 (file)
@@ -149,6 +149,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
        struct lpfc_nvmet_tgtport *tgtp;
        struct nvme_fc_local_port *localport;
        struct lpfc_nvme_lport *lport;
+       struct lpfc_nvme_rport *rport;
        struct lpfc_nodelist *ndlp;
        struct nvme_fc_remote_port *nrport;
        struct lpfc_nvme_ctrl_stat *cstat;
@@ -312,11 +313,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
                        localport->port_id, statep);
 
        list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
-               if (!ndlp->nrport)
+               rport = lpfc_ndlp_get_nrport(ndlp);
+               if (!rport)
                        continue;
 
                /* local short-hand pointer. */
-               nrport = ndlp->nrport->remoteport;
+               nrport = rport->remoteport;
+               if (!nrport)
+                       continue;
 
                /* Port state is only one of two values for now. */
                switch (nrport->port_state) {
@@ -3290,6 +3294,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport)
 {
        struct Scsi_Host  *shost;
        struct lpfc_nodelist  *ndlp;
+#if (IS_ENABLED(CONFIG_NVME_FC))
+       struct lpfc_nvme_rport *rport;
+#endif
 
        shost = lpfc_shost_from_vport(vport);
        spin_lock_irq(shost->host_lock);
@@ -3299,8 +3306,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport)
                if (ndlp->rport)
                        ndlp->rport->dev_loss_tmo = vport->cfg_devloss_tmo;
 #if (IS_ENABLED(CONFIG_NVME_FC))
-               if (ndlp->nrport)
-                       nvme_fc_set_remoteport_devloss(ndlp->nrport->remoteport,
+               rport = lpfc_ndlp_get_nrport(ndlp);
+               if (rport)
+                       nvme_fc_set_remoteport_devloss(rport->remoteport,
                                                       vport->cfg_devloss_tmo);
 #endif
        }
index cd3eb6b..afe7883 100644 (file)
@@ -552,6 +552,7 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
        struct nvme_fc_local_port *localport;
        struct lpfc_nvmet_tgtport *tgtp;
        struct nvme_fc_remote_port *nrport;
+       struct lpfc_nvme_rport *rport;
 
        cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE);
        outio = 0;
@@ -695,10 +696,13 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
        len += snprintf(buf + len, size - len, "\tRport List:\n");
        list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
                /* local short-hand pointer. */
-               if (!ndlp->nrport)
+               rport = lpfc_ndlp_get_nrport(ndlp);
+               if (!rport)
                        continue;
 
-               nrport = ndlp->nrport->remoteport;
+               nrport = rport->remoteport;
+               if (!nrport)
+                       continue;
 
                /* Port state is only one of two values for now. */
                switch (nrport->port_state) {
index 1414c58..1cb2c63 100644 (file)
@@ -335,6 +335,7 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport)
                        remoteport);
        spin_lock_irq(&vport->phba->hbalock);
        ndlp->nrport = NULL;
+       ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG;
        spin_unlock_irq(&vport->phba->hbalock);
 
        /* Remove original register reference. The host transport
@@ -2646,6 +2647,7 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        struct nvme_fc_local_port *localport;
        struct lpfc_nvme_lport *lport;
        struct lpfc_nvme_rport *rport;
+       struct lpfc_nvme_rport *oldrport;
        struct nvme_fc_remote_port *remote_port;
        struct nvme_fc_port_info rpinfo;
        struct lpfc_nodelist *prev_ndlp;
@@ -2678,7 +2680,9 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 
        rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn);
        rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn);
-       if (!ndlp->nrport)
+
+       oldrport = lpfc_ndlp_get_nrport(ndlp);
+       if (!oldrport)
                lpfc_nlp_get(ndlp);
 
        ret = nvme_fc_register_remoteport(localport, &rpinfo, &remote_port);
@@ -2688,8 +2692,8 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
                 * new rport.
                 */
                rport = remote_port->private;
-               if (ndlp->nrport) {
-                       if (ndlp->nrport == remote_port->private) {
+               if (oldrport) {
+                       if (oldrport == remote_port->private) {
                                /* Same remoteport.  Just reuse. */
                                lpfc_printf_vlog(ndlp->vport, KERN_INFO,
                                                 LOG_NVME_DISC,
@@ -2713,6 +2717,7 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
                         */
                        spin_lock_irq(&vport->phba->hbalock);
                        ndlp->nrport = NULL;
+                       ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG;
                        spin_unlock_irq(&vport->phba->hbalock);
                        rport->ndlp = NULL;
                        rport->remoteport = NULL;
@@ -2785,7 +2790,7 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        if (!lport)
                goto input_err;
 
-       rport = ndlp->nrport;
+       rport = lpfc_ndlp_get_nrport(ndlp);
        if (!rport)
                goto input_err;
 
index b07188b..5323697 100644 (file)
 #define LPFC_NVME_FB_SHIFT             9
 #define LPFC_NVME_MAX_FB               (1 << 20)       /* 1M */
 
+#define lpfc_ndlp_get_nrport(ndlp)                                     \
+       ((!ndlp->nrport || (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG))   \
+       ? NULL : ndlp->nrport)
+
 struct lpfc_nvme_qhandle {
        uint32_t index;         /* WQ index to use */
        uint32_t qidx;          /* queue index passed to create */