nightly-compile-matrix #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |