]> www.infradead.org Git - users/hch/misc.git/commitdiff
nvmet-fcloop: prevent double port deletion
authorDaniel Wagner <wagi@kernel.org>
Wed, 7 May 2025 12:23:04 +0000 (14:23 +0200)
committerChristoph Hellwig <hch@lst.de>
Mon, 12 May 2025 14:03:29 +0000 (16:03 +0200)
The delete callback can be called either via the unregister function or
from the transport directly. Thus it is necessary ensure resources are
not freed multiple times.

Signed-off-by: Daniel Wagner <wagi@kernel.org>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/target/fcloop.c

index f0c0d95b8dde82d36a50a9189edd2b700861fcfe..52f0061920a823f6e45effdb91517aec01c90046 100644 (file)
@@ -215,6 +215,9 @@ struct fcloop_lport_priv {
        struct fcloop_lport *lport;
 };
 
+/* The port is already being removed, avoid double free */
+#define PORT_DELETED   0
+
 struct fcloop_rport {
        struct nvme_fc_remote_port      *remoteport;
        struct nvmet_fc_target_port     *targetport;
@@ -223,6 +226,7 @@ struct fcloop_rport {
        spinlock_t                      lock;
        struct list_head                ls_list;
        struct work_struct              ls_work;
+       unsigned long                   flags;
 };
 
 struct fcloop_tport {
@@ -233,6 +237,7 @@ struct fcloop_tport {
        spinlock_t                      lock;
        struct list_head                ls_list;
        struct work_struct              ls_work;
+       unsigned long                   flags;
 };
 
 struct fcloop_nport {
@@ -1061,30 +1066,38 @@ static void
 fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
 {
        struct fcloop_rport *rport = remoteport->private;
+       bool put_port = false;
        unsigned long flags;
 
        flush_work(&rport->ls_work);
 
        spin_lock_irqsave(&fcloop_lock, flags);
+       if (!test_and_set_bit(PORT_DELETED, &rport->flags))
+               put_port = true;
        rport->nport->rport = NULL;
        spin_unlock_irqrestore(&fcloop_lock, flags);
 
-       fcloop_nport_put(rport->nport);
+       if (put_port)
+               fcloop_nport_put(rport->nport);
 }
 
 static void
 fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
 {
        struct fcloop_tport *tport = targetport->private;
+       bool put_port = false;
        unsigned long flags;
 
        flush_work(&tport->ls_work);
 
        spin_lock_irqsave(&fcloop_lock, flags);
+       if (!test_and_set_bit(PORT_DELETED, &tport->flags))
+               put_port = true;
        tport->nport->tport = NULL;
        spin_unlock_irqrestore(&fcloop_lock, flags);
 
-       fcloop_nport_put(tport->nport);
+       if (put_port)
+               fcloop_nport_put(tport->nport);
 }
 
 #define        FCLOOP_HW_QUEUES                4
@@ -1427,6 +1440,7 @@ fcloop_create_remote_port(struct device *dev, struct device_attribute *attr,
        rport->nport = nport;
        rport->lport = nport->lport;
        nport->rport = rport;
+       rport->flags = 0;
        spin_lock_init(&rport->lock);
        INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work);
        INIT_LIST_HEAD(&rport->ls_list);
@@ -1524,6 +1538,7 @@ fcloop_create_target_port(struct device *dev, struct device_attribute *attr,
        tport->nport = nport;
        tport->lport = nport->lport;
        nport->tport = tport;
+       tport->flags = 0;
        spin_lock_init(&tport->lock);
        INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work);
        INIT_LIST_HEAD(&tport->ls_list);