From: Naman Jain <namjain@linux.microsoft.com>
To: Andrew Morton <akpm@linux-foundation.org>,
Thomas Gleixner <tglx@kernel.org>,
Ming Lei <tom.leiming@gmail.com>, Ming Lei <ming.lei@redhat.com>
Cc: Wangyang Guo <wangyang.guo@intel.com>,
Tianyou Li <tianyou.li@intel.com>,
Tim Chen <tim.c.chen@linux.intel.com>,
Long Li <longli@microsoft.com>,
linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org,
Michael Kelley <mhklinux@outlook.com>
Subject: Re: [PATCH v2] lib/group_cpus: rotate extra groups to avoid IRQ stacking
Date: Mon, 10 Aug 2026 14:17:08 +0530 [thread overview]
Message-ID: <6bb5bdd9-dbfb-4ac1-8f4a-c21fdadceec0@linux.microsoft.com> (raw)
In-Reply-To: <20260810062144.2108758-1-namjain@linux.microsoft.com>
On 8/10/2026 11:51 AM, Naman Jain wrote:
> group_cpus_evenly() computes how a device's queue interrupts are spread
> across CPUs. It backs managed-interrupt affinity (kernel/irq/affinity.c)
> and block-multiqueue mappings (block/blk-mq-cpumap.c), and is invoked
> independently by every device that uses them - NVMe, NICs, storage HBAs,
> and virtio devices. Its output is deterministic, i.e. for a given
> topology, two similar devices produce an identical group-to-CPU mapping.
>
> When ngroups < ncpus, some groups end up with only a single CPU. An
> interrupt whose mask has one CPU can only run there, making that CPU a
> "hot" handler. Because the mapping is deterministic, identical devices
> compute the same layout and stack all their single-CPU IRQs onto the
> very same CPUs, leaving the rest of the system idle.
>
> This is easy to hit in practice. On an Azure L96as_v4 VM (96 vCPUs, 2
> NUMA nodes of 48 CPUs, 6 NVMe disks with 62 I/O queues each),
> group_cpus_evenly() splits each disk's 62 queues into 31 per node over
> 48 CPUs. 48 does not divide evenly by 31:
>
> per NUMA node: 48 CPUs / 31 queues
> 17 groups get 2 CPUs (cover 34 CPUs)
> 14 groups get 1 CPU (cover 14 CPUs) <- single-CPU "hot" queues
>
> That is 14 hot queues per node, 28 per disk. All 6 disks land them on
> the same 28 CPUs, so 168 hot interrupts pile onto 28 of 96 CPUs while
> two-thirds of the system handles none:
>
> Before (per-CPU, disks whose IRQs it services):
> CPU 0: 3 disks ... CPU 34: 6 disks (all six)
> CPU 1: 3 disks ... CPU 47: 6 disks (all six)
> Summary: 28 CPUs (34-47, 82-95) served all 6 disks and the other 68
> served only 3. Those 28 CPUs cap throughput and inflate tail
> latency while most of the system is idle.
>
> Fix this by introducing a per-caller rotation via a static atomic
> counter (group_spread_cnt). Each call to group_cpus_evenly() takes a
> unique spread_offset, applied to the two decisions that were previously
> deterministic:
>
> 1) Cluster-level rotation in __try_group_cluster_cpus(): after
> alloc_groups_to_nodes() distributes groups proportionally across
> clusters, integer rounding leaves some clusters with one extra
> group. The extras are redistributed starting from a rotated
> position, with a stride of ncluster/total_extra to minimize overlap
> between consecutive callers. A multi-pass fallback ensures all
> extras are placed even when some clusters are at capacity.
>
> 2) Intra-cluster rotation in assign_cpus_to_groups(): the sequential
> extra assignment is replaced with a modular expression,
> (v + spread_offset) % nv->ngroups < extra_grps
> rotating which groups within a cluster receive the extra CPU.
>
> Nothing else about the layout changes - same queue count, same NUMA
> weighting, same full CPU coverage and locality. Each caller simply
> starts its mapping from a different point, and each individual call
> still produces a valid, fair distribution. Across callers, different
> CPUs absorb the single-CPU group IRQ load:
>
> After (same setup, with the rotation):
> CPU 0: 4 disks CPU 2: 4 disks CPU 47: 4 disks
> CPU 1: 4 disks CPU 3: 4 disks ...
> Summary: no CPU serves more than 4 disks, and all 96 CPUs are used.
>
> The total interrupt work is unchanged - every CPU still handles one
> queue per disk; only the placement of the single-CPU hot queues moves.
> This benefits every managed-IRQ, blk-mq, and virtio-vdpa / virtio-fs
> device with no driver changes.
>
> Because the offset comes from a global counter advanced once per call,
> the mapping now depends on call (device probe) order. A given device's
> exact layout can differ from one boot to the next, and a later recompute
> (e.g. a blk-mq remap) may pick a different layout. Every such layout is
> still valid, fair, and proportional - only the choice among equally good
> mappings varies.
>
> On a 96-vCPU Hyper-V VM running 4K random-read fio across 6 NVMe disks,
> worst-disk degradation versus average dropped from 11% to 5%, and the
> previously penalized disks gained 12% IOPS at 10% lower latency.
>
> Fixes: 89802ca36c96 ("lib/group_cpus: make group CPU cluster aware")
> Co-developed-by: Long Li <longli@microsoft.com>
> Signed-off-by: Long Li <longli@microsoft.com>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
Sashiko pointed to a minor issue in this patch, which can be addressed
in the next version. It was not seen when I ran Sashiko locally.
I would also want to add CC: stable tag and stable list in the next version.
But I will wait for any reviews on this patch before sending the next
version.
Regards,
Naman
Link:
https://sashiko.dev/#/patchset/20260810062144.2108758-1-namjain%40linux.microsoft.com
prev parent reply other threads:[~2026-08-10 8:47 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 6:21 [PATCH v2] lib/group_cpus: rotate extra groups to avoid IRQ stacking Naman Jain
2026-08-10 8:47 ` Naman Jain [this message]
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=6bb5bdd9-dbfb-4ac1-8f4a-c21fdadceec0@linux.microsoft.com \
--to=namjain@linux.microsoft.com \
--cc=akpm@linux-foundation.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=mhklinux@outlook.com \
--cc=ming.lei@redhat.com \
--cc=tglx@kernel.org \
--cc=tianyou.li@intel.com \
--cc=tim.c.chen@linux.intel.com \
--cc=tom.leiming@gmail.com \
--cc=wangyang.guo@intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox