[PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context

From: Tyrel Datwyler

Date: Wed Sep 16 2026 - 19:10:16 EST


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: 4eb644adc42f ("ibmvfc: register local nvme fc port after fabric login")
Signed-off-by: Tyrel Datwyler <tyreld@xxxxxxxxxxxxx>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 8 +++++++-
drivers/scsi/ibmvscsi/ibmvfc.h | 1 +
2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 26a73db66254..420ddb958466 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5245,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:
@@ -6072,6 +6072,12 @@ 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);
+ }
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