Skip to content

netdev: add queue-get and queue-create with queue leasing - #1195

Open
aarcamp wants to merge 1 commit into
vishvananda:mainfrom
aarcamp:pr/ac/netdev-queue-leasing
Open

netdev: add queue-get and queue-create with queue leasing#1195
aarcamp wants to merge 1 commit into
vishvananda:mainfrom
aarcamp:pr/ac/netdev-queue-leasing

Conversation

@aarcamp

@aarcamp aarcamp commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Add the "netdev" generic netlink family with NetDevQueueGet (decodes the nested lease attribute) and NetDevQueueCreate (creates an rx queue on a virtual device and leases it to a real queue on a physical device).

Queue leasing lets a virtual netdev proxy a physical NIC rx queue so io_uring zero-copy and AF_XDP can address it by (ifindex, queue-id). The lease nest is encoded with NLA_F_NESTED and may carry an optional netns-id. Mirrors include/uapi/linux/netdev.h. Tests skip unless the kernel supports the commands.

Tests use netdevsim as a hardware-free lease source (it implements the kernel's queue management ops): TestNetDevQueueLeaseRoundTrip leases a netdevsim rx queue to a netkit peer and reads it back, asserting the decoded lease matches exactly. It requires root and is skipped when netdevsim or the queue commands are unavailable.

Also performed a manual test on arm64 by creating a netdevsim interface w/ 2 queues:

acam@lima-default:~/src/netlink-test$ ls /sys/bus/netdevsim/devices/
netdevsim42
acam@lima-default:~/src/netlink-test$ ip -d link show eni42np1
82: eni42np1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 86:6a:b5:58:5f:cb brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 addrgenmode eui64 numtxqueues 2 numrxqueues 2 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 portname p1 switchid 1fc6d81d4dd7db8f091e23cca9eda3a1895b4f91574ed222a24c5ae7f1ad62 parentbus netdevsim parentdev netdevsim42
acam@lima-default:~/src/netlink-test$

And then exercising the new APIs:

acam@lima-default:~/src/netlink-test$ sudo ./nltest eni42np1 0
created netkit pair: primary="nktest0" peer="nktest0p" (peer numrxqueues=4 => lease headroom)
virtual peer="nktest0p" ifindex=89  <-- lease -->  physical "eni42np1" ifindex=82 queue=0
queue-create OK: new virtual rx queue id=1
queue-get(phys 82, q0) OK: ifindex=82 id=0 type=0 napi=514
  lease -> virtual ifindex=89 queue{id=1 type=0} netns-id-set=false netns-id=0
SUCCESS: create -> lease -> read-back round trip verified (lease decoded from physical side)
acam@lima-default:~/src/netlink-test$

Test program source:

// Usage:
//   sudo ./nltest <phys-ifname> <phys-rx-queue-id>
package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netlink/nl"
)

func init() {
	// Surface the kernel's extack error strings (same text ynl prints).
	nl.EnableErrorMessageReporting = true
}

