From: James Smart <jsmart2...@gmail.com>

[ Upstream commit 6fda20283e55b9d288cd56822ce39fc8e64f2208 ]

The current fcloop driver gets its lport structure from the private
area co-allocated with the fc_localport. All is fine except the
teardown path, which wants to wait on the completion, which is marked
complete by the delete_localport callback performed after
unregister_localport.  The issue is, the nvme_fc transport frees the
localport structure immediately after delete_localport is called,
meaning the original routine is trying to wait on a complete that
was just freed.

Change such that a lport struct is allocated coincident with the
addition and registration of a localport. The private area of the
localport now contains just a backpointer to the real lport struct.
Now, the completion can be waited for, and after completing, the
new structure can be kfree'd.

Signed-off-by: James Smart <james.sm...@broadcom.com>
Signed-off-by: Christoph Hellwig <h...@lst.de>
Signed-off-by: Sasha Levin <alexander.le...@microsoft.com>
---
 drivers/nvme/target/fcloop.c | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 6a018a0bd6ce..bedb66521a4a 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -204,6 +204,10 @@ struct fcloop_lport {
        struct completion unreg_done;
 };
 
+struct fcloop_lport_priv {
+       struct fcloop_lport *lport;
+};
+
 struct fcloop_rport {
        struct nvme_fc_remote_port *remoteport;
        struct nvmet_fc_target_port *targetport;
@@ -657,7 +661,8 @@ fcloop_nport_get(struct fcloop_nport *nport)
 static void
 fcloop_localport_delete(struct nvme_fc_local_port *localport)
 {
-       struct fcloop_lport *lport = localport->private;
+       struct fcloop_lport_priv *lport_priv = localport->private;
+       struct fcloop_lport *lport = lport_priv->lport;
 
        /* release any threads waiting for the unreg to complete */
        complete(&lport->unreg_done);
@@ -697,7 +702,7 @@ static struct nvme_fc_port_template fctemplate = {
        .max_dif_sgl_segments   = FCLOOP_SGL_SEGS,
        .dma_boundary           = FCLOOP_DMABOUND_4G,
        /* sizes of additional private data for data structures */
-       .local_priv_sz          = sizeof(struct fcloop_lport),
+       .local_priv_sz          = sizeof(struct fcloop_lport_priv),
        .remote_priv_sz         = sizeof(struct fcloop_rport),
        .lsrqst_priv_sz         = sizeof(struct fcloop_lsreq),
        .fcprqst_priv_sz        = sizeof(struct fcloop_ini_fcpreq),
@@ -728,11 +733,17 @@ fcloop_create_local_port(struct device *dev, struct 
device_attribute *attr,
        struct fcloop_ctrl_options *opts;
        struct nvme_fc_local_port *localport;
        struct fcloop_lport *lport;
-       int ret;
+       struct fcloop_lport_priv *lport_priv;
+       unsigned long flags;
+       int ret = -ENOMEM;
+
+       lport = kzalloc(sizeof(*lport), GFP_KERNEL);
+       if (!lport)
+               return -ENOMEM;
 
        opts = kzalloc(sizeof(*opts), GFP_KERNEL);
        if (!opts)
-               return -ENOMEM;
+               goto out_free_lport;
 
        ret = fcloop_parse_options(opts, buf);
        if (ret)
@@ -752,23 +763,25 @@ fcloop_create_local_port(struct device *dev, struct 
device_attribute *attr,
 
        ret = nvme_fc_register_localport(&pinfo, &fctemplate, NULL, &localport);
        if (!ret) {
-               unsigned long flags;
-
                /* success */
-               lport = localport->private;
+               lport_priv = localport->private;
+               lport_priv->lport = lport;
+
                lport->localport = localport;
                INIT_LIST_HEAD(&lport->lport_list);
 
                spin_lock_irqsave(&fcloop_lock, flags);
                list_add_tail(&lport->lport_list, &fcloop_lports);
                spin_unlock_irqrestore(&fcloop_lock, flags);
-
-               /* mark all of the input buffer consumed */
-               ret = count;
        }
 
 out_free_opts:
        kfree(opts);
+out_free_lport:
+       /* free only if we're going to fail */
+       if (ret)
+               kfree(lport);
+
        return ret ? ret : count;
 }
 
@@ -790,6 +803,8 @@ __wait_localport_unreg(struct fcloop_lport *lport)
 
        wait_for_completion(&lport->unreg_done);
 
+       kfree(lport);
+
        return ret;
 }
 
-- 
2.14.1

Reply via email to