scsi: lpfc: Fix kernel crash at lpfc_nvme_info_show during remote port bounce
When reading sysfs nvme_info file while a remote port leaves and comes back, a NULL pointer is encountered. The issue is due to ndlp list corruption as the the nvme_info_show does not use the same lock as the rest of the code. Correct by removing the rcu_xxx_lock calls and replace by the host_lock and phba->hbaLock spinlocks that are used by the rest of the driver. Given we're called from sysfs, we are safe to use _irq rather than _irqsave. Link: https://lore.kernel.org/r/20191105005708.7399-4-jsmart2021@gmail.com Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com> Signed-off-by: James Smart <jsmart2021@gmail.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
6bfb162082
commit
6c1e803eac
|
@ -176,7 +176,6 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
int i;
|
int i;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0};
|
char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0};
|
||||||
unsigned long iflags = 0;
|
|
||||||
|
|
||||||
if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
|
if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
|
||||||
len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
|
len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
|
||||||
|
@ -347,7 +346,6 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
if (strlcat(buf, "\nNVME Initiator Enabled\n", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, "\nNVME Initiator Enabled\n", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto buffer_done;
|
goto buffer_done;
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
scnprintf(tmp, sizeof(tmp),
|
scnprintf(tmp, sizeof(tmp),
|
||||||
"XRI Dist lpfc%d Total %d IO %d ELS %d\n",
|
"XRI Dist lpfc%d Total %d IO %d ELS %d\n",
|
||||||
phba->brd_no,
|
phba->brd_no,
|
||||||
|
@ -355,7 +353,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
phba->sli4_hba.io_xri_max,
|
phba->sli4_hba.io_xri_max,
|
||||||
lpfc_sli4_get_els_iocb_cnt(phba));
|
lpfc_sli4_get_els_iocb_cnt(phba));
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto buffer_done;
|
||||||
|
|
||||||
/* Port state is only one of two values for now. */
|
/* Port state is only one of two values for now. */
|
||||||
if (localport->port_id)
|
if (localport->port_id)
|
||||||
|
@ -371,15 +369,17 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
wwn_to_u64(vport->fc_nodename.u.wwn),
|
wwn_to_u64(vport->fc_nodename.u.wwn),
|
||||||
localport->port_id, statep);
|
localport->port_id, statep);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto buffer_done;
|
||||||
|
|
||||||
|
spin_lock_irq(shost->host_lock);
|
||||||
|
|
||||||
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
|
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
|
||||||
nrport = NULL;
|
nrport = NULL;
|
||||||
spin_lock_irqsave(&vport->phba->hbalock, iflags);
|
spin_lock(&vport->phba->hbalock);
|
||||||
rport = lpfc_ndlp_get_nrport(ndlp);
|
rport = lpfc_ndlp_get_nrport(ndlp);
|
||||||
if (rport)
|
if (rport)
|
||||||
nrport = rport->remoteport;
|
nrport = rport->remoteport;
|
||||||
spin_unlock_irqrestore(&vport->phba->hbalock, iflags);
|
spin_unlock(&vport->phba->hbalock);
|
||||||
if (!nrport)
|
if (!nrport)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -398,39 +398,39 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
|
||||||
/* Tab in to show lport ownership. */
|
/* Tab in to show lport ownership. */
|
||||||
if (strlcat(buf, "NVME RPORT ", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, "NVME RPORT ", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
if (phba->brd_no >= 10) {
|
if (phba->brd_no >= 10) {
|
||||||
if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
scnprintf(tmp, sizeof(tmp), "WWPN x%llx ",
|
scnprintf(tmp, sizeof(tmp), "WWPN x%llx ",
|
||||||
nrport->port_name);
|
nrport->port_name);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
|
|
||||||
scnprintf(tmp, sizeof(tmp), "WWNN x%llx ",
|
scnprintf(tmp, sizeof(tmp), "WWNN x%llx ",
|
||||||
nrport->node_name);
|
nrport->node_name);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
|
|
||||||
scnprintf(tmp, sizeof(tmp), "DID x%06x ",
|
scnprintf(tmp, sizeof(tmp), "DID x%06x ",
|
||||||
nrport->port_id);
|
nrport->port_id);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
|
|
||||||
/* An NVME rport can have multiple roles. */
|
/* An NVME rport can have multiple roles. */
|
||||||
if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) {
|
if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) {
|
||||||
if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) {
|
if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) {
|
||||||
if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) {
|
if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) {
|
||||||
if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
|
if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
|
||||||
FC_PORT_ROLE_NVME_TARGET |
|
FC_PORT_ROLE_NVME_TARGET |
|
||||||
|
@ -438,14 +438,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x",
|
scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x",
|
||||||
nrport->port_role);
|
nrport->port_role);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
scnprintf(tmp, sizeof(tmp), "%s\n", statep);
|
scnprintf(tmp, sizeof(tmp), "%s\n", statep);
|
||||||
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
|
||||||
goto rcu_unlock_buf_done;
|
goto unlock_buf_done;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
spin_unlock_irq(shost->host_lock);
|
||||||
|
|
||||||
if (!lport)
|
if (!lport)
|
||||||
goto buffer_done;
|
goto buffer_done;
|
||||||
|
@ -505,11 +505,11 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
|
||||||
atomic_read(&lport->cmpl_fcp_err));
|
atomic_read(&lport->cmpl_fcp_err));
|
||||||
strlcat(buf, tmp, PAGE_SIZE);
|
strlcat(buf, tmp, PAGE_SIZE);
|
||||||
|
|
||||||
/* RCU is already unlocked. */
|
/* host_lock is already unlocked. */
|
||||||
goto buffer_done;
|
goto buffer_done;
|
||||||
|
|
||||||
rcu_unlock_buf_done:
|
unlock_buf_done:
|
||||||
rcu_read_unlock();
|
spin_unlock_irq(shost->host_lock);
|
||||||
|
|
||||||
buffer_done:
|
buffer_done:
|
||||||
len = strnlen(buf, PAGE_SIZE);
|
len = strnlen(buf, PAGE_SIZE);
|
||||||
|
|
Loading…
Reference in New Issue