Skip to content

Count GPUs without waking the one that is asleep - #9482

Open
VykosMolt wants to merge 2 commits into
omacom:quattrofrom
VykosMolt:fix/hybrid-gpu-detector-wakes-card
Open

VykosMolt wants to merge 2 commits into
omacom:quattrofrom
VykosMolt:fix/hybrid-gpu-detector-wakes-card

Conversation

@VykosMolt

@VykosMolt VykosMolt commented Aug 31, 2026 •

Copy link
Copy Markdown

Current Quattro already counts display controllers from cached sysfs IDs in b0182842b (#15039), including the no-lspci behavior this PR proposed. e1338693c (#15410) retains that behavior and speeds up the hardware-guard tests. This change is superseded upstream and awaits maintainer disposition.

Previous proposal:

Opening the menu can wake a suspended discrete GPU because the Hybrid GPU row calls omarchy-hw-hybrid-gpu, whose fallback counts lines from lspci. Reading PCI config space resumes the card. The fallback now counts display-class devices (0x03) through cached sysfs values, as the NVIDIA detector already does.

The old VGA|3D|Display pattern also counted Non-VGA unclassified device as a graphics card. A machine with one GPU and one such device could therefore be reported as hybrid. Counting by PCI class excludes it. The supergfxctl path, its timeout handling, and the fallback when supergfxd cannot answer are unchanged.

On an Arrow Lake iGPU + RTX 5070 Ti Mobile laptop, starting with the discrete card suspended:

          verdict   time      card afterwards
quattro   hybrid    2659 ms   active
patched   hybrid       6 ms   suspended

The test now supplies a fake PCI tree instead of a stubbed lspci, including an unclassified device alongside the GPUs. It retains the eight existing cases and adds checks that one GPU beside an unclassified device is not hybrid and that the detector never calls lspci. The converted test fails on unmodified quattro.

./test/shell passed 217 of 221 files. The four failures (config-test, runtime-smoke-test, snapper-test, and unowned-system-paths-test) also failed on unmodified quattro.

omarchy-hw-hybrid-gpu decides whether the machine has two graphics cards by
counting lines out of lspci. lspci reads PCI config space, and that resumes a
runtime-suspended GPU. So the detector wakes the discrete card in order to
answer a question about the discrete card, and this gate is the `when:` on the
Hybrid GPU row, which means the menu's guard batch pays it.

On this laptop, from a suspended card:

  quattro   2659 ms   card left active
  patched      6 ms   card left suspended

nvidia.lua already says why, in a comment above its own detectors: "lspci reads
PCI config space, which resumes a runtime-suspended GPU, and on a hybrid laptop
that wake alone outlasts Hyprland's 1.5s config reload budget."
omarchy-hw-nvidia reads sysfs for exactly this reason. This one now does too,
counting devices whose PCI class is 0x03, the display-controller class.

That also fixes a miscount. The pattern was `VGA|3D|Display`, which matches the
VGA inside "Non-VGA unclassified device" -- there is one of those on this
machine at 80:14.5, class 0x000000. A machine with a single graphics card and
one of those counted two and called itself hybrid, offering a Hybrid GPU row for
hardware it does not have.

The supergfxctl path is untouched, including its timeout handling and the
fallback to counting when supergfxd cannot answer.

The test drove the count through a stubbed lspci, which the sysfs read would
have made inert, so it now builds a fake PCI device tree instead. Every tree
carries an unclassified device beside the graphics cards, which is the miscount
above. All eight existing assertions still describe what they did before.
@sprajs

sprajs commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Cross-linking #11038: its current omarchy-hw-hybrid-gpu implementation is identical to this one apart from comments, including the sysfs 0x03* scan and bounded supergfxctl fallback. That PR also changes the NVIDIA GLX environment handling, which is a separate part of its scope.

The detector fix only needs to land once. Keeping this focused PR and rebasing #11038 onto it, or consolidating the shared detector/tests there, would avoid maintaining two copies of the same change.

@omarchybot omarchybot added bug Something isn't working verified Omarchy Triage has verified that this issue is ready for final review labels Sep 27, 2026
@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed at f012156 against quattro. Nothing found that needs changing, and nothing was pushed.

Reproduced on an Omarchy worker VM. A test of my own, separate from the one in this PR, runs both versions of omarchy-hw-hybrid-gpu without supergfxctl, against a machine with one display controller and one class 0x0000 device. quattro's detector calls lspci and reports hybrid. This head reports not-hybrid and never calls lspci. On the worker's real PCI tree both versions agree (not hybrid). The PR's converted hw-hybrid-gpu-test.sh passes on the head (10 of 10) and fails once quattro's detector is put back.

The wake itself was not observed. The VM has no device in D3cold, and the kernel only resumes a D3cold device for a config-space read, so an SMBus controller suspended in the VM stayed suspended under lspci, as expected. That part rests on the kernel source: a config read through sysfs resumes a D3cold device, while class is served from a cached value and touches no hardware. It also matches the measurement in the description and the trace on #11038.

Suite on the worker: ./test/cli passes. ./test/shell ran 2900 assertions with 2 failures, in ascii-test.sh and branding-about-animation-test.sh, and both fail the same way on this branch's base commit. Proportionate: the change is limited to the counting function and its test, and it follows the sysfs pattern omarchy-hw-nvidia already uses.

No second opinion ran. The configured reviewer (Codex Medium) answered a ping, but this run's review allowance was already spent, so this is one model's reading (Claude Opus 5.5). The PR is labelled verified. It stays without ready until a second review has run at this head, and then it waits on the maintainer.

@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 2/5

[High impact] Adds account management for AI agents with new shell and Python scripts.

Fix account switching, the launch/removal race, and the driver migration order before merging.

Findings

  1. P1 Expired logins become active ▶
  2. P1 Removal can erase starting sessions ▶
  3. P1 Driver check blocks kernel installation ▶
  4. P2 Old dictation invitation stops working ▶

Summary

The GPU fallback now counts cached PCI display classes instead of calling lspci. Its tests use a fake PCI tree and exclude unclassified devices.

The supplied comparison also includes broader account, update, desktop, and installation changes.

  • Automatic account switching can select expired sign-ins.
  • Account removal can race with a new session.
  • The YT6801 migration can block the later kernel installation.
  • The dictation upgrade needs to clean up its old copied invitation hook.

No scripts or runtime tests were executed during this review.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Run pending migrations] --> B[Check running YT6801 driver]
  B -->|Required alias missing| C[Exit with failure]
  C --> D[Migration queue stops]
  B -->|Cutover succeeds| E[Continue migrations]
  E --> F[Install replacement kernel]