const (
	nkName     = "nktest0"
	nkPeerName = "nktest0p"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Fprintf(os.Stderr, "usage: %s <phys-ifname> <phys-rx-queue-id>\n", os.Args[0]         )
		os.Exit(2)
	}
	physName := os.Args[1]
	physQueue, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Fprintf(os.Stderr, "bad queue id %q: %v\n", os.Args[2], err)
		os.Exit(2)
	}

	// 1. Create a netkit PAIR. The kernel only permits leasing on the
	//    non-primary (peer) device of a pair ("netkit can only lease against
	//    the peer device"), so we give the PEER the rx-queue headroom and
	//    lease against it. The peer's NumRxQueues is carried in the
	//    IFLA_NETKIT_PEER_INFO nest (added to this library for queue leasing).
	netlink.LinkDel(&netlink.Netkit{LinkAttrs: netlink.LinkAttrs{Name: nkName}}) // best-effort clea  nup
	nk := &netlink.Netkit{
		LinkAttrs:  netlink.LinkAttrs{Name: nkName},
		Mode:       netlink.NETKIT_MODE_L3,
		Policy:     netlink.NETKIT_POLICY_FORWARD,
		PeerPolicy: netlink.NETKIT_POLICY_FORWARD,
	}
	nk.SetPeerAttrs(&netlink.LinkAttrs{Name: nkPeerName, NumRxQueues: 4})
	if err := netlink.LinkAdd(nk); err != nil {
		fmt.Fprintf(os.Stderr, "create netkit %q: %v\n", nkName, err)
		os.Exit(1)
	}
	defer netlink.LinkDel(nk)
	fmt.Printf("created netkit pair: primary=%q peer=%q (peer numrxqueues=4 => lease headroom)\n", n  kName, nkPeerName)

	// Lease against the PEER, not the primary.
	nkLink, err := netlink.LinkByName(nkPeerName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "lookup peer %q: %v\n", nkPeerName, err)
		os.Exit(1)
	}
	physLink, err := netlink.LinkByName(physName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "lookup phys %q: %v\n", physName, err)
		os.Exit(1)
	}
	nkIdx := nkLink.Attrs().Index
	physIdx := physLink.Attrs().Index
	fmt.Printf("virtual peer=%q ifindex=%d  <-- lease -->  physical %q ifindex=%d queue=%d\n",
		nkPeerName, nkIdx, physName, physIdx, physQueue)

	// 2. queue-create: make a new RX queue on the virtual device and lease it
	//    to the physical device's RX queue.
	newID, err := netlink.NetDevQueueCreate(netlink.NetDevQueueCreateRequest{
		IfIndex: nkIdx,
		Type:    netlink.NetDevQueueTypeRx,
		Lease: netlink.NetDevQueueLease{
			IfIndex: uint32(physIdx),
			Queue:   netlink.NetDevQueueID{ID: uint32(physQueue), Type: netlin                k.NetDevQueueTypeRx},
		},
	})
	if err != nil {
		fmt.Printf("queue-create FAILED: %v\n", err)
		fmt.Println("(on a VM without a queue-mgmt-capable NIC this is expected;")
		fmt.Println(" it still proves the nested lease attributes were encoded and")
		fmt.Println(" reached the kernel's lease-device validation.)")
		os.Exit(1)
	}
	fmt.Printf("queue-create OK: new virtual rx queue id=%d\n", newID)

	// 3. queue-get on the PHYSICAL device's leased queue: the kernel reports
	//    the lease pointing back to the virtual device (this is the direction
	//    shown in the kernel commit message and exercises our lease decoder).
	pq, err := netlink.NetDevQueueGet(physIdx, uint32(physQueue), netlink.NetDevQueueTypeRx)
	if err != nil {
		fmt.Printf("queue-get on physical queue FAILED: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("queue-get(phys %d, q%d) OK: ifindex=%d id=%d type=%d napi=%d\n",
		physIdx, physQueue, pq.IfIndex, pq.ID, pq.Type, pq.NapiID)
	if pq.Lease == nil {
		fmt.Println("WARNING: physical queue reports no lease info")
		os.Exit(1)
	}
	fmt.Printf("  lease -> virtual ifindex=%d queue{id=%d type=%d} netns-id-set=%v netns-id=%d\n",
		pq.Lease.IfIndex, pq.Lease.Queue.ID, pq.Lease.Queue.Type, pq.Lease.NetNSIDSet, pq         .Lease.NetNSID)
	if pq.Lease.IfIndex != uint32(nkIdx) {
		fmt.Printf("WARNING: lease points to ifindex %d, expected virtual peer %d\n", pq.         Lease.IfIndex, nkIdx)
		os.Exit(1)
	}
	fmt.Println("SUCCESS: create -> lease -> read-back round trip verified (lease decoded from physi  cal side)")
}

