tgt->nvme_remote_port is written in two places without holding host_lock:

1. ibmvfc_nvme_register_remoteport() calls nvme_fc_register_remoteport()
   and stores the result directly into tgt->nvme_remote_port with no lock
   held, racing against locked readers in ibmvfc_tgt_add_nvme_rport() and
   ibmvfc_do_work().

2. The ibmvfc_nvme_remoteport_delete() callback writes
   tgt->nvme_remote_port = NULL from the NVMe core's thread context with
   no lock held, again racing against every locked reader of the field.

All readers of tgt->nvme_remote_port acquire host_lock before reading the
field, so both writers must do the same.

For ibmvfc_nvme_register_remoteport(): nvme_fc_register_remoteport() may
sleep so it must be called before acquiring the spinlock. Use a local
pointer to capture the result, then take host_lock and store the pointer
into tgt->nvme_remote_port and set ->private under the lock.

For ibmvfc_nvme_remoteport_delete(): acquire host_lock around the
tgt->nvme_remote_port = NULL store. The callback is always invoked after
ibmvfc_nvme_unregister_remoteport() drops host_lock before waiting for
completion, so taking the lock here is safe.

Fixes: 696d1cc2aaa2 ("scsi: ibmvfc: process NVMe/FC rports in work thread")
Signed-off-by: Tyrel Datwyler <[email protected]>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c 
b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 58e3e50d0c5c..a6fc9e8a35f6 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -26,8 +26,11 @@ static void ibmvfc_nvme_localport_delete(struct 
nvme_fc_local_port *lport)
 static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
 {
        struct ibmvfc_target *tgt = rport->private;
+       unsigned long flags;
 
+       spin_lock_irqsave(&tgt->vhost->host->host_lock, flags);
        tgt->nvme_remote_port = NULL;
+       spin_unlock_irqrestore(&tgt->vhost->host->host_lock, flags);
        complete(&tgt->nvme_delete_done);
 }
 
@@ -473,7 +476,9 @@ static struct nvme_fc_port_template 
ibmvfc_nvme_fc_transport = {
 int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
 {
        struct ibmvfc_host *vhost = tgt->vhost;
+       struct nvme_fc_remote_port *rport;
        struct nvme_fc_port_info pinfo;
+       unsigned long flags;
        int rc;
 
        if (!IS_ENABLED(CONFIG_NVME_FC))
@@ -490,14 +495,16 @@ int ibmvfc_nvme_register_remoteport(struct ibmvfc_target 
*tgt)
        pinfo.port_id = tgt->ids.port_id;
        pinfo.port_role = FC_PORT_ROLE_NVME_TARGET;
 
-       rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo,
-                                        &tgt->nvme_remote_port);
+       rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo, 
&rport);
 
+       spin_lock_irqsave(&vhost->host->host_lock, flags);
        if (!rc) {
                ibmvfc_log(vhost, 2, "register_remoteport: 
traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
                           pinfo.node_name, pinfo.port_name, pinfo.port_id);
-               tgt->nvme_remote_port->private = tgt;
+               rport->private = tgt;
+               tgt->nvme_remote_port = rport;
        }
+       spin_unlock_irqrestore(&vhost->host->host_lock, flags);
 
        return rc;
 }
-- 
2.55.0


Reply via email to