scsi: lpfc: Fix shost refcount mismatch when deleting vport
authorDick Kennedy <dick.kennedy@broadcom.com>
Tue, 30 Jun 2020 21:49:54 +0000 (14:49 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Sep 2020 09:21:17 +0000 (11:21 +0200)
[ Upstream commit 03dbfe0668e6692917ac278883e0586cd7f7d753 ]

When vports are deleted, it is observed that there is memory/kthread
leakage as the vport isn't fully being released.

There is a shost reference taken in scsi_add_host_dma that is not released
during scsi_remove_host. It was noticed that other drivers resolve this by
doing a scsi_host_put after calling scsi_remove_host.

The vport_delete routine is taking two references one that corresponds to
an access to the scsi_host in the vport_delete routine and another that is
released after the adapter mailbox command completes that destroys the VPI
that corresponds to the vport.

Remove one of the references taken such that the second reference that is
put will complete the missing scsi_add_host_dma reference and the shost
will be terminated.

Link: https://lore.kernel.org/r/20200630215001.70793-8-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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/scsi/lpfc/lpfc_vport.c

index e18bbc6..77cb16d 100644 (file)
@@ -624,27 +624,16 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
                    vport->port_state < LPFC_VPORT_READY)
                        return -EAGAIN;
        }
+
        /*
-        * This is a bit of a mess.  We want to ensure the shost doesn't get
-        * torn down until we're done with the embedded lpfc_vport structure.
-        *
-        * Beyond holding a reference for this function, we also need a
-        * reference for outstanding I/O requests we schedule during delete
-        * processing.  But once we scsi_remove_host() we can no longer obtain
-        * a reference through scsi_host_get().
-        *
-        * So we take two references here.  We release one reference at the
-        * bottom of the function -- after delinking the vport.  And we
-        * release the other at the completion of the unreg_vpi that get's
-        * initiated after we've disposed of all other resources associated
-        * with the port.
+        * Take early refcount for outstanding I/O requests we schedule during
+        * delete processing for unreg_vpi.  Always keep this before
+        * scsi_remove_host() as we can no longer obtain a reference through
+        * scsi_host_get() after scsi_host_remove as shost is set to SHOST_DEL.
         */
        if (!scsi_host_get(shost))
                return VPORT_INVAL;
-       if (!scsi_host_get(shost)) {
-               scsi_host_put(shost);
-               return VPORT_INVAL;
-       }
+
        lpfc_free_sysfs_attr(vport);
 
        lpfc_debugfs_terminate(vport);
@@ -792,8 +781,9 @@ skip_logo:
                if (!(vport->vpi_state & LPFC_VPI_REGISTERED) ||
                                lpfc_mbx_unreg_vpi(vport))
                        scsi_host_put(shost);
-       } else
+       } else {
                scsi_host_put(shost);
+       }
 
        lpfc_free_vpi(phba, vport->vpi);
        vport->work_port_events = 0;