These changes will help Cilium take advantage of the zero-copy networking features added in Linux 7.1—see the upstream queue-leasing merge.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added Linux support for querying and creating network device queues.
    • Added RX and TX queue types, identities, leases, and creation requests.
    • Added optional network namespace targeting when creating queues.
    • Added clearer handling for interrupted requests, invalid namespace IDs, malformed responses, and missing queue data.
  • Tests
    • Added coverage for queue lease round trips, namespace targeting, and invalid namespace validation.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Linux netdev generic-netlink support for queue contracts, queue retrieval, queue creation, nested attribute parsing, and kernel integration tests for leases and network namespace IDs.

Changes

Linux Netdev Queue Management

Layer / File(s) Summary
UAPI constants and queue contracts
nl/netdev_linux.go, netdev_linux.go
Defines exported netdev UAPI constants and public queue, lease, identity, and creation-request types.
Generic-netlink plumbing and parsers
netdev_linux.go
Builds netdev requests, handles interrupted dumps and missing responses, and parses nested queue and lease attributes.
Queue get API
netdev_linux.go
Adds package-level and handle-level NetDevQueueGet methods that submit queue identity attributes and parse the first response.
Queue create API
netdev_linux.go
Adds NetDevQueueCreateRequest and creation methods that encode nested lease attributes, optional NetNSID values, validation, and created queue IDs.
Kernel integration tests
netdev_linux_test.go
Adds netdevsim setup and tests for lease round trips and invalid NetNSID handling returning ENONET.
Retry handle test update
handle_retry_linux_test.go
Creates the retry-enabled test handle with HandleOptions{RetryInterrupted: true}.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Handle
  participant GenericNetlink
  participant LinuxNetdev
  Caller->>Handle: NetDevQueueCreate(request)
  Handle->>GenericNetlink: send queue-create attributes
  GenericNetlink->>LinuxNetdev: execute netdev request
  LinuxNetdev-->>GenericNetlink: ACK and queue response
  GenericNetlink-->>Handle: return created queue ID
  Handle-->>Caller: return queue ID
Loading

Possibly related PRs

Suggested reviewers: borkmann

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding netdev queue-get and queue-create operations with queue leasing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@netdev_linux_test.go`:
- Line 68: The test uses fixed interface names ("nltestnk0" and "nltestnk1")
which can collide with stale interfaces and cause flaky LinkAdd failures; change
the constants to generate unique names per test (e.g., append a test-specific
suffix using t.Name(), testing.T.TempDir() hash, process id or time-based/random
suffix) so each run creates distinct interface names referenced where
"nltestnk0"/"nltestnk1" are used (search for the constants and LinkAdd calls in
netdev_linux_test.go, including the other occurrence around line 118) and ensure
cleanup still targets the generated names.
- Around line 44-47: The current checks call t.Skipf for any non-nil err (the
"queue-get on lo rx-0" block and the similar block at lines 101-105), which can
mask real encoding/decoding bugs; change these to only skip when the error
clearly indicates the queue/feature is genuinely absent (e.g. match the specific
expected sentinel or syscall error like ENOENT/ENOTSUP/ENXIO returned by the
netlink call), and otherwise call t.Fatalf or t.Errorf to fail the test; update
the two occurrences that use t.Skipf (the "queue-get on lo rx-0" check and the
later analogous check) to perform a targeted error-match then skip, else fail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6fcf3b7e-a293-4c06-85bd-80c0e4cc26a2

📥 Commits

Reviewing files that changed from the base of the PR and between 9c2aece and 6f8d9bb.

📒 Files selected for processing (3)
  • netdev_linux.go
  • netdev_linux_test.go
  • nl/netdev_linux.go

Comment thread netdev_linux_test.go Outdated
Comment thread netdev_linux_test.go Outdated
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from 6f8d9bb to 6edaf35 Compare June 11, 2026 12:29
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from 6edaf35 to 6f8d8fd Compare August 3, 2026 00:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@netdev_linux_test.go`:
- Around line 1-14: Update handle_retry_linux_test.go to stop calling
RetryInterrupted as a method; access it as the Handle.option boolean field when
configuring or asserting dumpHandle, so the netlink test package compiles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 80f9fa3d-4cc0-46ca-8138-34b2f09023eb

