From: James Smart <james.smart@broadcom.com>
Date: Sun, 15 Nov 2020 11:26:32 -0800
Subject: scsi: lpfc: Fix removal of SCSI transport device get and put on dev
structure
Patch-mainline: v5.11-rc1
Git-commit: 95f0ef8a8368b2195ca9b8b4eca9a3ec8d132a75
References: bsc#1164780
The lpfc driver is calling get_device and put_device on scsi_fc_transport
device structure. When this code was removed, the driver triggered an oops
in "scsi_is_host_dev" when the first SCSI target was unregistered from the
transport.
The reason the calls were necessary is that the driver is calling
scsi_remove_host too early, before the target rports are unregistered and
the scsi devices disconnected from the scsi_host. The fc_host was torn
down during fc_remove_host.
Fix by moving the lpfc_pci_remove_one_s3/s4 calls to scsi_remove_host to
after the nodes are cleaned up. Remove the get_device and put_device calls
and the supporting code.
Link: https://lore.kernel.org/r/20201115192646.12977-4-james.smart@broadcom.com
Co-developed-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <james.smart@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Acked-by: Daniel Wagner <dwagner@suse.de>
---
drivers/scsi/lpfc/lpfc_els.c | 4 ----
drivers/scsi/lpfc/lpfc_hbadisc.c | 12 ++----------
drivers/scsi/lpfc/lpfc_init.c | 14 +++++++++-----
drivers/scsi/lpfc/lpfc_vport.c | 6 ++++--
4 files changed, 15 insertions(+), 21 deletions(-)
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1601,7 +1601,6 @@ lpfc_plogi_confirm_nport(struct lpfc_hba
u32 keep_nlp_fc4_type = 0;
struct lpfc_nvme_rport *keep_nrport = NULL;
int put_node;
- int put_rport;
unsigned long *active_rrqs_xri_bitmap = NULL;
/* Fabric nodes can have the same WWPN so we don't bother searching
@@ -1804,13 +1803,10 @@ lpfc_plogi_confirm_nport(struct lpfc_hba
if (rport) {
rdata = rport->dd_data;
put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
rdata->pnode = NULL;
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
}
}
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -119,7 +119,6 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport
struct lpfc_hba *phba;
struct lpfc_work_evt *evtp;
int put_node;
- int put_rport;
unsigned long iflags;
rdata = rport->dd_data;
@@ -144,13 +143,10 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport
*/
if (vport->load_flag & FC_UNLOADING) {
put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
rdata->pnode = NULL;
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
return;
}
@@ -265,7 +261,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_no
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
- put_device(&rport->dev);
return fcf_inuse;
}
@@ -286,7 +281,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_no
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
- put_device(&rport->dev);
if (ndlp->nlp_type & NLP_FABRIC)
return fcf_inuse;
@@ -4186,8 +4180,6 @@ lpfc_register_remote_port(struct lpfc_vp
lpfc_nlp_put(ndlp);
rdata->pnode = NULL;
}
- /* drop reference for earlier registeration */
- put_device(&rport->dev);
}
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
@@ -4199,7 +4191,7 @@ lpfc_register_remote_port(struct lpfc_vp
return;
ndlp->rport = rport = fc_remote_port_add(shost, 0, &rport_ids);
- if (!rport || !get_device(&rport->dev)) {
+ if (!rport) {
dev_printk(KERN_WARNING, &phba->pcidev->dev,
"Warning: fc_remote_port_add failed\n");
return;
@@ -4240,6 +4232,7 @@ lpfc_register_remote_port(struct lpfc_vp
(rport->scsi_target_id < LPFC_MAX_TARGET)) {
ndlp->nlp_sid = rport->scsi_target_id;
}
+
return;
}
@@ -5214,7 +5207,6 @@ lpfc_nlp_remove(struct lpfc_vport *vport
rdata = rport->dd_data;
rdata->pnode = NULL;
ndlp->rport = NULL;
- put_device(&rport->dev);
}
}
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -12502,12 +12502,15 @@ lpfc_pci_remove_one_s3(struct pci_dev *p
}
lpfc_destroy_vport_work_array(phba, vports);
- /* Remove FC host and then SCSI host with the physical port */
+ /* Remove FC host with the physical port */
fc_remove_host(shost);
- scsi_remove_host(shost);
+ /* Clean up all nodes, mailboxes and IOs. */
lpfc_cleanup(vport);
+ /* Remove the shost now that the devices connections are lost. */
+ scsi_remove_host(shost);
+
/*
* Bring down the SLI Layer. This step disable all interrupts,
* clears the rings, discards all mailbox commands, and resets
@@ -13349,7 +13352,6 @@ lpfc_pci_remove_one_s4(struct pci_dev *p
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- /* Free the HBA sysfs attributes */
lpfc_free_sysfs_attr(vport);
/* Release all the vports against this physical port */
@@ -13362,9 +13364,8 @@ lpfc_pci_remove_one_s4(struct pci_dev *p
}
lpfc_destroy_vport_work_array(phba, vports);
- /* Remove FC host and then SCSI host with the physical port */
+ /* Remove FC host with the physical port */
fc_remove_host(shost);
- scsi_remove_host(shost);
/* Perform ndlp cleanup on the physical port. The nvme and nvmet
* localports are destroyed after to cleanup all transport memory.
@@ -13377,6 +13378,9 @@ lpfc_pci_remove_one_s4(struct pci_dev *p
if (phba->cfg_xri_rebalancing)
lpfc_destroy_multixri_pools(phba);
+ /* Remove the shost now that the devices connections are lost. */
+ scsi_remove_host(shost);
+
/*
* Bring down the SLI Layer. This step disables all interrupts,
* clears the rings, discards all mailbox commands, and resets
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -664,9 +664,8 @@ lpfc_vport_delete(struct fc_vport *fc_vp
ns_ndlp_referenced = true;
}
- /* Remove FC host and then SCSI host with the vport */
+ /* Remove FC host to break driver binding. */
fc_remove_host(shost);
- scsi_remove_host(shost);
ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
@@ -747,6 +746,9 @@ lpfc_vport_delete(struct fc_vport *fc_vp
}
lpfc_cleanup(vport);
+
+ /* Remove scsi host now. The nodes are cleaned up. */
+ scsi_remove_host(shost);
lpfc_sli_host_down(vport);
lpfc_stop_vport_timers(vport);