From 32b94bfbd2ba12c2fd8bb262857c6f3dbb43bed8 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Thu, 12 Nov 2026 13:03:12 +0100 Subject: [PATCH] Set AMD X3D V-cache mode to cache via udev amd_x3d_mode defaulted to frequency, so the scheduler preferred the non-V-cache CCD. kuhy chose cache: games prefer CPUs 0-5,12-17; background jobs are already pinned to 6-11,18-23. A udev rule matches DRIVER amd_x3d_vcache on add (boot coldplug) and bind (any rebind), so it hard-codes no device name and survives a rebind, unlike a tmpfiles w line. fixes/fix_amd_x3d_vcache.sh installs it, fires a synthetic bind event and checks the mode ends on cache. Same layout as fix_webcam_c270.sh, with a bats test. Host: mode frequency -> cache, prefcore ranking now favours 0-5,12-17. vmbox has no X3D device: only the install step was proven there. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WzxdWYWDKuhbBAoNETfoB9 --- .../amd-x3d-vcache-mode-cache-2026-11-12.json | 11 +++ .../amd-x3d-vcache-mode-cache-2026-11-12.json | 38 ++++++++ .../fixes/60-amd-x3d-vcache-mode.rules | 12 +++ .../fixes/fix_amd_x3d_vcache.sh | 85 ++++++++++++++++++ .../tests/test_fix_amd_x3d_vcache.bats | 90 +++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 docs/superpowers/contracts/amd-x3d-vcache-mode-cache-2026-11-12.json create mode 100644 docs/superpowers/evidence/amd-x3d-vcache-mode-cache-2026-11-12.json create mode 100644 linux_configuration/fixes/60-amd-x3d-vcache-mode.rules create mode 100755 linux_configuration/fixes/fix_amd_x3d_vcache.sh create mode 100755 linux_configuration/tests/test_fix_amd_x3d_vcache.bats diff --git a/docs/superpowers/contracts/amd-x3d-vcache-mode-cache-2026-11-12.json b/docs/superpowers/contracts/amd-x3d-vcache-mode-cache-2026-11-12.json new file mode 100644 index 00000000..ebcf6cb1 --- /dev/null +++ b/docs/superpowers/contracts/amd-x3d-vcache-mode-cache-2026-11-12.json @@ -0,0 +1,11 @@ +{ + "title": "AMD X3D V-cache mode set to cache, persistently", + "objective": "amd_x3d_mode reads cache after boot and after a driver rebind, installed by a repo script using a driver-matching udev rule.", + "acceptance_criteria": [ + "amd_x3d_mode reads cache on the host after install", + "prefcore ranking of CPUs 0-5,12-17 exceeds 6-11,18-23", + "The rule hard-codes no device name and survives unbind/bind" + ], + "out_of_scope": ["Pinning background jobs", "Other CPU tuning"], + "verifier": "bats linux_configuration/tests/test_fix_amd_x3d_vcache.bats plus host sysfs reads" +} diff --git a/docs/superpowers/evidence/amd-x3d-vcache-mode-cache-2026-11-12.json b/docs/superpowers/evidence/amd-x3d-vcache-mode-cache-2026-11-12.json new file mode 100644 index 00000000..05a6d921 --- /dev/null +++ b/docs/superpowers/evidence/amd-x3d-vcache-mode-cache-2026-11-12.json @@ -0,0 +1,38 @@ +{ + "intent": "AMD X3D V-cache mode is cache on every boot and driver rebind, so the scheduler prefers the V-cache CCD for games.", + "scope": [ + "linux_configuration/fixes/60-amd-x3d-vcache-mode.rules", + "linux_configuration/fixes/fix_amd_x3d_vcache.sh", + "linux_configuration/tests/test_fix_amd_x3d_vcache.bats", + "Non-goal: CPU pinning of background jobs (already done)" + ], + "changes": [ + "udev rule matching DRIVER amd_x3d_vcache on add and bind, no device name", + "Installer with install, --remove and --status, same pattern as fix_webcam_c270.sh" + ], + "verification": [ + { + "command": "bats linux_configuration/tests/test_fix_amd_x3d_vcache.bats", + "result": "pass", + "evidence": "red while the script was missing; 8 ok after" + }, + { + "command": "fixes/fix_amd_x3d_vcache.sh on the host", + "result": "pass", + "evidence": "amd_x3d_mode frequency -> cache; prefcore ranking CPUs 0-5,12-17 now 196-216 vs 6-11,18-23 166-191 (before 166-191 vs 196-216); unbind/bind keeps cache" + }, + { + "command": "vm run atomichosts fix_amd_x3d_vcache.sh", + "result": "pass", + "evidence": "installs the rule, udevadm verify clean; no X3D device in vmbox so the mode itself is host-only" + } + ], + "risks": [ + "Games on the V-cache CCD may boost lower than on the frequency CCD", + "Background load spills onto the V-cache CCD if the pinning is lifted" + ], + "rollback": [ + "fixes/fix_amd_x3d_vcache.sh --remove, then write frequency to amd_x3d_mode or reboot", + "Check cat /sys/bus/platform/drivers/amd_x3d_vcache/*/amd_x3d_mode" + ] +} diff --git a/linux_configuration/fixes/60-amd-x3d-vcache-mode.rules b/linux_configuration/fixes/60-amd-x3d-vcache-mode.rules new file mode 100644 index 00000000..5ffc368d --- /dev/null +++ b/linux_configuration/fixes/60-amd-x3d-vcache-mode.rules @@ -0,0 +1,12 @@ +# AMD X3D (dual-CCD V-cache) CPU: prefer the V-cache CCD for games. +# amd_x3d_mode=cache makes the scheduler rank the V-cache CCD (CPUs 0-5,12-17) +# above the frequency CCD; the firmware default is "frequency". Background jobs +# are already pinned to the other CCD (6-11,18-23). +# +# Why udev and not tmpfiles.d: a tmpfiles `w` line needs the exact device path +# (a firmware-assigned ACPI name) and runs once at boot, so it +# misses a driver rebind or module reload. This rule matches the DRIVER, hard- +# codes no device name, and fires on "add" (boot coldplug, driver already +# bound) and on "bind" (every later driver (re)bind, e.g. modprobe -r/+). +# Installed by fixes/fix_amd_x3d_vcache.sh. +ACTION=="add|bind", SUBSYSTEM=="platform", DRIVER=="amd_x3d_vcache", ATTR{amd_x3d_mode}="cache" diff --git a/linux_configuration/fixes/fix_amd_x3d_vcache.sh b/linux_configuration/fixes/fix_amd_x3d_vcache.sh new file mode 100755 index 00000000..645a7d5a --- /dev/null +++ b/linux_configuration/fixes/fix_amd_x3d_vcache.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# ============================================================================ +# AMD X3D: set the V-cache scheduling mode to "cache" (udev rule) +# +# The scheduler then prefers the V-cache CCD for games. The rule re-applies the +# setting on every boot and driver rebind; rationale lives in +# 60-amd-x3d-vcache-mode.rules. +# +# fix_amd_x3d_vcache.sh install the rule and apply it now +# fix_amd_x3d_vcache.sh --remove remove the rule (firmware default returns on reboot) +# fix_amd_x3d_vcache.sh --status show rule state and the live mode +# ============================================================================ + +set -euo pipefail + +SCRIPT_NAME="$(basename "$0")" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME SCRIPT_DIR +readonly RULE_NAME="60-amd-x3d-vcache-mode.rules" +readonly RULE_SRC="$SCRIPT_DIR/$RULE_NAME" +# Both overridable so the tests can point them at a scratch tree. +readonly RULE_DST="${X3D_RULES_DIR:-/etc/udev/rules.d}/$RULE_NAME" +readonly DRIVER_SYSFS="${X3D_DRIVER_SYSFS:-/sys/bus/platform/drivers/amd_x3d_vcache}" + +log() { echo "[$SCRIPT_NAME] $*"; } + +# Live mode of every bound X3D device, one "=" per line. +live_modes() { + local f + for f in "$DRIVER_SYSFS"/*/amd_x3d_mode; do + [[ -e $f ]] || continue + echo "$(basename "$(dirname "$f")")=$(cat "$f")" + done +} + +install_rule() { + sudo install -m 644 "$RULE_SRC" "$RULE_DST" + sudo udevadm control --reload + log "installed $RULE_DST" + # No driver, or a driver with no device (a VM): nothing to apply yet. + if [[ -z "$(live_modes)" ]]; then + log "no X3D device; rule applies when the driver binds" + return + fi + # A synthetic bind event runs the rule against the live device by driver, + # so no device name is needed here either. + sudo udevadm trigger --action=bind --subsystem-match=platform \ + --property-match=DRIVER=amd_x3d_vcache + sudo udevadm settle --timeout=10 + local modes + modes="$(live_modes)" + if [[ -n $modes ]] && ! grep -qv '=cache$' <<<"$modes"; then + log "applied: $modes" + else + log "mode is not cache after trigger: ${modes:-no device}" + exit 1 + fi +} + +remove_rule() { + sudo rm -f "$RULE_DST" + sudo udevadm control --reload + log "removed $RULE_DST" +} + +show_status() { + if [[ -f $RULE_DST ]]; then + log "rule: installed" + else + log "rule: not installed" + fi + log "mode: $(live_modes | tr '\n' ' ')" +} + +case "${1:-}" in +"") install_rule ;; +--remove) remove_rule ;; +--status) show_status ;; +-h | --help) sed -n '4,12p' "$0" | sed 's/^# \{0,3\}//' ;; +*) + log "unknown option: $1" + exit 2 + ;; +esac diff --git a/linux_configuration/tests/test_fix_amd_x3d_vcache.bats b/linux_configuration/tests/test_fix_amd_x3d_vcache.bats new file mode 100755 index 00000000..4872d886 --- /dev/null +++ b/linux_configuration/tests/test_fix_amd_x3d_vcache.bats @@ -0,0 +1,90 @@ +#!/usr/bin/env bats +# Tests for fixes/fix_amd_x3d_vcache.sh against a fake driver sysfs tree and +# stubbed sudo/udevadm, so nothing touches /etc or the real X3D driver. +# +# Requires: bats (extra/bats). Run with: +# bats linux_configuration/tests/test_fix_amd_x3d_vcache.bats + +bats_require_minimum_version 1.5.0 + +setup() { + REPO_DIR="$(cd -- "$(dirname -- "$BATS_TEST_FILENAME")/.." && pwd)" + SCRIPT="$REPO_DIR/fixes/fix_amd_x3d_vcache.sh" + export X3D_DRIVER_SYSFS="$BATS_TEST_TMPDIR/drivers/amd_x3d_vcache" + export X3D_RULES_DIR="$BATS_TEST_TMPDIR/rules.d" + mkdir -p "$X3D_DRIVER_SYSFS/AMDI0101:00" "$X3D_RULES_DIR" "$BATS_TEST_TMPDIR/bin" + echo frequency >"$X3D_DRIVER_SYSFS/AMDI0101:00/amd_x3d_mode" + export CALLS="$BATS_TEST_TMPDIR/calls" + # sudo runs its command; udevadm "trigger" stands in for the kernel bind + # event by flipping the fake attribute the way the installed rule would. + cat >"$BATS_TEST_TMPDIR/bin/sudo" <<'STUB' +#!/bin/bash +echo "sudo $*" >>"$CALLS" +exec "$@" +STUB + cat >"$BATS_TEST_TMPDIR/bin/udevadm" <<'STUB' +#!/bin/bash +echo "udevadm $*" >>"$CALLS" +if [[ "$1" == trigger && "${FAKE_RULE_WORKS:-1}" == 1 ]]; then + echo cache >"$X3D_DRIVER_SYSFS/AMDI0101:00/amd_x3d_mode" +fi +STUB + chmod +x "$BATS_TEST_TMPDIR/bin/sudo" "$BATS_TEST_TMPDIR/bin/udevadm" + export PATH="$BATS_TEST_TMPDIR/bin:$PATH" +} + +@test "install puts the rule in place, triggers a bind event and ends on cache" { + run "$SCRIPT" + [ "$status" -eq 0 ] + [ -f "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" ] + grep -q 'DRIVER=="amd_x3d_vcache"' "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" + grep -q 'ATTR{amd_x3d_mode}="cache"' "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" + grep -q '^udevadm control --reload' "$CALLS" + grep -q '^udevadm trigger --action=bind' "$CALLS" + [ "$(cat "$X3D_DRIVER_SYSFS/AMDI0101:00/amd_x3d_mode")" = cache ] +} + +@test "the rule hard-codes no device name" { + run ! grep -q 'AMDI0101' "$REPO_DIR/fixes/60-amd-x3d-vcache-mode.rules" +} + +@test "install fails loudly when the mode does not end on cache" { + FAKE_RULE_WORKS=0 run "$SCRIPT" + [ "$status" -eq 1 ] + [[ "$output" == *"frequency"* ]] +} + +@test "install without the driver installs the rule and skips the trigger" { + rm -rf "$X3D_DRIVER_SYSFS" + run "$SCRIPT" + [ "$status" -eq 0 ] + [ -f "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" ] + run ! grep -q '^udevadm trigger' "$CALLS" +} + +@test "install with the driver loaded but no X3D device installs the rule and succeeds" { + rm -rf "${X3D_DRIVER_SYSFS:?}/AMDI0101:00" + run "$SCRIPT" + [ "$status" -eq 0 ] + [[ "$output" == *"no X3D device"* ]] + [ -f "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" ] +} + +@test "--remove deletes the rule and reloads udev" { + touch "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" + run "$SCRIPT" --remove + [ "$status" -eq 0 ] + [ ! -e "$X3D_RULES_DIR/60-amd-x3d-vcache-mode.rules" ] + grep -q '^udevadm control --reload' "$CALLS" +} + +@test "--status reports rule and live mode" { + run "$SCRIPT" --status + [ "$status" -eq 0 ] + [[ "$output" == *"frequency"* ]] +} + +@test "unknown option exits 2" { + run "$SCRIPT" --bogus + [ "$status" -eq 2 ] +}