📥 Commits

Reviewing files that changed from the base of the PR and between 6edaf35 and 6f8d8fd.

📒 Files selected for processing (3)
  • netdev_linux.go
  • netdev_linux_test.go
  • nl/netdev_linux.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • nl/netdev_linux.go

Comment thread netdev_linux_test.go
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from 6f8d8fd to 3723edf Compare August 3, 2026 00:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@netdev_linux_test.go`:
- Around line 21-24: Update the setup error handling around GenlFamilyGet and
the other referenced test setup paths to skip only when the error explicitly
indicates the netdev generic-netlink family or feature is unavailable; call
t.Fatalf for all unexpected errors. For new_device ID collisions, retry setup
with a new ID rather than skipping, preserving skips only for genuinely
unavailable environments.

In `@netdev_linux.go`:
- Around line 178-232: Validate req.Lease.NetNSID in Handle.NetDevQueueCreate
before encoding NETDEV_A_LEASE_NETNS_ID; when NetNSIDSet is true, reject
negative values by returning a descriptive error. Preserve the existing
attribute encoding for non-negative IDs and the omission behavior when
NetNSIDSet is false.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 70535091-47db-46fe-b058-6e4de6ff4db0

📥 Commits

Reviewing files that changed from the base of the PR and between 6f8d8fd and 3723edf.

📒 Files selected for processing (4)
  • handle_retry_linux_test.go
  • netdev_linux.go
  • netdev_linux_test.go
  • nl/netdev_linux.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • nl/netdev_linux.go

Comment thread netdev_linux_test.go
Comment thread netdev_linux.go
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from 3723edf to a1c53b3 Compare August 3, 2026 14:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@netdev_linux.go`:
- Around line 84-152: Validate that each relevant attribute value has at least
four bytes before calling native.Uint32 in parseNetDevQueueID,
parseNetDevQueueLease, and parseNetDevQueue, covering all queue ID, type,
ifindex, netns ID, and NAPI ID fields. Return an appropriate Go error for short
values so malformed replies never reach the decoder or panic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 404d5e61-6040-441e-b8f2-35479cc830c1

📥 Commits

Reviewing files that changed from the base of the PR and between 3723edf and a1c53b3.

📒 Files selected for processing (4)
  • handle_retry_linux_test.go
  • netdev_linux.go
  • netdev_linux_test.go
  • nl/netdev_linux.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • handle_retry_linux_test.go
  • nl/netdev_linux.go

Comment thread netdev_linux.go
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from a1c53b3 to f24113e Compare August 3, 2026 14:38
Add the "netdev" generic netlink family with NetDevQueueGet (decodes the
nested lease attribute) and NetDevQueueCreate (creates an rx queue on a
virtual device and leases it to a real queue on a physical device).

Queue leasing lets a virtual netdev proxy a physical NIC rx queue so
io_uring zero-copy and AF_XDP can address it by (ifindex, queue-id). The
lease nest is encoded with NLA_F_NESTED and may carry an optional
netns-id.  Mirrors include/uapi/linux/netdev.h. Tests skip unless the
kernel supports the commands.

Tests use netdevsim as a hardware-free lease source (it implements the
kernel's queue management ops): TestNetDevQueueLeaseRoundTrip leases a
netdevsim rx queue to a netkit peer and reads it back, asserting the
decoded lease matches exactly. It requires root and is skipped when
netdevsim or the queue commands are unavailable.

Signed-off-by: Aaron Campbell <aaron@monkey.org>
@aarcamp
aarcamp force-pushed the pr/ac/netdev-queue-leasing branch from f24113e to 7f9257a Compare August 4, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant