* [RFC PATCH 1/3] scsi: lpfc: Do not touch the SFP mailbox after a wait timeout
2026-10-04 3:41 [RFC PATCH 0/3] scsi: lpfc: Fix mailbox timeout ownership races Artem Dinaburg
@ 2026-10-04 3:41 ` Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 2/3] scsi: lpfc: Resolve synchronous mailbox wait ownership under hbalock Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 3/3] scsi: lpfc: Add KUnit tests for mailbox wait ownership Artem Dinaburg
2 siblings, 0 replies; 4+ messages in thread
From: Artem Dinaburg @ 2026-10-04 3:41 UTC (permalink / raw)
To: Justin Tee, Paul Ely
Cc: James E.J. Bottomley, James Smart, James Bottomley,
Martin K . Petersen, linux-scsi, linux-kernel, Artem Dinaburg,
stable
Commit ede596b1434b ("scsi: lpfc: Handle mailbox timeouts in
lpfc_get_sfp_info") made the mailbox cleanup in lpfc_get_sfp_info_wait()
conditional on LPFC_MBX_WAKE, so that a timed-out mailbox is left for
its late completion.
Once lpfc_sli_issue_mbox_wait() returns MBX_TIMEOUT, the mailbox belongs
to the late completion, whose default handler frees it together with its
DMA buffer. Reading mbox_flag at the error label after a timeout is
therefore a use-after-free if the completion has already run.
The LPFC_MBX_WAKE test is also wrong when issuing fails immediately.
lpfc_sli_issue_mbox_wait() clears the flag before issuing, so the caller
still owns the mailbox but skips the cleanup, leaking the mailbox and its
DMA buffer. The A2 page dump does not check for an issue failure at all,
so it can report success with a zeroed page.
Never touch the mailbox after MBX_TIMEOUT, and treat every other
non-success return from either dump as a failure that the caller cleans
up.
Fixes: ede596b1434b ("scsi: lpfc: Handle mailbox timeouts in lpfc_get_sfp_info")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Artem Dinaburg <artem@trailofbits.com>
---
drivers/scsi/lpfc/lpfc_els.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 52fc50589..ea8835515 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -7410,12 +7410,12 @@ int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
}
mbox->vport = phba->pport;
rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_SLI4_CONFIG_TMO);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc == MBX_TIMEOUT)
+ goto error;
+ if (rc != MBX_SUCCESS) {
rc = 1;
goto error;
}
- if (rc == MBX_TIMEOUT)
- goto error;
if (phba->sli_rev == LPFC_SLI_REV4)
mp = mbox->ctx_buf;
else
@@ -7472,6 +7472,10 @@ int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
if (rc == MBX_TIMEOUT)
goto error;
+ if (rc != MBX_SUCCESS) {
+ rc = 1;
+ goto error;
+ }
if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) {
rc = 1;
goto error;
@@ -7482,7 +7486,7 @@ int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
DMP_SFF_PAGE_A2_SIZE);
error:
- if (mbox->mbox_flag & LPFC_MBX_WAKE) {
+ if (rc != MBX_TIMEOUT) {
mbox->ctx_buf = mpsave;
lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
}
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [RFC PATCH 2/3] scsi: lpfc: Resolve synchronous mailbox wait ownership under hbalock
2026-10-04 3:41 [RFC PATCH 0/3] scsi: lpfc: Fix mailbox timeout ownership races Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 1/3] scsi: lpfc: Do not touch the SFP mailbox after a wait timeout Artem Dinaburg
@ 2026-10-04 3:41 ` Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 3/3] scsi: lpfc: Add KUnit tests for mailbox wait ownership Artem Dinaburg
2 siblings, 0 replies; 4+ messages in thread
From: Artem Dinaburg @ 2026-10-04 3:41 UTC (permalink / raw)
To: Justin Tee, Paul Ely
Cc: James E.J. Bottomley, James Smart, James Bottomley,
Martin K . Petersen, linux-scsi, linux-kernel, Artem Dinaburg,
stable
lpfc_sli_issue_mbox_wait() detaches the waiter under hbalock and, on
timeout, switches mbox_cmpl to lpfc_sli_def_mbox_cmpl() so that a late
completion frees the mailbox. The completion side does not make its
decision under the same lock. lpfc_sli_wake_mbox_wait() sets
LPFC_MBX_WAKE before taking hbalock, and the mailbox worker and the
flush path load mbox_cmpl without hbalock (lpfc_sli_handle_mb_event(),
lpfc_sli_mbox_sys_flush()). A completion that loaded the wake callback
before the waiter timed out then finds no waiter, wakes nobody and leaks
the mailbox. Because LPFC_MBX_WAKE is set before hbalock is taken, a
waiter that times out in that window also returns MBX_SUCCESS, and its
caller can free the mailbox before the wake handler reads it. This
affects every caller of lpfc_sli_issue_mbox_wait().
Decide ownership in lpfc_sli_wake_mbox_wait() under hbalock. If a waiter
is still attached, set LPFC_MBX_WAKE and complete it. Otherwise run
lpfc_sli_def_mbox_cmpl() after dropping the lock.
The wake callback now handles the detached case itself, so the timeout
path no longer switches mbox_cmpl, and the waiter no longer writes it
while the completion side may be reading it without the lock.
Fixes: 858c9f6c19c6 ("[SCSI] lpfc: bug fixes")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Artem Dinaburg <artem@trailofbits.com>
---
drivers/scsi/lpfc/lpfc_sli.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index cfa437116..c2086f331 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -2814,9 +2814,9 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
*
* This is completion handler function for mailbox commands issued from
* lpfc_sli_issue_mbox_wait function. This function is called by the
- * mailbox event handler function with no lock held. This function
- * will wake up thread waiting on the wait queue pointed by context1
- * of the mailbox.
+ * mailbox event handler function with no lock held. If the waiter is
+ * still attached, wake it; otherwise the caller timed out and the
+ * callback owns the mailbox resources.
**/
void
lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
@@ -2824,17 +2824,18 @@ lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
unsigned long drvr_flag;
struct completion *pmbox_done;
- /*
- * If pmbox_done is empty, the driver thread gave up waiting and
- * continued running.
- */
- pmboxq->mbox_flag |= LPFC_MBX_WAKE;
+ /* Decide who owns the mailbox under the same lock as waiter detach. */
spin_lock_irqsave(&phba->hbalock, drvr_flag);
pmbox_done = pmboxq->ctx_u.mbox_wait;
- if (pmbox_done)
- complete(pmbox_done);
+ if (!pmbox_done) {
+ spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
+ lpfc_sli_def_mbox_cmpl(phba, pmboxq);
+ return;
+ }
+
+ pmboxq->mbox_flag |= LPFC_MBX_WAKE;
+ complete(pmbox_done);
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
- return;
}
/**
@@ -13313,15 +13314,11 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
spin_lock_irqsave(&phba->hbalock, flag);
pmboxq->ctx_u.mbox_wait = NULL;
- /*
- * if LPFC_MBX_WAKE flag is set the mailbox is completed
- * else do not free the resources.
- */
+ /* The callback owns cleanup if it has not woken the waiter. */
if (pmboxq->mbox_flag & LPFC_MBX_WAKE) {
retval = MBX_SUCCESS;
} else {
retval = MBX_TIMEOUT;
- pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
spin_unlock_irqrestore(&phba->hbalock, flag);
}
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [RFC PATCH 3/3] scsi: lpfc: Add KUnit tests for mailbox wait ownership
2026-10-04 3:41 [RFC PATCH 0/3] scsi: lpfc: Fix mailbox timeout ownership races Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 1/3] scsi: lpfc: Do not touch the SFP mailbox after a wait timeout Artem Dinaburg
2026-10-04 3:41 ` [RFC PATCH 2/3] scsi: lpfc: Resolve synchronous mailbox wait ownership under hbalock Artem Dinaburg
@ 2026-10-04 3:41 ` Artem Dinaburg
2 siblings, 0 replies; 4+ messages in thread
From: Artem Dinaburg @ 2026-10-04 3:41 UTC (permalink / raw)
To: Justin Tee, Paul Ely
Cc: James E.J. Bottomley, James Smart, James Bottomley,
Martin K . Petersen, linux-scsi, linux-kernel, Artem Dinaburg
Exercise synchronous mailbox ownership without LPFC hardware by
replacing the mailbox issue callback and tracking one mailbox through a
one-element mempool.
Cover completion before timeout with both accepted issue returns
(MBX_SUCCESS and MBX_BUSY), normal late completion, immediate
submission failure, and the stale-callback interleaving where the
completion dispatcher saved the wake callback before the waiter timed
out. The stale-callback test fails without the wake handler change
because the mailbox is not returned to the pool.
Also run lpfc_get_sfp_info_wait() on SLI-4 against a KUnit device with
a real DMA pool, counting released mbufs through the mbuf safety pool.
Cover a successful A0/A2 dump, an issue failure on either page, and a
timeout on the A0 page followed by the current or a stale late
completion. The issue failure cases fail without the SFP fix: the
mailbox and its mbuf are leaked, and an A2 issue failure is reported as
success.
The fake issue callback never starts a command, so these tests check
mailbox ownership, not an SFP transaction or a timeout on an adapter.
Assisted-by: LLM
Signed-off-by: Artem Dinaburg <artem@trailofbits.com>
---
drivers/scsi/Kconfig | 16 ++
drivers/scsi/lpfc/.kunitconfig | 9 +
drivers/scsi/lpfc/Makefile | 2 +
drivers/scsi/lpfc/tests/mbox_kunit.c | 362 +++++++++++++++++++++++++++
4 files changed, 389 insertions(+)
create mode 100644 drivers/scsi/lpfc/.kunitconfig
create mode 100644 drivers/scsi/lpfc/tests/mbox_kunit.c
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 1eec66195..72f2a9da3 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1163,6 +1163,22 @@ config SCSI_LPFC_DEBUG_FS
This makes debugging information from the lpfc driver
available via the debugfs filesystem.
+config LPFC_MBOX_KUNIT_TEST
+ bool "KUnit tests for LPFC mailbox ownership" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y && SCSI_LPFC=y
+ default KUNIT_ALL_TESTS
+ help
+ Build KUnit tests for synchronous LPFC mailbox wait and completion
+ ownership, including the SFP page dump. The tests use a fake issue
+ callback and a one-element memory pool, so they do not require an
+ LPFC adapter. They are built into LPFC to exercise its non-exported
+ mailbox callbacks.
+
+ For more information on KUnit and unit tests in general, please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
source "drivers/scsi/elx/Kconfig"
config SCSI_SIM710
diff --git a/drivers/scsi/lpfc/.kunitconfig b/drivers/scsi/lpfc/.kunitconfig
new file mode 100644
index 000000000..3fa4ad272
--- /dev/null
+++ b/drivers/scsi/lpfc/.kunitconfig
@@ -0,0 +1,9 @@
+CONFIG_KUNIT=y
+CONFIG_NET=y
+CONFIG_PCI=y
+CONFIG_SCSI=y
+CONFIG_SCSI_LOWLEVEL=y
+CONFIG_SCSI_FC_ATTRS=y
+CONFIG_CPU_FREQ=y
+CONFIG_SCSI_LPFC=y
+CONFIG_LPFC_MBOX_KUNIT_TEST=y
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
index bbd1faf41..d2b1f0990 100644
--- a/drivers/scsi/lpfc/Makefile
+++ b/drivers/scsi/lpfc/Makefile
@@ -34,3 +34,5 @@ lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \
lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o \
lpfc_scsi.o lpfc_attr.o lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o \
lpfc_nvme.o lpfc_nvmet.o lpfc_vmid.o
+
+lpfc-$(CONFIG_LPFC_MBOX_KUNIT_TEST) += tests/mbox_kunit.o
diff --git a/drivers/scsi/lpfc/tests/mbox_kunit.c b/drivers/scsi/lpfc/tests/mbox_kunit.c
new file mode 100644
index 000000000..2bdc2d5fc
--- /dev/null
+++ b/drivers/scsi/lpfc/tests/mbox_kunit.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/mempool.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_crtn.h"
+
+enum lpfc_mbox_issue_mode {
+ LPFC_MBOX_TEST_CAPTURE,
+ LPFC_MBOX_TEST_COMPLETE,
+ LPFC_MBOX_TEST_EXPIRE,
+};
+
+struct lpfc_mbox_test {
+ struct lpfc_hba phba;
+ struct lpfc_vport pport;
+ LPFC_MBOXQ_t storage;
+ mempool_t pool;
+ struct lpfc_dmabuf mbuf_slots[2];
+ LPFC_MBOXQ_t *mbox;
+ void (*captured_cmpl)(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox);
+ enum lpfc_mbox_issue_mode mode;
+ int issue_result;
+ unsigned int good_issues;
+ bool storage_allocated;
+ unsigned int excess_frees;
+};
+
+static void *lpfc_mbox_test_alloc(gfp_t gfp_mask, void *pool_data)
+{
+ struct lpfc_mbox_test *ctx = pool_data;
+
+ (void)gfp_mask;
+ if (ctx->storage_allocated)
+ return NULL;
+
+ ctx->storage_allocated = true;
+ return &ctx->storage;
+}
+
+static void lpfc_mbox_test_free(void *element, void *pool_data)
+{
+ struct lpfc_mbox_test *ctx = pool_data;
+
+ (void)element;
+ ctx->excess_frees++;
+}
+
+static int lpfc_mbox_test_issue(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox,
+ uint32_t flag)
+{
+ struct lpfc_mbox_test *ctx;
+
+ (void)flag;
+ ctx = container_of(phba, struct lpfc_mbox_test, phba);
+ ctx->captured_cmpl = mbox->mbox_cmpl;
+ if (ctx->good_issues) {
+ ctx->good_issues--;
+ ctx->captured_cmpl(phba, mbox);
+ return MBX_SUCCESS;
+ }
+
+ if (ctx->mode == LPFC_MBOX_TEST_COMPLETE)
+ ctx->captured_cmpl(phba, mbox);
+ else if (ctx->mode == LPFC_MBOX_TEST_EXPIRE)
+ /* End the wait without completing the command. */
+ complete(mbox->ctx_u.mbox_wait);
+
+ return ctx->issue_result;
+}
+
+static LPFC_MBOXQ_t *lpfc_mbox_test_take_from_pool(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ LPFC_MBOXQ_t *mbox;
+
+ mbox = mempool_alloc_preallocated(&ctx->pool);
+ KUNIT_EXPECT_PTR_EQ(test, mbox, &ctx->storage);
+ return mbox;
+}
+
+static void lpfc_mbox_test_expect_released(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ LPFC_MBOXQ_t *mbox;
+
+ mbox = lpfc_mbox_test_take_from_pool(test);
+ if (mbox)
+ mempool_free(mbox, &ctx->pool);
+ else
+ mempool_free(&ctx->storage, &ctx->pool);
+ KUNIT_EXPECT_EQ(test, ctx->excess_frees, 0U);
+}
+
+static void lpfc_mbox_test_expect_caller_owned(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ KUNIT_EXPECT_PTR_EQ(test, mempool_alloc_preallocated(&ctx->pool), NULL);
+}
+
+static void lpfc_mbox_test_mbuf_exit(void *data)
+{
+ struct lpfc_hba *phba = data;
+ struct lpfc_dma_pool *safety = &phba->lpfc_mbuf_safety_pool;
+
+ while (safety->current_count) {
+ safety->current_count--;
+ dma_pool_free(phba->lpfc_mbuf_pool,
+ safety->elements[safety->current_count].virt,
+ safety->elements[safety->current_count].phys);
+ }
+ dma_pool_destroy(phba->lpfc_mbuf_pool);
+}
+
+static struct lpfc_rdp_context *lpfc_mbox_test_sfp_init(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ struct lpfc_hba *phba = &ctx->phba;
+ struct lpfc_rdp_context *rdp;
+ struct device *dev;
+ int rc;
+
+ /* lpfc_get_sfp_info_wait() allocates its own mailbox. */
+ mempool_free(ctx->mbox, &ctx->pool);
+ phba->sli_rev = LPFC_SLI_REV4;
+
+ dev = kunit_device_register(test, "lpfc_mbox_test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ rc = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ phba->lpfc_mbuf_pool = dma_pool_create("lpfc_mbox_test", dev,
+ LPFC_BPL_SIZE, 8, 0);
+ KUNIT_ASSERT_NOT_NULL(test, phba->lpfc_mbuf_pool);
+
+ /* Park freed mbufs in the safety pool so that they can be counted. */
+ phba->lpfc_mbuf_safety_pool.elements = ctx->mbuf_slots;
+ phba->lpfc_mbuf_safety_pool.max_count = ARRAY_SIZE(ctx->mbuf_slots);
+ rc = kunit_add_action_or_reset(test, lpfc_mbox_test_mbuf_exit, phba);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+
+ rdp = kunit_kzalloc(test, sizeof(*rdp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, rdp);
+ return rdp;
+}
+
+static void lpfc_mbox_test_expect_sfp_released(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ lpfc_mbox_test_expect_released(test);
+ KUNIT_EXPECT_EQ(test, ctx->phba.lpfc_mbuf_safety_pool.current_count,
+ 1U);
+}
+
+static int lpfc_mbox_test_init(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx;
+ int rc;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ spin_lock_init(&ctx->phba.hbalock);
+ ctx->phba.pport = &ctx->pport;
+ ctx->phba.lpfc_sli_issue_mbox = lpfc_mbox_test_issue;
+ ctx->pport.phba = &ctx->phba;
+ ctx->issue_result = MBX_SUCCESS;
+ rc = mempool_init(&ctx->pool, 1, lpfc_mbox_test_alloc,
+ lpfc_mbox_test_free, ctx);
+ if (rc)
+ return rc;
+
+ ctx->phba.mbox_mem_pool = &ctx->pool;
+ ctx->mbox = mempool_alloc_preallocated(&ctx->pool);
+ if (!ctx->mbox) {
+ mempool_exit(&ctx->pool);
+ return -ENOMEM;
+ }
+ ctx->mbox->u.mb.mbxCommand = MBX_HEARTBEAT;
+ test->priv = ctx;
+ return 0;
+}
+
+static void lpfc_mbox_test_exit(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ LPFC_MBOXQ_t *mbox;
+
+ mbox = mempool_alloc_preallocated(&ctx->pool);
+ if (mbox)
+ mempool_free(mbox, &ctx->pool);
+ else
+ mempool_free(&ctx->storage, &ctx->pool);
+ mempool_exit(&ctx->pool);
+}
+
+static void lpfc_mbox_completion_before_timeout(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ int rc;
+
+ ctx->mode = LPFC_MBOX_TEST_COMPLETE;
+ rc = lpfc_sli_issue_mbox_wait(&ctx->phba, ctx->mbox, 0);
+ KUNIT_EXPECT_EQ(test, rc, MBX_SUCCESS);
+ lpfc_mbox_test_expect_caller_owned(test);
+
+ lpfc_mbox_rsrc_cleanup(&ctx->phba, ctx->mbox, MBOX_THD_UNLOCKED);
+ lpfc_mbox_test_expect_released(test);
+}
+
+static void lpfc_mbox_busy_completion_before_timeout(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ /* MBX_BUSY means the command was queued, not rejected. */
+ ctx->issue_result = MBX_BUSY;
+ lpfc_mbox_completion_before_timeout(test);
+}
+
+static void lpfc_mbox_timeout_then_current_callback(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ void (*callback)(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox);
+ int rc;
+
+ rc = lpfc_sli_issue_mbox_wait(&ctx->phba, ctx->mbox, 0);
+ KUNIT_ASSERT_EQ(test, rc, MBX_TIMEOUT);
+ callback = ctx->mbox->mbox_cmpl;
+ callback(&ctx->phba, ctx->mbox);
+
+ lpfc_mbox_test_expect_released(test);
+}
+
+static void lpfc_mbox_timeout_then_stale_callback(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ int rc;
+
+ rc = lpfc_sli_issue_mbox_wait(&ctx->phba, ctx->mbox, 0);
+ KUNIT_ASSERT_EQ(test, rc, MBX_TIMEOUT);
+ KUNIT_ASSERT_TRUE(test,
+ ctx->captured_cmpl == lpfc_sli_wake_mbox_wait);
+ ctx->captured_cmpl(&ctx->phba, ctx->mbox);
+
+ lpfc_mbox_test_expect_released(test);
+}
+
+static void lpfc_mbox_immediate_issue_failure(struct kunit *test)
+{
+ struct lpfc_mbox_test *ctx = test->priv;
+ int rc;
+
+ ctx->issue_result = MBX_NOT_FINISHED;
+ rc = lpfc_sli_issue_mbox_wait(&ctx->phba, ctx->mbox, 0);
+ KUNIT_EXPECT_EQ(test, rc, MBX_NOT_FINISHED);
+ lpfc_mbox_test_expect_caller_owned(test);
+
+ lpfc_mbox_rsrc_cleanup(&ctx->phba, ctx->mbox, MBOX_THD_UNLOCKED);
+ lpfc_mbox_test_expect_released(test);
+}
+
+static void lpfc_mbox_sfp_success(struct kunit *test)
+{
+ struct lpfc_rdp_context *rdp = lpfc_mbox_test_sfp_init(test);
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ ctx->mode = LPFC_MBOX_TEST_COMPLETE;
+ KUNIT_EXPECT_EQ(test, lpfc_get_sfp_info_wait(&ctx->phba, rdp), 0);
+ lpfc_mbox_test_expect_sfp_released(test);
+}
+
+static void lpfc_mbox_sfp_a0_issue_failure(struct kunit *test)
+{
+ struct lpfc_rdp_context *rdp = lpfc_mbox_test_sfp_init(test);
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ ctx->issue_result = MBX_NOT_FINISHED;
+ KUNIT_EXPECT_NE(test, lpfc_get_sfp_info_wait(&ctx->phba, rdp), 0);
+ lpfc_mbox_test_expect_sfp_released(test);
+}
+
+static void lpfc_mbox_sfp_a2_issue_failure(struct kunit *test)
+{
+ struct lpfc_rdp_context *rdp = lpfc_mbox_test_sfp_init(test);
+ struct lpfc_mbox_test *ctx = test->priv;
+
+ ctx->good_issues = 1;
+ ctx->issue_result = MBX_NOT_FINISHED;
+ KUNIT_EXPECT_NE(test, lpfc_get_sfp_info_wait(&ctx->phba, rdp), 0);
+ lpfc_mbox_test_expect_sfp_released(test);
+}
+
+static void lpfc_mbox_test_sfp_a0_timeout(struct kunit *test, bool stale)
+{
+ struct lpfc_rdp_context *rdp = lpfc_mbox_test_sfp_init(test);
+ struct lpfc_mbox_test *ctx = test->priv;
+ int rc;
+
+ ctx->mode = LPFC_MBOX_TEST_EXPIRE;
+ rc = lpfc_get_sfp_info_wait(&ctx->phba, rdp);
+ KUNIT_ASSERT_EQ(test, rc, MBX_TIMEOUT);
+ lpfc_mbox_test_expect_caller_owned(test);
+ KUNIT_EXPECT_EQ(test, ctx->phba.lpfc_mbuf_safety_pool.current_count,
+ 0U);
+
+ /* The late completion now owns and releases the mailbox and mbuf. */
+ if (stale)
+ ctx->captured_cmpl(&ctx->phba, &ctx->storage);
+ else
+ ctx->storage.mbox_cmpl(&ctx->phba, &ctx->storage);
+ lpfc_mbox_test_expect_sfp_released(test);
+}
+
+static void lpfc_mbox_sfp_a0_timeout(struct kunit *test)
+{
+ lpfc_mbox_test_sfp_a0_timeout(test, false);
+}
+
+static void lpfc_mbox_sfp_a0_timeout_stale_callback(struct kunit *test)
+{
+ lpfc_mbox_test_sfp_a0_timeout(test, true);
+}
+
+static struct kunit_case lpfc_mbox_test_cases[] = {
+ KUNIT_CASE(lpfc_mbox_completion_before_timeout),
+ KUNIT_CASE(lpfc_mbox_busy_completion_before_timeout),
+ KUNIT_CASE(lpfc_mbox_timeout_then_current_callback),
+ KUNIT_CASE(lpfc_mbox_timeout_then_stale_callback),
+ KUNIT_CASE(lpfc_mbox_immediate_issue_failure),
+ KUNIT_CASE(lpfc_mbox_sfp_success),
+ KUNIT_CASE(lpfc_mbox_sfp_a0_issue_failure),
+ KUNIT_CASE(lpfc_mbox_sfp_a2_issue_failure),
+ KUNIT_CASE(lpfc_mbox_sfp_a0_timeout),
+ KUNIT_CASE(lpfc_mbox_sfp_a0_timeout_stale_callback),
+ {}
+};
+
+static struct kunit_suite lpfc_mbox_test_suite = {
+ .name = "lpfc_mbox",
+ .init = lpfc_mbox_test_init,
+ .exit = lpfc_mbox_test_exit,
+ .test_cases = lpfc_mbox_test_cases,
+};
+
+kunit_test_suite(lpfc_mbox_test_suite);
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread