Build host: AMD BC-250 (Debian). Tool: https://github.com/Frogging-Family/linux-tkg
- git clone https://github.com/Frogging-Family/linux-tkg
- Copy
customization.cfgover linux-tkg/customization.cfg - Patch install.sh line ~176:
_kernel_flavor="tkg-${_kernel_localversion}"->_kernel_flavor="${_kernel_localversion}"(drops thetkg-prefix so uname = 7.0.10-skillfishos) - Put userpatches/*.mypatch into linux-tkg/linux70-tkg-userpatches/
- 0001-bc250-freq-unlock.mypatch (SCLK 350-2230 MHz)
- 0002-bc250-40cu-unlock.mypatch (40 CU, opt-in amdgpu.bc250_cc_write_mode=3) - from duggasco/bc250-40cu-unlock
- 0003-bc250-rdseed-quiet.mypatch
- 0004-rtw89-fw-O2.mypatch (fw.c does not build at -O3)
- 0005-bc250-vcn.mypatch (registers VCN 2.0.3 behind amdgpu.bc250_vcn=1; off by default, and there is no firmware for it -- see the VCN notes)
- 0006-bc250-8core-telemetry.mypatch (GPU clock readout with 8 cores unlocked:
the 8-core SMU metrics layout has no slot for GfxclkFrequency, so the driver
was reading a residency counter as a clock. Reads it from the firmware
instead. By higorprado, carried from GabriWar/bc250-core-cu-unlock.
⚠️ Once this ships, check whether the Monitor's SMU sampler is still needed: it works around this very bug, and two fixes for one bug is one too many.) (drops the cosmetic per-CPUpr_emerg("RDSEED is not reliable...")boot spam in arch/x86/kernel/cpu/amd.c; RDSEED is still correctly disabled via clear_cpu_cap/msr_clear_bit, just silently) 4b. Copyconfig-fragments/*.myfraginto the linux-tkg root (same folder as the PKGBUILD).customization.cfgalready has_config_fragments="true"and_config_fragments_no_confirm="true", so they are applied without asking. skillfish-sched-ext.myfrag— turns onCONFIG_SCHED_CLASS_EXT, the extensible scheduler class, plus the BTF it needs. Installdwarveson the build host first (pahole), or the build fails late with a missing tool.
userpatches/ is not the only set in here, and picking the wrong one gives a
kernel that boots and is not the one we ship. What each directory is:
userpatches-350/— the current recipe. 43 patches; this is what builds7.2.9-skillfishos, the published kernel and the one installed on both boards. (It built7.2.3,7.2.4and7.2.5before; only the upstream version changed.) Four series, and the numbering says where each one comes from:0100-0118— Project Ariel (cachenetics): SMU/PMFW message enums, forced GFX clock and DPM levels, PMFW telemetry, the race-free GFXCLK query, the CU unlock that does not touch RLC_PG, and the gfx1013 GFXOFF disable.0130-0131— DryhoppedIPA, the compute-queue pair. Measured on a BC-250: the Mesa half is worth +4.2% on Cyberpunk, the kernel half about +0.6% on Wukong. The +20.8% claimed upstream did not reproduce.0200-0211and0299— our own series, renumbered from0001-0016so it applies after Ariel's.0300— gpu_busy_percent for gfx1013, see below.
userpatches/— the legacy 16-patch set that built the published 7.2.0 and 7.2.2 kernels. Kept because it documents what users currently get from the APT archive.userpatches-ariel/anduserpatches-gfxon/— an A/B pair, not a recipe.gfxonisarielminus the gfx1013 GFXOFF disable, built to find out whether GFXOFF was behind the compute errors on bc250-dev. Keep them together or the experiment cannot be repeated.userpatches-sospese/— the VCN patch. It registers the block, the PSP refuses to load firmware for it, and both donor firmwares wedge the board. Not built into anything.
Cyan Skillfish has no GFX activity metric: gpu_metrics reports
average_gfx_activity = 0xFFFF ("not present") and no PMFW message returns it
either. Overlays divide 65535 by 100 and draw 655% over every game.
The patch samples GRBM_STATUS.GUI_ACTIVE — the counter radeontop reads from
userspace — with a soft hrtimer at 1 kHz, publishes the average about four
times a second, and exposes it two ways: through
AMDGPU_PP_SENSOR_GPU_LOAD (that is sysfs gpu_busy_percent) and in the
gpu_metrics table.
Both are needed. Fixing only the sensor leaves the wrong number on screen: measured with the sensor already reporting 99% while MangoHud still drew 655%, because overlays read the table, not the sysfs file.
The sampling rate was measured, not guessed. Under a steady vkcube load, sampling GUI_ACTIVE from userspace gives 75.0% at 100 Hz, 79.6% at 500 Hz and 84.8% at 2 kHz: the slower the sampling, the more the sample instants line up with the CPU wake-ups that bracket GPU submissions, and the more busy time is missed. At 1 kHz the driver reports 82-92% where radeontop reports 76-89%. Cost is not measurable: 1.00% CPU with and without a reader, and the timer stops itself two seconds after the last read.
The same 43 patches build -skillfishos-x64 for machines that are not a
BC-250: the BC-250 patches are inert elsewhere because they hook that device.
What changes is _processor_opt="x86-64", the localversion, and
config-fragments/skillfish-generic-cpu.myfrag, which must be in place or
the kernel comes out -march=native for the build host — the failure behind
issue #53, where a generic image loads GRUB and then does nothing.
- localversion. Running
makeinside the tree by hand produces modules whose vermagic is7.2.3instead of7.2.3-skillfishos, and they will not load. Write the suffix into the tree first:printf -- '-skillfishos\n' > localversion. - debug symbols. A hand-built module is not stripped: amdgpu came out at
117 MB compressed against the 7 MB of the packaged one, which also blows up
the initramfs. Run
strip --strip-debugbefore compressing it.
- ./install.sh install -> .deb in DEBS/ (then publish via scripts/publish-kernel.sh)
Key config: BORE, GCC -O3, -march=znver2, 1000Hz, NTsync+fsync, no LTO,
localversion=skillfishos, and from 7.2.1 onwards sched_ext enabled.
CONFIG_SCHED_CLASS_EXT lets the kernel host a scheduler written in BPF and
swap it at runtime. It is the one real kernel-level difference between this
kernel and CachyOS's — measured on 2026-08-23, our config did not have the
symbol at all.
scx-scheds nor scx. Until we
package them the kernel keeps running BORE, which is the default and is fine.
Enabling it now means the kernel is ready the day those land, instead of asking
people to wait for a rebuild.
CONFIG_DEBUG_INFO_NONE=y — none at all. Turning it on lengthens the build
and adds a few MB to the image. The build host needs pahole from dwarves.
_ftracedisable="false". With FTRACE off the LAVD scheduler does not
work — customization.cfg says so on the line above the option.
install.sh reads ~/.config/frogminer/linux-tkg.cfg if it exists, and that
file overrides customization.cfg — the one in this repository. It says so in
one line of output that scrolls past in a second:
-> External configuration file /root/.config/frogminer/linux-tkg.cfg will be
used and will override customization.cfg values
On 2026-08-22 that leftover file still said _version="7.1-latest", so a build
started for 7.2 quietly checked out 7.1 instead. Nothing failed; it just built
the wrong kernel.
Before every build, check what will actually be used:
grep -E '^_(version|kernel_localversion)=' ~/.config/frogminer/linux-tkg.cfgand make it agree with customization.cfg, or move it aside.
_version="7.2" is refused — the value has to be either 7.2-latest (resolves to
the newest 7.2.z) or a tag that exists in the remote, like v7.2. Getting this
wrong stops the build at the first step, which at least is honest.
linux72-tkg-userpatches — VERSION and PATCHLEVEL glued together, no dot. Put
them in the wrong folder and the build succeeds without them: no core unlock,
no frequency unlock, and nothing says so. Check the log:
grep 'Applying your own' /root/build-*.logThere should be one line per patch.
0004-rtw89-fw-O2.mypatch claimed three lines of context (@@ -1,3 +1,6 @@) and
carried none. patch(1) cannot place a hunk it cannot match, and linux-tkg
aborts the whole build on a failed patch. Never hand-write a hunk header:
generate patches with diff against the real tree, the way
0005-bc250-vcn.mypatch was made.
The BC-250 kernel is <version>-skillfishos. Its -march=znver2 build will not
boot anywhere else, and the name does not say so — which is fine as long as it
is the only one.
<version>-skillfishos-x64 (LOCALVERSION=-skillfishos-x64), not
-skillfishos-generic and not a bare -skillfishos.
The reason is not tidiness: both kernels land in the same /boot, the same GRUB
menu and the same APT repository, and a user who ends up with two entries whose
names differ only by version number has no way to tell which one runs on their
hardware. -x64 says it plainly — this is the one for any 64-bit machine —
while the plain name stays with the board the distribution is built around.
scripts/iso-prep-kernel.sh matches *skillfishos*, so it keeps working for
both without changes.