Skip to content

[26.04_linux-nvidia] veth: set the real number of queues before registering the devices - #696 - #697

Open
JiandiAnNVIDIA wants to merge 2 commits into
NVIDIA:26.04_linux-nvidiafrom
JiandiAnNVIDIA:veth-init-queues-nvidia
Open

JiandiAnNVIDIA wants to merge 2 commits into
NVIDIA:26.04_linux-nvidiafrom
JiandiAnNVIDIA:veth-init-queues-nvidia

Conversation

@JiandiAnNVIDIA

@JiandiAnNVIDIA JiandiAnNVIDIA commented Nov 7, 2026 •

Copy link
Copy Markdown

Problem

Container churn on NVIDIA Vera is roughly 5x slower with udev running than with it
stopped. On a 352-CPU NVL72 node, throughput is capped at 3,100-3,850 container
cycles/h from 8 concurrent containers upward and does not improve with more
load; stopping systemd-udevd raises it to 14,000-19,500. Across the loaded
concurrency levels the penalty is +276% to +514%. Kernel CPU sits at 19.9-26.0%
while user CPU is ~1%, so the machine is mostly idle while delivering a fifth
of its achievable throughput.

Root cause

Docker's default bridge networking creates a veth pair per container, and its
netlink library sets neither IFLA_NUM_TX_QUEUES nor IFLA_NUM_RX_QUEUES. In
that case veth allocates one rx and one tx queue per possible CPU and then
reduces the real count to one — but veth_newlink() does the reduction after
register_netdevice():

	err = register_netdevice(dev);
	...
	err = veth_init_queues(dev, tb);

register_queue_kobjects() publishes sysfs objects for real_num_rx_queues and
real_num_tx_queues, so every queue it just created is destroyed again a moment
later, each creation and destruction emitting a uevent. On this node that is
2816 uevents on the queues subsystem per veth pair — 704 adds plus 702
immediate removals per device, times two devices, plus 2 removals at delete.
udev's 720 workers then contend on the per-kernfs-root semaphores while
processing them, which throttles all subsequent sysfs activity on the host.

The cost scales with num_possible_cpus(), which is why this node is hit harder
than the 176-CPU node in the original report.

Fix

Set the real queue counts before registering each device. For a device that
is not yet registered, netif_set_real_num_tx_queues() and
netif_set_real_num_rx_queues() only assign the counter — they skip all kobject
work and the expedited RCU grace period the tx path otherwise takes — so the
extra queues never appear in sysfs at all, and the error label that unwound a
registration is no longer needed.

Two commits:

Commit Upstream status
veth: decide peer queue count from the peer's own attributes merged, 267bc3f113d8c441f57014c502443686f5d33d57, in v7.4-rc1
NVIDIA: SAUCE: veth: set the real number of queues before registering the devices posted to netdev 2026-10-16, reviewed and tested, repost after the merge window requested — carried as SAUCE until it lands

Both are context-adjusted for this tree, which predates the netdev_hold() peer
tracker in veth_newlink() and therefore has one error label where upstream has
two.

Verification

Measured on a Vera NVL72 node: 2 sockets x 88 cores x 2 threads = 352 logical
CPUs, 64k pages, Ubuntu 26.04, udev 259.5-0ubuntu3.4 with the default 720
workers, Docker 29.7.2 on the default bridge.

Uevents per veth pair

before after
queues adds 1408 4
queues removes 1408 4

A 352x reduction, exactly num_possible_cpus(). No removals occur during
creation at all — the storm does not happen rather than being made cheaper.

200 veth pairs created and deleted, no Docker

phase before after
create 5.44 s 0.64 s
create drain (udevadm settle) 107.01 s 0.01 s
delete 4.43 s 4.37 s
total 116.89 s 5.03 s

Deletion is unaffected, as upstream's own numbers also show; this fixes
creation.

Container churn, cycles/h, two runs per kernel

containers stock on stock off patched on patched off
8 3,309–3,839 14,360–14,449 15,381–15,612 15,373–15,507
16 3,304–3,560 17,882–18,621 18,471–18,670 19,713–20,310
32 3,185–3,553 19,033–19,550 12,863–15,099 14,739–15,025
64 3,099–3,578 17,827–18,663 14,077–14,521 14,541–14,803
128 3,558–3,747 17,088–18,026 12,947–13,379 13,776–13,874

