[PATCH v1 1/1] nvme-pci: adaptive interrupt coalescing

Andy Shevchenko andriy.shevchenko at intel.com
Tue Jul 21 03:07:07 PDT 2026


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 at bytedance.com>
> 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





More information about the Linux-nvme mailing list