Skip to content

Latest commit

 

History

History
164 lines (97 loc) · 12.4 KB

File metadata and controls

164 lines (97 loc) · 12.4 KB

System optimizations

This is the heart of SkillFishOS: the work that turns a raw AMD BC‑250 compute board into a fast, stable, daily‑drivable machine. Everything here is pre‑configured and tested — you don't need to apply any of it by hand. It's documented so the community can understand, reproduce and improve it.

The BC‑250 is a semi‑custom APU from the AMD Zen 2 + RDNA 2 family: CPU codename Oberon, GPU Cyan Skillfish (GFX1013), 16 GB of shared GDDR6. It's great value but a hostile Linux target. Here's each problem and how it's solved.


1. Custom kernel — linux-tkg 7.2.6‑skillfishos

Built from Frogging‑Family/linux‑tkg with:

  • BORE scheduler, GCC -O3, -march=znver2, 1000 Hz, NTsync + fsync — for gaming responsiveness.
  • BC‑250 userpatches:
    • GPU frequency unlock — exposes the SMU clock range 350–2230 MHz (stock firmware otherwise pins it).
    • 40‑CU unlock (opt‑in) — enables all 40 compute units instead of the default 24.
    • RDSEED‑quiet — the BC‑250 silicon has an unreliable RDSEED; mainline correctly disables it but prints RDSEED is not reliable on this platform; disabling. twelve times at boot (once per CPU, at EMERG priority, before any graphical console). The patch removes only the cosmetic pr_emerg() line while keeping clear_cpu_cap() + the MSR bit clear, so RDSEED stays correctly disabled — silently.

Warning: Never enable IOMMU on the BC‑250 — it's broken on this hardware. Avoid kernels 6.15.0–6.15.6 and 6.17.8–6.17.10.

Recipe and patches: kernel-build/. Build instructions: BUILD.md. Prebuilt .deb: Releases. Two flavours ship: main (-march=znver2, BC‑250) and x64 (-march=x86-64, any other x64 box). Measured against 7.0.11 and 7.1.7 the kernels are identical within ±2% — the update is for maintenance and security, not speed.


2. GPU clock control — the governor

Warning: The standard amdgpu sysfs (power_dpm_force_performance_level) does not control the BC‑250 — only the SMU does, through the OD voltage curve. SkillFishOS drives it with its own V/F governor (skillfish-vf-governor), installed as a systemd service.

The governor follows a voltage/frequency curve: a multi‑point ladder of MHz/mV knots, drawn and edited as a chart in the Control Center's Tuner section — drag a knot, or type the value in the table beside the chart — with a ceiling (maximum MHz) on top. Three presets are built in: Cautious (1850 MHz), Balanced (2000 MHz) and Performance (2100 MHz). Apply runs a candidate curve on trial for 25 seconds before it is written to disk, so a bad curve never survives a reboot. The governor idles the GPU to 350 MHz when it is unused.

Warning: The voltage curve must be a smooth multi‑point ladder — 350/700, 1500/900, 2000/1000, 2200/1000. On the BC‑250, 1000 mV is the practical stable ceiling at ~2150–2200 MHz; 2230 MHz @ 1000 mV is undervolted, and an abrupt clock transition there can hard‑freeze the whole machine (reproduced: a 2‑point 350/700 → 2230/1000 curve hung the box on the load→idle transition, with nothing in the logs). Governor reloads stop → settle → start to avoid the abrupt SMU jump. 2230 needs 1000–1060 mV and the silicon lottery.

Current FPS numbers with the curve‑based governor are in docs/CONTROL-CENTER.md, under "Measured after the change": moving to the Control Center changed nothing on the hardware side.

Memory bandwidth was measured (clpeak/OpenCL) at ~350–367 GB/s — healthy, not a bottleneck. The Memory Clock 450 MHz the driver reports is a reporting convention, not a 1/4 clock. Memory clock is not adjustable on the BC‑250.


3. CPU overclock & undervolt — 4.0 GHz