The udev penalty goes from +276%..+514% to −9%..+6%, straddling zero in both
patched runs. Kernel CPU with udev running drops from 19.9–26.0% to 0.6–7.9%.
Mean container cycle time at 32 concurrent containers goes from 30.75–33.01 s
to 7.52–7.81 s.

Profile — udev workers account for 0.96% of cycles on the patched kernel,
and rwsem_read_trylock plus up_read together total ~0.45% across all
processes.

No functional change — ethtool -l on a freshly created veth still reports
pre-set maximums of 352 RX and 352 TX with current settings of 1, so
num_tx_queues remains nr_possible_cpus() and ethtool -L headroom is
preserved. Container networking verified working.

LP: https://bugs.launchpad.net/ubuntu/+source/linux-nvidia-bos/+bug/2173592
NvBug: https://nvbugspro.nvidia.com/bug/6962526

@nirmoy nirmoy added the help wanted Extra attention is needed label Nov 7, 2026
@nirmoy

nirmoy commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

BaseOS Kernel Review

Tip

✅ Review passed

No issues found across the reviewed commits.

Findings: none

🔍 Review artifacts

📦 Build checks — 🟢 4/4 passed

Install the PR kernel with APT

Published for reviewed head 30af1de94314: noble/amd64. Requires the NVIDIA network.

curl -fsS https://baseos-reviewer-66d7b9.gitlab-master-pages.nvidia.com/install.sh | bash -s -- install pr-697

Manual setup · signing fingerprint DF52E10C47F28AC221DA19ED6D25A5DFC6686DFC

Manual commands for Ubuntu 24.04 Noble / amd64, on the NVIDIA network:

(
set -euo pipefail
work=$(mktemp -d)
trap 'rm -rf -- "$work"' EXIT
curl -qfsS --proto '=https' -o "$work/archive-keyring.gpg" https://baseos-reviewer-66d7b9.gitlab-master-pages.nvidia.com/archive-keyring.gpg
curl -qfsS --proto '=https' -o "$work/baseos-internal-tools.crt" https://baseos-reviewer-66d7b9.gitlab-master-pages.nvidia.com/baseos-internal-tools.crt
sudo install -d -m 0755 /etc/apt/keyrings
sudo install -m 0644 "$work/archive-keyring.gpg" /usr/share/keyrings/baseos-pr-kernels.gpg
sudo install -m 0644 "$work/baseos-internal-tools.crt" /etc/apt/keyrings/baseos-pr-https.crt
printf '%s\n' \
  'Acquire::https::baseos-internal-tools.nvidia.com::CaInfo "/etc/apt/keyrings/baseos-pr-https.crt";' \
  'Acquire::https::baseos-internal-tools.nvidia.com::Verify-Peer "true";' \
  'Acquire::https::baseos-internal-tools.nvidia.com::Verify-Host "true";' | \
  sudo tee /etc/apt/apt.conf.d/90baseos-pr-kernels >/dev/null
printf '%s\n' 'deb [arch=amd64 signed-by=/usr/share/keyrings/baseos-pr-kernels.gpg] https://baseos-internal-tools.nvidia.com:4001/reviews/apt/NVIDIA/NV-Kernels/697/noble/amd64 noble main' | \
  sudo tee /etc/apt/sources.list.d/nv-kernels-pr697.list >/dev/null
sudo apt update --error-on=any
sudo apt install --no-remove nv-kernels-pr-697
)

The repository remains configured. Later apt update and apt install nv-kernels-pr-697 pick up newly published builds; APT verifies signed metadata with the shown Signed-By key.

Note

Build reports and debs are retained for 10 days after the PR closes.

  • 🟢 PR Illustrated: ready
Review metadata
  • Reviewed head: 30af1de94314
  • Overall status: kernel validation inconclusive
  • Build checks: 4/4 passed

This comment is maintained by BaseOS Reviewer and updated when the GitHub watcher publishes a newer review.

@github-actions

github-actions Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

PR Validation Report

Patchscan ✅ No Missing Fixes

All cherry-picked commits checked — no missing upstream fixes found.

PR Lint ❌ Errors found

Details
Checking 2 commits...

