Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ for finer detail.
## Unreleased

### Fixed
- The doctor's reverse-PRIME warning (#270, a black panel after login on a
hybrid-GPU laptop) now also fires when a forced or drifted GPU render pin
coexists with a stored hybrid or passthrough choice, which previously
silenced the check.
- Lock, suspend, lid-close and wake now share one fail-closed lifecycle across
Hyprland and niri. Lockscreen generations change atomically during updates,
every sleep waits for compositor-secure qylock, and output recovery retries
Expand Down
7 changes: 7 additions & 0 deletions ryoku/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
(`internal/doctor/reconcile_boot_rw.go`).

### Fixed
- **The doctor's reverse-PRIME guard (#270) now sees the pin the machine
actually runs.** The render-pin-vs-panel check resolved the pin through
`ryoku-gpu order`, the policy's recommendation, which refuses to speak
whenever a stored hybrid or passthrough choice opts out: a RYOKU_GPU_FORCE
pin kept under a hybrid stamp, or one drifted in by an older release, hid
from the guard while the panel sat on the iGPU under a NVIDIA-first pin.
The check now reads `order --effective`, the pin file's own value.
- **`ryoku update` no longer dies where taking a sleep inhibitor is denied.**
The transaction runs under `systemd-inhibit --mode=block`, which is
polkit-gated in sessions with no agent (SSH, a headless run): there it exits
Expand Down
8 changes: 6 additions & 2 deletions ryoku/cli/internal/doctor/reconcile_gpu_pin_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ var drmRoot = "/sys/class/drm"
// pinFirstDriver resolves the leftmost entry of the effective AQ_DRM_DEVICES
// pin to its kernel driver, "" when there is no pin or it cannot be resolved.
// The verdict comes from ryoku-gpu itself (the same tool the stale-pin
// reconciler trusts), so this never re-implements the pin policy.
// reconciler trusts), so this never re-implements the pin policy. --effective
// reads the pin the compositor would actually read, straight from the pin
// file: the plain order is the policy's recommendation and refuses to speak
// exactly when a stored hybrid/passthrough choice coexists with a pin: the
// drifted and forced-pin states the #270 guard most needs to see.
var pinFirstDriver = func() string {
out, err := sys.RunOut("ryoku-gpu", "order")
out, err := sys.RunOut("ryoku-gpu", "order", "--effective")
if err != nil {
return ""
}
Expand Down
6 changes: 6 additions & 0 deletions system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@
package.

### Fixed
- `hardware/gpu/ryoku-gpu`: `order --effective [PATH]` prints the AQ_DRM_DEVICES
value the compositor would actually read, straight from the pin file, while
the plain `order` keeps reporting the policy's recommendation (and keeps
exiting 1 under a stored hybrid/passthrough choice). doctor's reverse-PRIME
guard (#270) audits the machine as it is, and previously went blind exactly
when a forced or drifted pin coexisted with an opt-out mode stamp.
- `hardware/power/ryoku-power-cutover`: a killed generation guard releases
its locks. The hold's keep-alive coprocess inherited the launch and
generation flock fds, so a holder killed mid-swap left the locks pinned by
Expand Down
22 changes: 20 additions & 2 deletions system/hardware/gpu/ryoku-gpu
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,21 @@ cmd_detect() {
}

cmd_order() {
local dest="${1:-}"
# --effective: the AQ_DRM_DEVICES value the compositor would actually read
# at its next start, straight out of the pin file, or exit 1 when there is
# none. Deliberately indifferent to the stored mode choice: a forced pin
# kept under a hybrid stamp, or a pin drifted in by an older release, is
# exactly what the doctor's render-pin-vs-panel guard (#270) has to see,
# and the plain order (the policy's recommendation) refuses to speak in
# those states.
if [[ $dest == "--effective" ]]; then
local conf="${2:-$GPU_CONF_DEFAULT}" value
value="$(sed -n 's/^hl\.env("AQ_DRM_DEVICES", *"\([^"]*\)")$/\1/p' "$conf" 2>/dev/null | head -n1)"
[[ -n $value ]] || return 1
printf '%s\n' "$value"
return 0
fi
analyze
if ! beneficial; then
echo "ryoku-gpu: no discrete-GPU pin needed on this machine" >&2
Expand Down Expand Up @@ -457,7 +472,10 @@ Pin the strongest GPU as Hyprland's primary render device on multi-GPU machines.

Commands:
detect [--json] Show detected GPUs (strongest first) and the chosen primary.
order Print the recommended AQ_DRM_DEVICES value (exit 1 if none).
order [--effective [PATH]]
Print the recommended AQ_DRM_DEVICES value (exit 1 if none);
with --effective, print the pin file's own value instead
(what the compositor would read), exit 1 when unpinned.
persist [PATH] Write the Hyprland pin (Lua) to PATH, to ~/.config/hypr/gpu.lua
by default, or to stdout when PATH is '-'.
disable [PATH] Reset the pin file to a managed comment-only placeholder.
Expand All @@ -475,7 +493,7 @@ case "${1:-status}" in
detect)
if [[ "${2:-}" == "--json" ]]; then gpu_records_json; else cmd_detect; fi
;;
order) cmd_order ;;
order) cmd_order "${2:-}" ;;
persist) cmd_persist "${2:-}" ;;
install-udev) cmd_install_udev ;;
status) cmd_status ;;
Expand Down
17 changes: 17 additions & 0 deletions tests/gpu-pin-policy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,21 @@ egpu() { RYOKU_GPU_DRM_ROOT="$drme" RYOKU_GPU_DRI_DIR="$dri" RYOKU_GPU_CONF="$tm
egpu persist >/dev/null
[[ "$(egpu check-pin)" == "ok" ]] || fail "eGPU pin on a laptop must be ok, got $(egpu check-pin)"

# --- 10. order --effective sees a pin the policy order refuses to speak about:
# a RYOKU_GPU_FORCE pin kept under a stored hybrid choice is the drifted state
# behind #270, and doctor's render-pin-vs-panel guard must still see it.
run mode hybrid >/dev/null
RYOKU_GPU_FORCE=1 run persist >/dev/null # forced pin, hybrid stamp kept
grep -Eq '^hl\.env\("AQ_DRM_DEVICES"' "$conf" || fail "force wrote no pin under hybrid"
if run order >/dev/null 2>&1; then
fail "plain order must keep refusing under a stored hybrid choice"
fi
v="$(run order --effective)" \
&& [[ "$v" == *"ryoku-gpu-0000-02-00-0:"*"ryoku-gpu-0000-01-00-0" ]] \
|| fail "order --effective must print the pin file's value, got ${v:-<none>}"
run disable >/dev/null
if run order --effective >/dev/null 2>&1; then
fail "order --effective must exit 1 on an unpinned machine"
fi

echo "gpu-pin-policy: all cases passed"
Loading