Loading

Reviews (1) · Last reviewed commit: "Merge current quattro and validate cache..." · Reviewed by Greptile

record = read_json(usage_record_path(provider)) or {}
accounts = [
a for a in record.get("accounts") or []
if isinstance(a, dict) and a.get("id") and a.get("usageStatusText") != "Waiting for auth"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Expired logins become active

usage_by_account() excludes Waiting for auth but still accepts Sign-in expired. The Claude collector keeps cached limits after both tokens expire, including zeroed limits after a reset. When the active account reaches its threshold, automatic switching can select an account that requires a new login. New sessions then cannot sign in without user help.

Exclude expired sign-ins while keeping Limits paused accounts eligible.

Knowledge Base Used: Agent accounts and provider usage

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This finding concerns the shared upstream implementation. The tested fix is in #14656. Expired sign-ins are excluded while limits-paused accounts remain eligible. Independent source review and the final branch account, real mise dispatch, autoswitch, usage, panel, and CLI suites pass. Keeping this thread open while that corrective PR awaits integration.

Comment on lines +463 to +468
if home_in_use(provider, home):
raise AccountError(f"A running {PROVIDERS[provider]['name']} session uses {account['label']}. Quit it first, then remove the account.")
# Only the account's own directory goes. Its links point into the primary
# home, and rmtree removes a link without following it.
if home.resolve().parent == (accounts_root() / provider).resolve():
shutil.rmtree(home, ignore_errors=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Removal can erase starting sessions

remove() can delete an account while a session is starting. The launcher reads its home without taking Locked, then sets the provider environment and starts the CLI. If removal runs after that read but before the child starts, home_in_use() finds no matching process and deletes the selected home. The new session starts without its saved login.

Coordinate launching and removal with a shared lock or a session claim that exists before the home is returned.

Knowledge Base Used: Agent accounts and provider usage

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This finding concerns the shared upstream implementation. The tested fix is in #14656. Home selection and session claims now share the short registry lock; removal requires an exclusive claim retained through registry save and cleanup. Independent source review and the final branch account, real mise dispatch, autoswitch, usage, panel, and CLI suites pass. Keeping this thread open while that corrective PR awaits integration.

Comment thread migrations/1788279117.sh
Comment on lines +48 to +52
if ! aliases=$(modinfo -F alias dwmac-motorcomm) ||
! grep -Fxq 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*' <<< "$aliases"; then
echo "The running kernel does not provide YT6801 support in dwmac-motorcomm." >&2
echo "Reboot into the latest Omarchy kernel and rerun omarchy-migrate." >&2
exit 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Driver check blocks kernel installation

If an existing YT6801 machine's running kernel lacks the required dwmac-motorcomm alias, this migration exits with failure. omarchy-migrate stops there, so the later 1789325478.sh never installs linux-omarchy. The user is told to reboot into a replacement kernel that the stopped queue has not installed.

Install the replacement kernel before this check, and leave the driver cutover pending until the user has booted it.

Knowledge Base Used: Hardware, devices, and power

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This finding concerns the shared upstream implementation. The tested fix is in #14651. The earlier driver migration now stages the existing supported-kernel/boot repair while leaving the vendor driver and completion markers pending until reboot. All 45 driver, 19 kernel, and 117 CLI checks pass. Keeping this thread open while that corrective PR awaits integration.

Comment thread migrations/1791479273.sh
@@ -0,0 +1,9 @@
echo "Preserve Voxtype as the default for existing dictation users"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Old dictation invitation stops working

The upgrade leaves the old copied install-voxtype.hook in users' post-update.d directory. Removing the source hook does not remove that copy. If its invitation marker is absent, the next update still offers a Voxtype installation action that calls the deleted omarchy-voxtype-install. Clicking it opens a failed command instead of installing dictation.

Add a migration that removes the known old hook or updates its action to the new installer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This finding concerns the shared upstream implementation. The tested fix is in #14654. An idempotent migration removes only the byte-identical legacy copied invitation and preserves custom or linked hooks. All 15 dictation and 117 CLI checks pass. Keeping this thread open while that corrective PR awaits integration.

@omarchybot omarchybot removed the verified Omarchy Triage has verified that this issue is ready for final review label Oct 8, 2026
@VykosMolt

VykosMolt commented Oct 8, 2026 •

Copy link
Copy Markdown
Author

Merged current quattro in 70e07ff; all 10 detector checks pass. Kept this PR focused on counting cached PCI class metadata without lspci. The identical detector part of #11038 should land once; this branch can serve as the focused base for that broader environment change rather than keeping two implementations.

@omarchybot omarchybot removed the bug Something isn't working label Oct 8, 2026
@omarchybot

Copy link
Copy Markdown
Collaborator

Looked again at 70e07ff, the merge of current quattro. Against quattro this PR still changes only bin/omarchy-hw-hybrid-gpu and test/shell.d/hw-hybrid-gpu-test.sh, and both files are byte-identical to f012156, which was reproduced on a worker in the earlier review. Nothing was pushed, and nothing was run again at this head.

GitHub's page for this PR is comparing against a stale base. It reports 709 changed files, because it still diffs against quattro as of 5b91db5 (8 September), while the branch now merges quattro at 50d687a. The compare API shows the true change: 2 files, 2 commits ahead. That is why Greptile's four findings are about bin/omarchy-agent-account-state, migrations/1788279117.sh and migrations/1791479273.sh. Those are quattro's own changes, and none of them is in this PR.

The classification is unsettled. Claude Opus 5.5 classed it as a bug fix: a gate that wakes a suspended GPU and counts a non-GPU device as a GPU is broken behaviour. The second classifier, Codex Medium, classed it as an enhancement. It was given GitHub's PR diff, which is the 709-file one above, so its answer was most likely about quattro's changes rather than this one. That reason is my inference, not something it stated. Because the two disagree, the bug label is off, and the PR is not re-verified or reviewed at this head until the classification is settled.

Related: #11259 makes the same change to multiple_gpus() (counting sysfs class 0x03 instead of using lspci) as part of a broader sysfs refactor, and it was opened after this one. #8076 also edits test/shell.d/hw-hybrid-gpu-test.sh. #11038, cross-linked above, is closed.

Waiting on the maintainer to settle the classification. Refreshing this PR's base on GitHub would also give the next classifier the 2-file diff.

@VykosMolt

Copy link
Copy Markdown
Author

Refreshed GitHub's cached base metadata using the same quattro target. The head remains 70e07ff88c2b1e9a22954ad02e63227cfdb4cd4e; the PR now reports 2 changed files against c352b62d67456f56ca898d0e23effc1b20c25ca6, matching the scoped comparison. The unrelated 709-file comparison is gone.

Executed validation details: 10 detector checks pass; this is cached PCI-class counting coverage, with no physical hardware reproduction claimed. Classification and merge decisions remain with the maintainers.

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.

3 participants