A persistent SMU overclock via bc250_smu_oc, applied at boot by a one‑shot systemd service from /etc/bc250-smu-oc.conf:

  • 4000 MHz with Vid ≤ 1.325 V, 85 °C thermal cap.
  • 4000 is this chip's verified‑stable maximum with all 8 cores unlocked, re‑measured step by step from 3500: every rung up to 4000 passes with zero MCEs, and the score climbs +14% over 3500. It only became reachable after the fan control was fixed — below ~3900 the limiter is the heatsink, not the silicon.
  • With half the cores parked (4c/8t) the ceiling moves to 4200 MHz, but multi‑thread throughput halves: worth it only for loads that use ≤8 threads.
  • Warning: Under a combined CPU+GPU soak the clock settles at 3375‑3492 MHz at 86 °C — no crash, just the thermal budget.
  • APU power sharing: under a combined CPU+GPU load the APU eases the CPU to ~3450 MHz to stay in budget — by design, no instability. Under CPU‑only load it holds 3700 MHz pinned right at the 85 °C guard.
  • A thermal guard watchdog steps the clock down if temperature exceeds the cap.

Warning: SMU contention: the governor and the OC tool both talk to the SMU. The OC service ordering (After=) and a lock prevent them from clashing during apply/detect.


3b. 8‑core unlock — 6c/12t → 8c/16t

The BC‑250 ships with two cores fused off in software: the SMU's core‑enable mask at SMN 0x5A870 reads 0x77 (3 of 4 cores per CCX). Writing 0xFF through SMU queue 3 (message 0x98) brings all 8 cores / 16 threads online — no patched BIOS required. skillfish-core-unlock, shipped in skillfish-base, does this at boot.

Measured gain (same boot, extra cores toggled off via /sys/.../cpuN/online): +20% — xz -T 6.41 s → 5.11 s, CPU llama.cpp inference 34.0 → 40.8 tok/s, for +2 °C. Below the theoretical +33% because of memory bandwidth and thread overhead, but the unlock genuinely pays.


4. 40‑CU unlock

active_cu_number goes from 24 → 40, and fp32 from ~6.9 to ~11.3 TFLOPS (vkpeak 11329 GFLOPS).

This used to be the kernel parameter amdgpu.bc250_cc_write_mode=3, applied at GPU init and needing a reboot to change. It is now skillfish-cu.service, which writes the same WGP masks at boot through umr and can change them live, so the Control Center moves compute units without restarting anything. Measured on the dev board on 2026-09-17: with the kernel parameter removed and the board rebooted, still 40/40 CU and 10169 GFLOPS against 10166 with it. The parameter is now dead weight wherever it is still written.

Configuration vkpeak fp32 (GFLOPS)
Stock ~2000 MHz, 24 CU (baseline) 6141
tkg + governor, 24 CU 6868
tkg + governor + 40 CU 11329

5. Memory split — VRAM (UMA) & GTT

All memory is shared GDDR6, so the split can be managed from the OS:

  • UMA VRAM is set in the BIOS CMOS via bc250_memcfg (persistent, battery‑backed; needs a reboot). The Tuner exposes this.
  • GTT is raised with ttm.pages_limit / ttm.page_pool_size on the cmdline. Those are counted in 4 KiB pages, so 1572864 = 6 GiB, and that number is the GTT ceiling: the GPU can address far more than the UMA VRAM, which is what makes large LLMs fit on the GPU. See AI.md.
  • amdgpu.gttsize= used to be the knob for this and is deprecated since kernel 7.x. When both are set the driver obeys gttsize, says so at every boot, and warns again if the two numbers disagree — which they did here (6 GiB of GTT under a 16 GiB TTM ceiling, on a board with 7.5 GiB of RAM). We now set only TTM.

6. Display — broken HPD / DisplayPort

The BC‑250's hotplug‑detect (HPD) line is non‑functional: the GPU never reads the monitor's EDID, so connectors report disconnected and audio/video fail or fall back to VESA.

  • Generic fix: video=DP-1:e on the cmdline force‑enables the connector without hardcoding a resolution — the monitor's EDID is then read and the monitor decides the resolution. This also makes the DisplayPort audio sink appear.
  • Hot‑swap daemon (skillfish-dp-hotswap): the kernel still updates the sysfs edid file passively on plug/unplug, so the daemon polls it silently and triggers a re‑detect only when the EDID changes — giving monitor hot‑swap with automatic resolution change and (almost) no flicker.

