Skip to content

nightly-compile-matrix #44

nightly-compile-matrix

nightly-compile-matrix #44

name: nightly-compile-matrix
on:
schedule:
# 04:17 UTC daily. Off the hour so we don't hammer github.com or
# cdn.kernel.org alongside everyone else.
- cron: '17 4 * * *'
workflow_dispatch:
inputs:
kernels:
description: 'Space-separated kernel versions to test'
required: false
default: '6.6.93 6.12.30 6.14.4 6.16.12 6.18.29'
# Don't pile up nightly runs if one is already in progress.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
compile:
name: ${{ matrix.driver }} @ ${{ matrix.kernel }}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
# Kernels covered:
# 6.6 - scarthgap LTS
# 6.12 - walnascar
# 6.14 - mid-stable, exercises the cfg80211 punct_bitmap revert
# 6.16 - whinlatter
# 6.18 - wrynose LTS
kernel: ['6.6.93', '6.12.30', '6.14.4', '6.16.12', '6.18.29']
driver:
- rtl8192eu
- rtl8723bu
- rtl8723du
- rtl8812au
- rtl8814au
- rtl8821au
- rtl8821cu
- rtl88x2bu
steps:
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential bc bison flex libssl-dev libelf-dev kmod \
cpio rsync xz-utils
- name: Cache prepared kernel tree
uses: actions/cache@v4
with:
path: kernels/linux-${{ matrix.kernel }}
key: kernel-prepared-${{ matrix.kernel }}-v2
- name: Download and prepare kernel ${{ matrix.kernel }}
run: |
set -eo pipefail
mkdir -p kernels
KDIR="kernels/linux-${{ matrix.kernel }}"
if [ -f "$KDIR/.prepared" ]; then
echo "kernel ${{ matrix.kernel }} already prepared (cache hit)"
exit 0
fi
MAJOR="${{ matrix.kernel }}"
MAJOR="${MAJOR%%.*}"
curl -fLo "kernels/linux-${{ matrix.kernel }}.tar.xz" \
"https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${{ matrix.kernel }}.tar.xz"
tar -xf "kernels/linux-${{ matrix.kernel }}.tar.xz" -C kernels
make -C "$KDIR" -j"$(nproc)" defconfig >/dev/null
"$KDIR/scripts/config" --file "$KDIR/.config" \
-e CFG80211 -e MAC80211 -e WIRELESS -e WLAN \
-e USB -e USB_SUPPORT \
-d WERROR
make -C "$KDIR" -j"$(nproc)" olddefconfig >/dev/null
make -C "$KDIR" -j"$(nproc)" modules_prepare >/dev/null
touch "$KDIR/.prepared"
- name: Checkout meta-rtlwifi (for recipe SRCREVs)
uses: actions/checkout@v4
with:
path: meta-rtlwifi
- name: Clone driver ${{ matrix.driver }} at the recipe's pinned SRCREV
run: |
set -eo pipefail
RECIPE="meta-rtlwifi/recipes-bsp/drivers/${{ matrix.driver }}.bb"
# Pull SRC_URI and SRCREV straight out of the .bb so this CI
# always tracks the same upstream+revision the layer ships.
URL=$(awk -F'[ =]+' '
/^SRC_URI[[:space:]]*=/ { capture=1 }
capture {
match($0, /git:\/\/[^ ;"]+/);
if (RSTART) { print substr($0, RSTART+6, RLENGTH-6); exit }
}
' "$RECIPE")
BRANCH=$(grep -oE 'branch=[^ ;"]+' "$RECIPE" | head -1 | cut -d= -f2)
SRCREV=$(awk -F'"' '/^SRCREV[[:space:]]*=/ { print $2; exit }' "$RECIPE")
: "${URL:?could not parse SRC_URI from $RECIPE}"
: "${BRANCH:?could not parse branch from $RECIPE}"
: "${SRCREV:?could not parse SRCREV from $RECIPE}"
# SRC_URI strips the protocol so we got github.com/... -> need
# to put a scheme back on. The recipes use protocol=https.
echo "driver=${{ matrix.driver }} url=https://$URL branch=$BRANCH srcrev=$SRCREV"
git clone "https://$URL" "drivers/${{ matrix.driver }}"
git -C "drivers/${{ matrix.driver }}" checkout "$SRCREV"
- name: Build ${{ matrix.driver }} against linux-${{ matrix.kernel }}
run: |
set -eo pipefail
cd "drivers/${{ matrix.driver }}"
make KSRC="$GITHUB_WORKSPACE/kernels/linux-${{ matrix.kernel }}" \
KBUILD_MODPOST_WARN=1 \
-j"$(nproc)" modules
- name: modinfo + depmod -n
# Validate module metadata (license tag, version, USB id table,
# parameters, vermagic) and that the dependency wiring resolves
# cleanly. depmod -n is a dry-run that exits non-zero if symbols
# don't satisfy. Cheap; runs in seconds.
run: |
set -eo pipefail
shopt -s nullglob
# The driver Makefiles produce one .ko named after MODULE_NAME
# which doesn't always match the recipe name (rtl8723bu -> 8723bu.ko
# etc.). Pick up every .ko produced.
KOS=( drivers/${{ matrix.driver }}/*.ko )
if [ ${#KOS[@]} -eq 0 ]; then
echo "No .ko produced; nothing to inspect" >&2
exit 1
fi
for ko in "${KOS[@]}"; do
echo ">>> modinfo $ko"
modinfo "$ko"
echo
done
# depmod -n -b <dir> reads modules from <dir>/lib/modules/<kver>/
# so stage the .ko in that layout, then dry-run-check that
# dependencies resolve against the kernel's Module.symvers.
KVER=$(make -sC "$GITHUB_WORKSPACE/kernels/linux-${{ matrix.kernel }}" kernelrelease)
STAGE="$RUNNER_TEMP/stage"
MODDIR="$STAGE/lib/modules/$KVER"
mkdir -p "$MODDIR/kernel/drivers/net/wireless"
cp "${KOS[@]}" "$MODDIR/kernel/drivers/net/wireless/"
cp "$GITHUB_WORKSPACE/kernels/linux-${{ matrix.kernel }}/System.map" \
"$MODDIR/" 2>/dev/null || true
echo ">>> depmod -n -b $STAGE $KVER"
depmod -n -b "$STAGE" "$KVER" > "$RUNNER_TEMP/depmod.out"
tail -20 "$RUNNER_TEMP/depmod.out"
# Surface unresolved-symbol warnings to humans without failing
# the build (Module.symvers is empty from modules_prepare-only,
# so most external-symbol checks are tautologically empty here).
if grep -i "needs unknown symbol" "$RUNNER_TEMP/depmod.out"; then
echo "::warning::depmod reported unresolved symbols"
fi
static-analysis:
# Run sparse over every driver's source. Single kernel target since
# sparse cares about the C, not about kernel API drift.
name: sparse (${{ matrix.driver }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
driver:
- rtl8192eu
- rtl8723bu
- rtl8723du
- rtl8812au
- rtl8814au
- rtl8821au
- rtl8821cu
- rtl88x2bu
steps:
- name: Install build deps + sparse
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential bc bison flex libssl-dev libelf-dev kmod \
cpio rsync xz-utils sparse
- name: Cache prepared kernel tree (6.12 LTS)
uses: actions/cache@v4
with:
path: kernels/linux-6.12.30
key: kernel-prepared-6.12.30-v2
- name: Download and prepare kernel 6.12.30
run: |
set -eo pipefail
mkdir -p kernels
KDIR="kernels/linux-6.12.30"
if [ -f "$KDIR/.prepared" ]; then exit 0; fi
curl -fLo "kernels/linux-6.12.30.tar.xz" \
"https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.30.tar.xz"
tar -xf "kernels/linux-6.12.30.tar.xz" -C kernels
make -C "$KDIR" -j"$(nproc)" defconfig >/dev/null
"$KDIR/scripts/config" --file "$KDIR/.config" \
-e CFG80211 -e MAC80211 -e WIRELESS -e WLAN \
-e USB -e USB_SUPPORT -d WERROR
make -C "$KDIR" -j"$(nproc)" olddefconfig >/dev/null
make -C "$KDIR" -j"$(nproc)" modules_prepare >/dev/null
touch "$KDIR/.prepared"
- uses: actions/checkout@v4
with:
path: meta-rtlwifi
- name: Clone driver ${{ matrix.driver }} at pinned SRCREV
run: |
set -eo pipefail
RECIPE="meta-rtlwifi/recipes-bsp/drivers/${{ matrix.driver }}.bb"
URL=$(awk -F'[ =]+' '/^SRC_URI[[:space:]]*=/ { capture=1 } capture { match($0, /git:\/\/[^ ;"]+/); if (RSTART) { print substr($0, RSTART+6, RLENGTH-6); exit } }' "$RECIPE")
BRANCH=$(grep -oE 'branch=[^ ;"]+' "$RECIPE" | head -1 | cut -d= -f2)
SRCREV=$(awk -F'"' '/^SRCREV[[:space:]]*=/ { print $2; exit }' "$RECIPE")
git clone "https://$URL" "drivers/${{ matrix.driver }}"
git -C "drivers/${{ matrix.driver }}" checkout "$SRCREV"
- name: Build with sparse
run: |
set -eo pipefail
cd "drivers/${{ matrix.driver }}"
# C=2 forces sparse on every file; CHECK=sparse names the tool.
# We capture, then summarise; sparse warnings don't fail the
# build unless egregious to keep this informational while the
# warning baseline is established.
make KSRC="$GITHUB_WORKSPACE/kernels/linux-6.12.30" \
KBUILD_MODPOST_WARN=1 \
C=2 CHECK="sparse" \
-j"$(nproc)" modules \
2>&1 | tee "$RUNNER_TEMP/sparse.log"
echo
echo "=== Sparse summary ==="
grep -E "warning:|error:" "$RUNNER_TEMP/sparse.log" \
| awk -F: '{print $4}' | sort | uniq -c | sort -rn | head -20 \
|| true
qemu-insmod:
# Actually load every built .ko into a running kernel under QEMU
# and assert that module init returns 0. Single kernel target
# (6.12 LTS walnascar) for cost reasons; per-kernel insmod would
# require building a bzImage per kernel which is multi-hour.
name: qemu insmod (${{ matrix.driver }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
driver:
- rtl8192eu
- rtl8723bu
- rtl8723du
- rtl8812au
- rtl8814au
- rtl8821au
- rtl8821cu
- rtl88x2bu
steps:
- name: Install build deps + qemu + busybox
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential bc bison flex libssl-dev libelf-dev kmod \
cpio rsync xz-utils qemu-system-x86 busybox-static
- name: Cache built kernel image + module tree (6.12 LTS)
uses: actions/cache@v4
with:
path: kernels/linux-6.12.30
key: kernel-bzimage-6.12.30-v1
- name: Build kernel image (6.12.30, tinyconfig + wifi)
run: |
set -eo pipefail
mkdir -p kernels
KDIR="kernels/linux-6.12.30"
if [ -f "$KDIR/.bzimage-built" ]; then
echo "bzImage already in cache"
exit 0
fi
if [ ! -d "$KDIR" ]; then
curl -fLo "kernels/linux-6.12.30.tar.xz" \
"https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.30.tar.xz"
tar -xf "kernels/linux-6.12.30.tar.xz" -C kernels
fi
# tinyconfig + just enough to boot a kernel that can load
# WiFi modules. Keep it small so the bzImage build fits in
# a few minutes on a GHA runner.
make -C "$KDIR" -j"$(nproc)" tinyconfig >/dev/null
"$KDIR/scripts/config" --file "$KDIR/.config" \
-e 64BIT -e SMP \
-e PRINTK -e EARLY_PRINTK -e SERIAL_8250 -e SERIAL_8250_CONSOLE \
-e BLK_DEV_INITRD -e RD_GZIP \
-e MODULES -e MODULE_UNLOAD \
-e CFG80211 -e MAC80211 -e WIRELESS -e WLAN \
-e USB -e USB_SUPPORT -e USB_XHCI_HCD \
-e DEVTMPFS -e DEVTMPFS_MOUNT \
-e NET -e PACKET -e UNIX -e INET \
-e TTY -e VT -e VT_CONSOLE \
-e BINFMT_ELF -e BINFMT_SCRIPT \
-e PROC_FS -e SYSFS \
-e PCI -e ACPI \
-d WERROR
make -C "$KDIR" -j"$(nproc)" olddefconfig >/dev/null
make -C "$KDIR" -j"$(nproc)" bzImage modules_prepare
touch "$KDIR/.bzimage-built"
- uses: actions/checkout@v4
with:
path: meta-rtlwifi
- name: Clone + build driver at pinned SRCREV
run: |
set -eo pipefail
RECIPE="meta-rtlwifi/recipes-bsp/drivers/${{ matrix.driver }}.bb"
URL=$(awk -F'[ =]+' '/^SRC_URI[[:space:]]*=/ { capture=1 } capture { match($0, /git:\/\/[^ ;"]+/); if (RSTART) { print substr($0, RSTART+6, RLENGTH-6); exit } }' "$RECIPE")
BRANCH=$(grep -oE 'branch=[^ ;"]+' "$RECIPE" | head -1 | cut -d= -f2)
SRCREV=$(awk -F'"' '/^SRCREV[[:space:]]*=/ { print $2; exit }' "$RECIPE")
git clone "https://$URL" "drivers/${{ matrix.driver }}"
git -C "drivers/${{ matrix.driver }}" checkout "$SRCREV"
cd "drivers/${{ matrix.driver }}"
make KSRC="$GITHUB_WORKSPACE/kernels/linux-6.12.30" \
KBUILD_MODPOST_WARN=1 -j"$(nproc)" modules
- name: Build initramfs with the driver
run: |
set -eo pipefail
shopt -s nullglob
ROOT=$(mktemp -d)
mkdir -p "$ROOT"/{bin,sbin,proc,sys,dev,lib/modules}
# Bundle busybox-static; it gives us /bin/sh, insmod, dmesg.
install -m 0755 /usr/bin/busybox "$ROOT/bin/busybox"
for cmd in sh insmod dmesg mount poweroff echo cat; do
ln -s busybox "$ROOT/bin/$cmd"
done
cp drivers/${{ matrix.driver }}/*.ko "$ROOT/lib/modules/"
cat > "$ROOT/init" <<'INIT'
#!/bin/sh
export PATH=/bin:/sbin
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
echo "=== running insmod on each driver .ko ==="
rc=0
for m in /lib/modules/*.ko; do
echo "--- insmod $m ---"
if insmod "$m"; then
echo " insmod OK: $m"
else
echo " insmod FAILED: $m" >&2
rc=1
fi
done
echo "DRIVER_INSMOD_RESULT=$rc"
# `reboot -f` does LINUX_REBOOT_CMD_RESTART, which qemu's
# -no-reboot catches and exits with. `poweroff` on this
# tinyconfig falls back to halt and qemu sits forever.
reboot -f
INIT
chmod +x "$ROOT/init"
( cd "$ROOT" && find . | cpio -o -H newc | gzip -9 ) > initramfs.cpio.gz
ls -la initramfs.cpio.gz
- name: Boot kernel under QEMU and observe insmod
timeout-minutes: 5
run: |
set -eo pipefail
qemu-system-x86_64 \
-no-reboot -nographic \
-m 256 -smp 2 \
-kernel kernels/linux-6.12.30/arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append 'console=ttyS0 panic=1' \
2>&1 | tee qemu.log
# The init script prints DRIVER_INSMOD_RESULT=0 on success.
if grep -q "DRIVER_INSMOD_RESULT=0" qemu.log; then
echo "::notice::all .ko modules insmod-loaded cleanly"
else
echo "::error::insmod failed inside qemu (see qemu.log)"
exit 1
fi
summary:
name: matrix summary
needs: [compile, static-analysis, qemu-insmod]
if: always()
runs-on: ubuntu-22.04
steps:
- name: Report
env:
COMPILE: ${{ needs.compile.result }}
SPARSE: ${{ needs.static-analysis.result }}
QEMU: ${{ needs.qemu-insmod.result }}
run: |
echo "compile : $COMPILE"
echo "static-analysis: $SPARSE"
echo "qemu-insmod : $QEMU"
for r in "$COMPILE" "$SPARSE" "$QEMU"; do
if [ "$r" != "success" ]; then exit 1; fi
done