From: Ranjan Kumar <ranjan.kumar@broadcom.com>
To: linux-scsi@vger.kernel.org, martin.petersen@oracle.com
Cc: sathya.prakash@broadcom.com, chandrakanth.patil@broadcom.com,
vishakhavc@google.com, ipylypiv@google.com,
Ranjan Kumar <ranjan.kumar@broadcom.com>,
Sashiko <sashiko-bot@kernel.org>
Subject: [PATCH v3 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep
Date: Fri, 24 Jul 2026 15:55:00 +0530 [thread overview]
Message-ID: <20260724102505.115136-6-ranjan.kumar@broadcom.com> (raw)
In-Reply-To: <20260724102505.115136-1-ranjan.kumar@broadcom.com>
Commit 24d7071d9645 ("scsi: mpi3mr: A performance fix") increased the
threaded IRQ poll sleep range from 2-20 us to 20-21 us to work around a
timer slack issue.
On kernels unaffected by the timer slack issue, the longer sleep interval
reduces reply queue processing efficiency and causes an approximately 7%
throughput regression on NVMe direct-attached RAID10 configurations.
Restore the IRQ poll sleep range to 2-20 us to recover the lost
throughput.
Additionally, resolve the following issues in the reply queue processing
and polling logic:
1. Add missing dma_rmb() memory barriers in the admin and operational
reply queue processing loops. This ensures that the descriptor
payload is only read after the phase bit check is complete, preventing
weakly ordered architectures from speculatively processing stale data.
2. Add bounds checking for `request_queue_id` in
mpi3mr_process_op_reply_q() to prevent out-of-bounds memory
corruption if the hardware provides an invalid queue ID.
3. Fix a TOCTOU race condition in mpi3mr_process_op_reply_q() by
implementing a double-check pattern with dma_rmb() and
atomic_add_unless() when exiting the loop. This prevents lost
interrupts if a descriptor arrives exactly as the thread drops the
in_use lock.
4. Replace a direct panic() call with a safe ioc_err() log and abort in
mpi3mr_process_op_reply_desc() when mpi3mr_get_reply_virt_addr()
returns NULL. This prevents a single malformed DMA reply address from
crashing the entire host OS.
Note: The unbounded busy-wait loop (usleep_range) in mpi3mr_isr_poll()
flagged by automated review is intentionally retained. This short sleep
polling mechanism is critical for batching completions and achieving the
target throughput on high-performance NVMe configurations.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=5
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=5
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 2 +-
drivers/scsi/mpi3mr/mpi3mr_fw.c | 56 ++++++++++++++++++++++++++++++---
drivers/scsi/mpi3mr/mpi3mr_os.c | 3 +-
3 files changed, 54 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 1f2f0951b560..1d11d7c69536 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -178,7 +178,7 @@ extern atomic64_t event_counter;
#define MPI3MR_DEFAULT_SDEV_QD 32
/* Definitions for Threaded IRQ poll*/
-#define MPI3MR_IRQ_POLL_SLEEP 20
+#define MPI3MR_IRQ_POLL_SLEEP 2
#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8
/* Definitions for the controller security status*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 434b66f7b502..9f7cee26ebcd 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -473,6 +473,12 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
return 0;
}
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
+
do {
if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
break;
@@ -493,6 +499,13 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
if ((le16_to_cpu(reply_desc->reply_flags) &
MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
break;
+
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
+
if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
writel(admin_reply_ci,
&mrioc->sysif_regs->admin_reply_queue_ci);
@@ -565,14 +578,33 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
if ((le16_to_cpu(reply_desc->reply_flags) &
MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
atomic_dec(&op_reply_q->in_use);
+ /* Check for a TOCTOU race condition */
+ dma_rmb();
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase) {
+ if (atomic_add_unless(&op_reply_q->in_use, 1, 1))
+ goto process_desc;
+ }
return 0;
}
+process_desc:
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
do {
if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
break;
req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1;
+
+ if (unlikely(req_q_idx >= mrioc->num_op_req_q)) {
+ ioc_err(mrioc, "Invalid request queue id %d\n", req_q_idx + 1);
+ break;
+ }
+
op_req_q = &mrioc->req_qinfo[req_q_idx];
WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci));
@@ -592,8 +624,23 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
if ((le16_to_cpu(reply_desc->reply_flags) &
- MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ atomic_dec(&op_reply_q->in_use);
+ /* Check for a TOCTOU race condition */
+ dma_rmb();
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase) {
+ /* Descriptor arrived, try to reclaim ownership */
+ if (atomic_add_unless(&op_reply_q->in_use, 1, 1))
+ continue;
+ }
break;
+ }
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
#ifndef CONFIG_PREEMPT_RT
/*
* Exit completion loop to avoid CPU lockup
@@ -743,11 +790,12 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
num_op_reply +=
mpi3mr_process_op_reply_q(mrioc,
intr_info->op_reply_q);
+ if (!atomic_read(&intr_info->op_reply_q->pend_ios))
+ break;
- usleep_range(MPI3MR_IRQ_POLL_SLEEP, MPI3MR_IRQ_POLL_SLEEP + 1);
+ usleep_range(MPI3MR_IRQ_POLL_SLEEP, 10 * MPI3MR_IRQ_POLL_SLEEP);
- } while (atomic_read(&intr_info->op_reply_q->pend_ios) &&
- (num_op_reply < mrioc->max_host_ios));
+ } while (num_op_reply < mrioc->max_host_ios);
intr_info->op_reply_q->enable_irq_poll = false;
enable_irq(intr_info->os_irq);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 7b86152922ba..39624fae9131 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -3426,8 +3426,7 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
scsi_reply = mpi3mr_get_reply_virt_addr(mrioc,
*reply_dma);
if (!scsi_reply) {
- panic("%s: scsi_reply is NULL, this shouldn't happen\n",
- mrioc->name);
+ ioc_err(mrioc, "scsi_reply is NULL, invalid reply_frame_address\n");
goto out;
}
host_tag = le16_to_cpu(scsi_reply->host_tag);
--
2.47.3
next prev parent reply other threads:[~2026-07-24 10:32 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 10:24 [PATCH v3 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 01/10] mpi3mr: Skip device shutdown during unload per controller configuration Ranjan Kumar
2026-07-24 10:59 ` sashiko-bot
2026-07-24 10:24 ` [PATCH v3 02/10] mpi3mr: Update MPI Headers to revision 41 Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 03/10] mpi3mr: Add early timestamp synchronization after driver load Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 04/10] mpi3mr: Fix NVMe page size caching for non-operational devices Ranjan Kumar
2026-07-24 10:44 ` sashiko-bot
2026-07-24 10:25 ` Ranjan Kumar [this message]
2026-07-24 10:50 ` [PATCH v3 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep sashiko-bot
2026-07-24 10:25 ` [PATCH v3 06/10] mpi3mr: Fix memory leak on operational queue creation failure Ranjan Kumar
2026-07-24 10:51 ` sashiko-bot
2026-07-24 10:25 ` [PATCH v3 07/10] mpi3mr: Fix firmware event reference leak during cleanup Ranjan Kumar
2026-07-24 11:02 ` sashiko-bot
2026-07-24 10:25 ` [PATCH v3 08/10] mpi3mr: Fix SAS port allocation and registration error handling Ranjan Kumar
2026-07-24 10:58 ` sashiko-bot
2026-07-24 10:25 ` [PATCH v3 09/10] mpi3mr: Fix SAS PHY cleanup in host addition error paths Ranjan Kumar
2026-07-24 10:25 ` [PATCH v3 10/10] mpi3mr: Driver version update to 8.18.0.8.50 Ranjan Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260724102505.115136-6-ranjan.kumar@broadcom.com \
--to=ranjan.kumar@broadcom.com \
--cc=chandrakanth.patil@broadcom.com \
--cc=ipylypiv@google.com \
--cc=linux-scsi@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=sashiko-bot@kernel.org \
--cc=sathya.prakash@broadcom.com \
--cc=vishakhavc@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.