ibmvfc_fabric_login_nvme_done() is invoked under vhost->host->host_lock
via the ibmvfc_locked_done() wrapper, which acquires the spinlock with
IRQs disabled before calling the MAD completion handler.

On the IBMVFC_MAD_SUCCESS path the handler called ibmvfc_nvme_register()
directly. That function calls nvme_fc_register_localport(), which
performs GFP_KERNEL memory allocations and may sleep — both illegal
inside a spinlock-held (atomic) context — resulting in a 'scheduling
while atomic' kernel panic during NVMe/FC fabric login.

Fix this by replacing the direct call with a new do_nvme_register flag
that mirrors the existing do_nvme_login pattern already used in the
driver. The flag is consumed in ibmvfc_do_work() under the
IBMVFC_HOST_ACTION_QUERY case, which runs in the kernel thread context
(ibmvfc_work) where sleeping allocations are safe. The lock is dropped
before calling ibmvfc_nvme_register() and re-acquired on return so the
rest of the QUERY case proceeds normally.

Fixes: 3831863f9f56 ("ibmvfc: register local nvme fc port after fabric login")
Signed-off-by: Tyrel Datwyler <[email protected]>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 12 +++++++++++-
 drivers/scsi/ibmvscsi/ibmvfc.h      |  1 +
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c 
b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 6558ed3c67c4..728529155ded 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -991,6 +991,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host 
*vhost)
        vhost->using_channels = 0;
        vhost->do_scsi_login = 0;
        vhost->do_nvme_login = 0;
+       vhost->do_nvme_register = 0;
        spin_unlock(vhost->crq.q_lock);
        spin_unlock_irqrestore(&vhost->host->host_lock, flags);
 
@@ -1032,6 +1033,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
        vhost->using_channels = 0;
        vhost->do_scsi_login = 0;
        vhost->do_nvme_login = 0;
+       vhost->do_nvme_register = 0;
 
        /* Clean out the queue */
        memset(crq->msgs.crq, 0, PAGE_SIZE);
@@ -5243,7 +5245,7 @@ static void ibmvfc_fabric_login_nvme_done(struct 
ibmvfc_event *evt)
        switch (mad_status) {
        case IBMVFC_MAD_SUCCESS:
                fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
-               ibmvfc_nvme_register(vhost);
+               vhost->do_nvme_register = 1;
                ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
                break;
        case IBMVFC_MAD_FAILED:
@@ -6070,6 +6072,14 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
                        vhost->job_step(vhost);
                break;
        case IBMVFC_HOST_ACTION_QUERY:
+               if (vhost->do_nvme_register) {
+                       vhost->do_nvme_register = 0;
+                       spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+                       ibmvfc_nvme_register(vhost);
+                       spin_lock_irqsave(&vhost->host->host_lock, flags);
+                       if (vhost->action != IBMVFC_HOST_ACTION_QUERY)
+                               break;
+               }
                list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
                        ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
                list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index ca80ceffe53a..df0775183d72 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1005,6 +1005,7 @@ struct ibmvfc_host {
        unsigned int nvme_enabled:1;
        unsigned int do_scsi_login:1;
        unsigned int do_nvme_login:1;
+       unsigned int do_nvme_register:1;
        unsigned int aborting_passthru:1;
        unsigned int scan_complete:1;
        int scan_timeout;
-- 
2.55.0


Reply via email to