[PR #334] [MERGED] perf(setup): default ublk block devices to the none I/O scheduler #356

Closed
opened 2026-10-29 14:22:33 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/kvcache-ai/AgentENV/pull/334
Author: @odysseusy
Created: 10/1/2026
Status: ✅ Merged
Merged: 10/1/2026
Merged by: @yingdi-shan

Base: main ← Head: perf/ublk-scheduler-none


📝 Commits (1)

  • ad135f9 perf(setup): default ublk block devices to the none I/O scheduler

📊 Changes

3 files changed (+4 additions, -2 deletions)

View changed files

📝 docs/src/internals/sandbox-testing.md (+1 -1)
📝 scripts/tests/setup-ublk-access.sh (+1 -0)
📝 src/setup/ublk.rs (+2 -1)

📄 Description

What

Set queue/scheduler to none for every new /dev/ublkb* device. The change adds one line to the udev rule that server --setup-host installs (/etc/udev/rules.d/99-agentenv-ublk.rules), mirrors it in scripts/tests/setup-ublk-access.sh, and documents it in docs/src/internals/sandbox-testing.md.

Why

Single-queue ublk devices get mq-deadline by default. For a userspace target, the elevator only adds locking and dispatch cost. On Linux 7.2 and later it also sends every direct I/O through kblockd. Measurements and background are in the issue.

Closes #333

Scope and non-goals

Only the scheduler of ublk block devices changes. Other queue attributes and non-ublk devices are untouched.

Design and behavior changes

The daemon runs as an unprivileged user and can't write sysfs, so the setting lives in the udev rule that already grants device access. udev applies the attribute in the same event that sets the node's group. A daemon without CAP_DAC_OVERRIDE can't open the node before that event, so wait_for_ublk_dev() returns after the scheduler is already none.

Compatibility and operations

  • Public API or generated protocol: N/A
  • Configuration or defaults: the default ublk scheduler changes from mq-deadline to none. Operators can override it with a later-ordered udev rule.
  • Snapshot manifest, artifact layout, or storage format: N/A
  • Upgrade and rollback: takes effect for new devices after server --setup-host reinstalls the rule. Rolling back is removing the line.
  • Host requirements, permissions, ports, or dependencies: none new. Permissions are unchanged.

Validation

  • make fmt
  • make clippy
  • make test-unit
  • Relevant Rust integration tests
  • make -C services test (required when services/ changes)
  • Generated clients/server regenerated with the documented make target
  • Documentation updated
  • Benchmarks or performance comparison completed

Commands and results:

cargo fmt --all -- --check                                  ok
cargo clippy -p agentenv --all-targets -- -D warnings      ok

uvm-ublk create ... overlaybd (sparse 8 GiB upper; 1 queue, depth 16, 256 KiB)
fio --direct=1 --ioengine=io_uring --bs=4k, 2 s ramp + 15 s, median of 3

kernel    workload                mq-deadline   none
6.6       randread qd16, 1 dev    169K          304K   (+80%)
6.6       randread qd16, 4 devs   690K          1247K  (+81%)
6.6       randread qd1,  1 dev    32.3K         38.5K  (+19%)
6.6       randwrite qd16, 1 dev   108K          175K   (+63%)
7.3-rc5   randread qd16, 1 dev    107K          307K   (+186%)
7.3-rc5   randread qd16, 4 devs   434K          1232K  (+184%)
7.3-rc5   randread qd1,  1 dev    43.3K         54.1K  (+25%)
7.3-rc5   randwrite qd16, 1 dev   105K          181K   (+73%)

Rule check, production privilege model (non-root, CAP_SYS_ADMIN + CAP_NET_ADMIN):
10/10 devices read "[none] mq-deadline kyber bfq" when uvm-ublk printed "ready".

Skipped checks and reasons: the make targets cover the whole workspace including Go services; I ran the cargo equivalents scoped to the changed crate above. make test-unit and integration tests need a full host setup with Firecracker and KVM, and the change has no Rust logic beyond a string constant.

Risks and reviewer notes

If the daemon runs as root, it can open the node before udev has processed it, and the switch to none lands a few milliseconds later. That's harmless, because the kernel freezes the queue during the switch.

Checklist

  • The PR contains one coherent change and no unrelated formatting or refactoring.
  • New behavior is covered by tests, or I explained why testing is impractical.
  • Logs and examples contain no credentials, tokens, or private registry information.
  • I did not manually edit generated code without updating its source and regenerating it.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/kvcache-ai/AgentENV/pull/334 **Author:** [@odysseusy](https://github.com/odysseusy) **Created:** 10/1/2026 **Status:** ✅ Merged **Merged:** 10/1/2026 **Merged by:** [@yingdi-shan](https://github.com/yingdi-shan) **Base:** `main` ← **Head:** `perf/ublk-scheduler-none` --- ### 📝 Commits (1) - [`ad135f9`](https://github.com/kvcache-ai/AgentENV/commit/ad135f9f54aff7083d334b40015bd14660536c21) perf(setup): default ublk block devices to the none I/O scheduler ### 📊 Changes **3 files changed** (+4 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `docs/src/internals/sandbox-testing.md` (+1 -1) 📝 `scripts/tests/setup-ublk-access.sh` (+1 -0) 📝 `src/setup/ublk.rs` (+2 -1) </details> ### 📄 Description ## What Set `queue/scheduler` to `none` for every new `/dev/ublkb*` device. The change adds one line to the udev rule that `server --setup-host` installs (`/etc/udev/rules.d/99-agentenv-ublk.rules`), mirrors it in `scripts/tests/setup-ublk-access.sh`, and documents it in `docs/src/internals/sandbox-testing.md`. ## Why Single-queue ublk devices get `mq-deadline` by default. For a userspace target, the elevator only adds locking and dispatch cost. On Linux 7.2 and later it also sends every direct I/O through kblockd. Measurements and background are in the issue. ## Related issue Closes #333 ## Scope and non-goals Only the scheduler of ublk block devices changes. Other queue attributes and non-ublk devices are untouched. ## Design and behavior changes The daemon runs as an unprivileged user and can't write sysfs, so the setting lives in the udev rule that already grants device access. udev applies the attribute in the same event that sets the node's group. A daemon without CAP_DAC_OVERRIDE can't open the node before that event, so `wait_for_ublk_dev()` returns after the scheduler is already `none`. ## Compatibility and operations - Public API or generated protocol: N/A - Configuration or defaults: the default ublk scheduler changes from `mq-deadline` to `none`. Operators can override it with a later-ordered udev rule. - Snapshot manifest, artifact layout, or storage format: N/A - Upgrade and rollback: takes effect for new devices after `server --setup-host` reinstalls the rule. Rolling back is removing the line. - Host requirements, permissions, ports, or dependencies: none new. Permissions are unchanged. ## Validation - [ ] `make fmt` - [ ] `make clippy` - [ ] `make test-unit` - [ ] Relevant Rust integration tests - [ ] `make -C services test` (required when `services/` changes) - [ ] Generated clients/server regenerated with the documented `make` target - [x] Documentation updated - [x] Benchmarks or performance comparison completed Commands and results: ```text cargo fmt --all -- --check ok cargo clippy -p agentenv --all-targets -- -D warnings ok uvm-ublk create ... overlaybd (sparse 8 GiB upper; 1 queue, depth 16, 256 KiB) fio --direct=1 --ioengine=io_uring --bs=4k, 2 s ramp + 15 s, median of 3 kernel workload mq-deadline none 6.6 randread qd16, 1 dev 169K 304K (+80%) 6.6 randread qd16, 4 devs 690K 1247K (+81%) 6.6 randread qd1, 1 dev 32.3K 38.5K (+19%) 6.6 randwrite qd16, 1 dev 108K 175K (+63%) 7.3-rc5 randread qd16, 1 dev 107K 307K (+186%) 7.3-rc5 randread qd16, 4 devs 434K 1232K (+184%) 7.3-rc5 randread qd1, 1 dev 43.3K 54.1K (+25%) 7.3-rc5 randwrite qd16, 1 dev 105K 181K (+73%) Rule check, production privilege model (non-root, CAP_SYS_ADMIN + CAP_NET_ADMIN): 10/10 devices read "[none] mq-deadline kyber bfq" when uvm-ublk printed "ready". ``` Skipped checks and reasons: the `make` targets cover the whole workspace including Go services; I ran the cargo equivalents scoped to the changed crate above. `make test-unit` and integration tests need a full host setup with Firecracker and KVM, and the change has no Rust logic beyond a string constant. ## Risks and reviewer notes If the daemon runs as root, it can open the node before udev has processed it, and the switch to `none` lands a few milliseconds later. That's harmless, because the kernel freezes the queue during the switch. ## Checklist - [x] The PR contains one coherent change and no unrelated formatting or refactoring. - [x] New behavior is covered by tests, or I explained why testing is impractical. - [x] Logs and examples contain no credentials, tokens, or private registry information. - [x] I did not manually edit generated code without updating its source and regenerating it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-29 14:22:33 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kvcache-ai/AgentENV#356
No description provided.