Warning: Active DP→HDMI adapters break audio on the BC‑250 — use a native DP monitor, a passive adapter, or a USB DAC.


7. Audio

The full PipeWire stack (pipewire‑pulse, WirePlumber, ALSA/BT) with user services enabled. Output works over DisplayPort (once the display fix above validates the ELD), USB DAC, or Bluetooth speakers — WirePlumber creates the sink automatically.


8. Wi‑Fi & Bluetooth

Common combo adapters (e.g. Realtek RTL8851BU, Wi‑Fi 6 + BT) ship in CD‑ROM installer mode. usb-modeswitch + usb-modeswitch-data flip them to the real device, persistently via udev — without them there is no Wi‑Fi and no Bluetooth on these adapters.


9. Game controllers

The kernel ships xpad, hid_playstation, hid_nintendo, hid_sony, hid_steam, etc. Verified working:

  • DualShock 4 over Bluetooth (with gyro/motion) — trusted auto‑reconnect.
  • Generic "Pro Controller" clones: most reliable over USB, where they enumerate as an Xbox 360 pad (xpad, XInput mode).

Controller battery levels (via UPower) are surfaced in the desktop HUD.


10. Storage — Btrfs + Snapper + grub‑btrfs

  • Subvolumes @rootfs and a separate @home — rollbacks never touch user data.
  • Snapper with timeline + automatic pre/post‑apt snapshots.
  • grub‑btrfs (built from source — not in Debian) puts bootable snapshots directly in the GRUB menu. The "safety net for tinkering" is real and one reboot away.

Warning: desktop-base can re‑inject the Debian GRUB theme via /etc/grub.d/05_debian_theme; SkillFishOS disables it (chmod -x) so the steampunk GRUB theme stays.


11. Telemetry — what this APU will and won't tell you

The BC‑250 mis‑reports its own graphics clock: under load pp_dpm_sclk reads 17‑51 MHz. Real numbers only come from the SMU mailbox, which is what skillfish-gpu-freq-sampler reads for the HUD and the charts.

On the CPU side each Zen 2 core has its own P‑state, so per‑core frequency is real and meaningful — with 8 cores unlocked, idle threads sit at 800, 1775 and 3990 MHz simultaneously. Sampling one core (what /proc/cpuinfo's first entry gives you) is therefore misleading, and Monitor and the dashboard now chart every logical CPU separately.

There is no per‑CU frequency, and that's hardware. umr -c reports max_shader_engines=2, max_sh_per_se=2, max_cu_per_sh=10 — 2×2×10 = 40 CU (20 WGP) — all in a single sclk domain. RDNA2 has no per‑CU DVFS: every enabled CU always runs at the same clock, so there is nothing to read.

Per‑CU occupancy is blocked too, on three independent counts (all measured with vkcube running):

  • umr -wa returns "No active waves!" — GFXOFF power‑gates the shader array between frames;
  • the amdgpu_gfxoff* debugfs nodes are read‑only and return EINVAL, so GFXOFF can't even be queried from there;
  • GRBM_STATUS, GRBM_STATUS_SE0 and GRBM_STATUS_SE1 stay pinned at 0x00000006 both idle and loaded — the cheap per‑shader‑engine signal doesn't move on this ASIC.

Getting there would need GFXOFF disabled via module parameter plus -O halt_waves, which halts the SQ — unacceptable on a GPU that is driving the desktop.


12. Always‑on (no suspend)

The BC‑250's ACPI suspend is broken (it enters s2idle and never wakes → reset). SkillFishOS masks sleep.target suspend.target hibernate.target hybrid-sleep.target and sets logind/KDE to never idle‑suspend or lock — so the box stays reachable (important for remote access) and a child or a remote session is never locked out. This mask is mandatory on any desktop environment.


Further reading