Re: [PATCH v1 1/1] nvme-pci: adaptive interrupt coalescing
From: Andy Shevchenko
Date: Tue Jul 21 2026 - 06:10:29 EST
On Tue, Jul 21, 2026 at 04:37:39PM +0800, Fengnan Chang wrote:
> The patch is included below.
The above in this case can be moved to cover letter, in this form it's harder
for maintainers to apply this.
...
> From 1c84b5afc906e780e1dbd2ee186251f9de2716b5 Mon Sep 17 00:00:00 2001
> From: Fengnan Chang <changfengnan@xxxxxxxxxxxxx>
> Date: Tue, 21 Jul 2026 12:31:46 +0800
> Subject: [PATCH] nvme-pci: adaptively poll busy queues from threaded
> interrupts
>
> On systems with multiple fast NVMe devices, hard IRQ completion
> processing can become CPU-bound and cap 4K random-read throughput
> before the devices are saturated.
>
> Add adaptive polling for busy interrupt-driven queues. Track the SQ head
> reported by completions while the CQE is still owned by the host. After
> the hard IRQ reaps pending CQEs, wake the IRQ thread and mask the queue
> interrupt when the number of SQ entries not yet consumed reaches a high
> watermark. Poll in the IRQ thread until the queue drops below a low
> watermark or a bounded loop count is reached, then reenable the
> interrupt.
>
> Only enable adaptive polling when the controller has independent
> vectors, as masking a shared single vector could suppress unrelated
> queues. Expose the enable switch, watermarks, loop bound, and sleep
> interval as module parameters.
...
> +static irqreturn_t nvme_adaptive_poll(int irq, void *data)
> {
> struct nvme_queue *nvmeq = data;
> + unsigned int loops = 0;
Unneeded. See below how.
> + unsigned int max_loops = READ_ONCE(adaptive_poll_max_loops);
> + unsigned int sleep_min = READ_ONCE(adaptive_poll_sleep_min_us);
> + unsigned int sleep_max = READ_ONCE(adaptive_poll_sleep_max_us);
>
> - if (nvme_cqe_pending(nvmeq))
> - return IRQ_WAKE_THREAD;
> - return IRQ_NONE;
> + do {
> + if (nvme_cqe_pending(nvmeq))
> + nvme_irq(irq, data);
> + if (nvme_adaptive_poll_should_stop(nvmeq))
> + break;
> + usleep_range(sleep_min, sleep_max);
fsleep() ? This automatically makes the upper bound ~25% of the requested value.
Supplying max in this situation makes not much sense.
> + } while (++loops < max_loops);
} while (--max_loops);
> + enable_irq(irq);
> + return IRQ_HANDLED;
> +}
--
With Best Regards,
Andy Shevchenko