Cherry-pick digest:
E: 67f8106eba0a ("veth: decide peer queue count from the p"): patch-ID mismatch with upstream 267bc3f113d8
┌──────────────┬──────────────────────────────────────────────────────────────────┬────────────┬─────────┬───────────────────────────┐
│ Local        │ Referenced upstream / Patch subject                              │ Patch-ID   │ Subject │ SoB chain                 │
├──────────────┼──────────────────────────────────────────────────────────────────┼────────────┼─────────┼───────────────────────────┤
│ 30af1de94314 │ veth: set the real number of queues before registering the devic │ noted      │ found   │ ok, backporter: jan       │
├──────────────┼──────────────────────────────────────────────────────────────────┼────────────┼─────────┼───────────────────────────┤
│ 67f8106eba0a │ 267bc3f113d8 veth: decide peer queue count from the peer's own a │ MISMATCH   │ match   │ preserved + jan added     │
└──────────────┴──────────────────────────────────────────────────────────────────┴────────────┴─────────┴───────────────────────────┘

Lint: all checks passed.

lrq-max and others added 2 commits November 7, 2026 01:02
veth_newlink() creates the peer netdev from tbp, which points at the
nested VETH_INFO_PEER attributes when they are supplied (peer_tb) and
only aliases the primary device's tb otherwise. However the queue
reduction is gated on the primary device's attributes:

	err = veth_init_queues(peer, tb);

veth_init_queues() checks !tb[IFLA_NUM_{TX,RX}_QUEUES], so for the peer
it inspects the wrong attribute set whenever nested peer attributes are
used:

 - If the user requests IFLA_NUM_TX/RX_QUEUES only for the peer, tb
   carries no such attribute and veth_init_queues() still shrinks the
   peer's real_num_{tx,rx}_queues back to 1, silently dropping the
   user's explicit multi-queue request for the peer.

 - If the user requests the counts only for the primary device, the
   peer keeps nr_possible_cpus real queues instead of the legacy
   default of 1.

Pass tbp so the peer's queue count is decided from the peer's own
attributes. When no nested peer attributes are given tbp aliases tb, so
the common case is unchanged.

Cc: stable+noautosel@kernel.org # never worked, high regression risk
Fixes: 9d3684c ("veth: create by default nr_possible_cpus queues")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Link: https://patch.msgid.link/20261014013620.2497-1-lirongqing@baidu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(backported from commit 267bc3f)
[jan: context has no netdev_hold() peer tracker in this tree]
Signed-off-by: Jiandi An <jan@nvidia.com>
… the devices

Since commit 9d3684c ("veth: create by default nr_possible_cpus
queues"), each veth device is allocated with one rx and one tx queue
per possible CPU, and veth_newlink() reduces the real number of queues
to one unless the user asked for a number.

Both devices are already registered at that point, so
netif_set_real_num_tx_queues() and netif_set_real_num_rx_queues()
remove the sysfs objects of every extra queue that register_netdevice()
has just created, each one emitting an add and a remove uevent, and the
tx side also waits for an expedited RCU grace period. This happens with
RTNL held, twice per pair, and grows with the number of possible CPUs:
with 240 of them a veth pair emits about 1900 uevents, and udev
processing them slows down later sysfs work such as deleting devices.

Set the number of queues before registering each device. For a device
that is not registered yet this only updates the counters, so the extra
queues never appear in sysfs, and the error paths that undid a
registration are no longer needed.

Creating and then deleting 500 veth pairs, one ip -batch each, in a 16
vCPU guest, median of 3 runs, per pair:

                                   before      after
  16 possible CPUs    create      1644 us     520 us
                      delete     11822 us    9750 us
  240 possible CPUs   create     17253 us    1339 us
                      delete     17082 us    9941 us
                      uevents        1918          6

Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
Reviewed-by: Wang Zhan <wang.zhan@smartx.com>
Tested-by: Wang Zhan <wang.zhan@smartx.com>
(backported from https://lore.kernel.org/netdev/20261016-bug-veth-init-queues-before-register-v1-1-f48eb2bb25a0@gmail.com/)
[jan: no netdev_hold() peer tracker in this tree, so one error label; peer call keeps tbp]
Signed-off-by: Jiandi An <jan@nvidia.com>
@nirmoy

nirmoy commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Acked-by: Nirmoy Das <nirmoyd@nvidia.com>

@jamieNguyenNVIDIA

Copy link
Copy Markdown
Collaborator

Acked-by: Jamie Nguyen <jamien@nvidia.com>

@nirmoy nirmoy added has_2_acks and removed help wanted Extra attention is needed has_1_ack labels Nov 9, 2026
@clsotog

clsotog commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Acked-by: Carol L Soto <csoto@nvidia.com>

@nvmochs

nvmochs commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Acked-by: Matthew R. Ochs <mochs@nvidia.com>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants