forked from marfrit/kernel-agent
Compare commits
27 Commits
9092d9aaaa
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 84cdb5b4ee | |||
| 3d15c5367d | |||
| 588350c4da | |||
| cc6f2378ab | |||
| dd631fd3c7 | |||
| 38fd672940 | |||
| 8b356aa11f | |||
| 443f5e992e | |||
| 2f119a3fb7 | |||
| 7a86ebb587 | |||
| 731e98e079 | |||
| bae99da612 | |||
| 3d10a2c21a | |||
| 3ee0ef7d86 | |||
| 878e86f103 | |||
| 4d98a8169d | |||
| 84734ba527 | |||
| c9e9ad973c | |||
| a840f76907 | |||
| 989b8842fb | |||
| f203b70f4f | |||
| a254b6f0bb | |||
| 43c8f0cba8 | |||
| 42b0c5042a | |||
| 4c80458d1f | |||
| 96af34d775 | |||
| 95be39ef80 |
@@ -264,8 +264,8 @@ build. `ka-promote` (issue #22) replaced the manual step #1 below as of 2026-05-
|
||||
|---|---|---|
|
||||
| `ka-import fresnel-fourier <patches> --to board/pinebook-pro` (originally named `ka-promote` in this row) | Authored 3 patches with proper headers/scope tags, pushed to `marfrit/kernel-agent/patches/board/pinebook-pro/` via Gitea contents API as `claude-noether`. | still manual — `ka-import` unimplemented |
|
||||
| `ka-promote fresnel` (new — manifest → cumulative.patch + manifest.lock) | n/a (didn't exist) | **automated 2026-05-18, issue #22** |
|
||||
| `ka-build fresnel` | On boltzmann: cloned linux v7.0 from kernel.org, ran `makepkg -s --skipchecksums --skippgpcheck` against `marfrit-packages/arch/linux-fresnel-fourier/PKGBUILD`. Native aarch64 (boltzmann is RK3588). One headers-pkg bug discovered (`ln -sr` on missing parent dir) and fixed mid-flight. Repackaged. | still manual — next verb to implement |
|
||||
| `ka-sign + push` | scp pkgs hertz → `sudo /opt/herding/bin/marfrit-publish-arch aarch64 <pkg>` per pkg. Script signs with key `92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C`, runs repo-add, rsyncs to nc. | still manual — folded into `ka-build` |
|
||||
| `ka-build fresnel` | On boltzmann: cloned linux v7.0 from kernel.org, ran `makepkg -s --skipchecksums --skippgpcheck` against `marfrit-packages/arch/linux-fresnel-fourier/PKGBUILD`. Native aarch64 (boltzmann is RK3588). One headers-pkg bug discovered (`ln -sr` on missing parent dir) and fixed mid-flight. Repackaged. | **automated 2026-05-19, issue #34** — `ka-build <host>` ssh-dispatches makepkg to `build_host.primary`, verifies kernel-agent patches still match the PKGBUILD-side files (b2sum cross-check from `manifest.lock`), and pulls the resulting `*.pkg.tar.zst` back. |
|
||||
| `ka-sign + push` | scp pkgs hertz → `sudo /opt/herding/bin/marfrit-publish-arch aarch64 <pkg>` per pkg. Script signs with key `92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C`, runs repo-add, rsyncs to nc. | **folded into `ka-build` 2026-05-19** — `ka-build` scp's each pkg to hertz and runs `marfrit-publish-arch` over ssh. `--skip-publish` flag retained for offline builds. |
|
||||
| `ka-install fresnel` (consent-via-action) | `sudo pacman -U /tmp/<pkg>` over LAN scp (HTTPS to nc was throttled by fresnel's wifi). pacman post-transaction hook updated extlinux. mkinitcpio run manually because the standard hook trigger watches `vmlinuz` not `Image`. | still manual — last verb to implement |
|
||||
| Bar 1..3 verification | SSH heartbeat OK, `pacman -Q linux-fresnel-fourier` = `7.0-1`, post-reboot cluster0 1.704 GHz / cluster1 2.184 GHz confirmed. | folded into `ka-install` |
|
||||
|
||||
|
||||
Executable
+199
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
# ka-build — render PKGBUILD from manifest.lock, build native on host,
|
||||
# sign+publish via marfrit-publish-arch on hertz.
|
||||
#
|
||||
# Phase-1 (issue #34): arch makepkg wrapper. Debian path deferred.
|
||||
#
|
||||
# Usage:
|
||||
# ka-build <host>
|
||||
# ka-build <host> --packages-repo <path> # default: ~/src/marfrit-packages
|
||||
# ka-build <host> --dry-run # stop after staging, don't makepkg
|
||||
# ka-build <host> --skip-publish # build only, don't push to hertz
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 success (pkg built + published)
|
||||
# 2 missing input (manifest.lock, PKGBUILD, ssh target)
|
||||
# 3 patch drift (resolved.sha256 != PKGBUILD-side file sha256)
|
||||
# 4 makepkg / sign / publish failure
|
||||
# 5 manifest parse error
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION=1
|
||||
|
||||
die() { echo "ka-build: error: $1" >&2; exit "${2:-1}"; }
|
||||
note() { echo "ka-build: $1"; }
|
||||
|
||||
# Defaults
|
||||
PACKAGES_REPO="${KA_PACKAGES_REPO:-${HOME}/src/marfrit-packages}"
|
||||
DRY_RUN=0
|
||||
SKIP_PUBLISH=0
|
||||
HOST=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--packages-repo) PACKAGES_REPO="$2"; shift 2 ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
--skip-publish) SKIP_PUBLISH=1; shift ;;
|
||||
--version) echo "ka-build version $VERSION"; exit 0 ;;
|
||||
-h|--help) sed -n '1,30p' "$0" | grep -E '^# ' | sed 's/^# //'; exit 0 ;;
|
||||
-*) die "unknown flag: $1" ;;
|
||||
*) [ -z "$HOST" ] && HOST="$1" || die "extra arg: $1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$HOST" ] || die "host is required" 2
|
||||
[ -d "$PACKAGES_REPO" ] || die "--packages-repo not found: $PACKAGES_REPO" 2
|
||||
|
||||
# Locate kernel-agent repo root (where bin/ + fleet/ live)
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$script_dir/.." && pwd)"
|
||||
[ -d "$REPO_ROOT/fleet" ] || die "fleet/ not found relative to $script_dir" 2
|
||||
|
||||
manifest="$REPO_ROOT/fleet/${HOST}.yaml"
|
||||
[ -f "$manifest" ] || die "no manifest for host '$HOST': $manifest" 2
|
||||
|
||||
# Read fields from manifest via python (yaml in bash is masochism)
|
||||
py_read() {
|
||||
python3 -c "
|
||||
import sys, yaml, os
|
||||
m = yaml.safe_load(open('$manifest'))
|
||||
keys = '$1'.split('.')
|
||||
v = m
|
||||
for k in keys:
|
||||
if not isinstance(v, dict) or k not in v: sys.exit('missing key: $1')
|
||||
v = v[k]
|
||||
print(v)
|
||||
"
|
||||
}
|
||||
|
||||
PKG_NAME="$(py_read package.name)"
|
||||
BASELINE_REF="$(py_read baseline.ref)"
|
||||
BUILD_HOST="$(py_read build_host.primary)"
|
||||
|
||||
# Locate the most recent ka-promote output
|
||||
build_dir_root="${KA_BUILD_DIR:-$REPO_ROOT/build}"
|
||||
promote_out="${build_dir_root}/${HOST}/${BASELINE_REF}"
|
||||
lock="${promote_out}/manifest.lock"
|
||||
cumulative="${promote_out}/cumulative.patch"
|
||||
[ -f "$lock" ] || die "no manifest.lock at $lock — run 'ka-promote $HOST' first" 2
|
||||
[ -f "$cumulative" ] || die "no cumulative.patch at $cumulative — run 'ka-promote $HOST' first" 2
|
||||
|
||||
# Locate the PKGBUILD
|
||||
pkg_dir="${PACKAGES_REPO}/arch/${PKG_NAME}"
|
||||
pkgbuild="${pkg_dir}/PKGBUILD"
|
||||
[ -f "$pkgbuild" ] || die "no PKGBUILD at $pkgbuild (expected from manifest package.name)" 2
|
||||
|
||||
note "host=$HOST pkg=$PKG_NAME baseline=$BASELINE_REF build_host=$BUILD_HOST"
|
||||
note "PKGBUILD: $pkgbuild"
|
||||
note "manifest.lock: $lock"
|
||||
|
||||
# Refuse if PKGBUILD-side patches drifted from kernel-agent patches/.
|
||||
# manifest.lock.resolved_patches[].sha256 must match PKGBUILD-dir-side
|
||||
# files of the same basename. (If a patch is in resolved but missing from
|
||||
# PKGBUILD dir, fail loud — operator needs to sync.)
|
||||
note "verifying patch consistency between kernel-agent and marfrit-packages..."
|
||||
drift=0
|
||||
while IFS=$'\t' read -r basename expected_sha; do
|
||||
pkg_side="${pkg_dir}/${basename}"
|
||||
if [ ! -f "$pkg_side" ]; then
|
||||
echo " MISSING in PKGBUILD dir: $basename" >&2
|
||||
drift=1; continue
|
||||
fi
|
||||
actual_sha=$(sha256sum "$pkg_side" | cut -d' ' -f1)
|
||||
if [ "$actual_sha" != "$expected_sha" ]; then
|
||||
echo " DRIFT: $basename (expected $expected_sha, got $actual_sha)" >&2
|
||||
drift=1
|
||||
fi
|
||||
done < <(python3 -c "
|
||||
import yaml, sys, os
|
||||
lk = yaml.safe_load(open('$lock'))
|
||||
for r in lk['resolved_patches']:
|
||||
bn = os.path.basename(r['include'])
|
||||
print(f\"{bn}\t{r['sha256']}\")
|
||||
")
|
||||
[ "$drift" -eq 0 ] || die "patches differ between kernel-agent and marfrit-packages — sync first" 3
|
||||
note "patches OK ($(python3 -c "import yaml; print(len(yaml.safe_load(open('$lock'))['resolved_patches']))") files)"
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
note "--dry-run: stopping before makepkg"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Stage build dir on the build host via ssh
|
||||
note "staging build on ${BUILD_HOST}..."
|
||||
remote_stage="/tmp/ka-build-${HOST}-$$"
|
||||
ssh "${BUILD_HOST}" "mkdir -p '$remote_stage'"
|
||||
rsync -a "${pkg_dir}/" "${BUILD_HOST}:${remote_stage}/"
|
||||
|
||||
# Run makepkg natively
|
||||
note "running makepkg --syncdeps --noconfirm --cleanbuild on ${BUILD_HOST}..."
|
||||
ssh "${BUILD_HOST}" "cd '$remote_stage' && makepkg --syncdeps --noconfirm --cleanbuild --skipchecksums" \
|
||||
|| die "makepkg failed on ${BUILD_HOST}" 4
|
||||
|
||||
# Fetch built packages
|
||||
note "fetching .pkg.tar.zst from ${BUILD_HOST}..."
|
||||
local_out="${promote_out}/pkgs"
|
||||
mkdir -p "$local_out"
|
||||
rsync -av "${BUILD_HOST}:${remote_stage}/*.pkg.tar.zst" "$local_out/" 2>&1 | tail -5
|
||||
|
||||
# Compute b2sums
|
||||
pkg_b2sum_list=$(cd "$local_out" && for p in *.pkg.tar.zst; do
|
||||
[ -f "$p" ] || continue
|
||||
printf '%s %s\n' "$(b2sum "$p" | cut -d' ' -f1)" "$p"
|
||||
done)
|
||||
note "built packages:"
|
||||
echo "$pkg_b2sum_list" | sed 's/^/ /'
|
||||
|
||||
# Publish via hertz marfrit-publish-arch (unless --skip-publish)
|
||||
if [ "$SKIP_PUBLISH" -eq 0 ]; then
|
||||
note "publishing to packages.reauktion.de/arch/aarch64/..."
|
||||
for p in "$local_out"/*.pkg.tar.zst; do
|
||||
[ -f "$p" ] || continue
|
||||
base="$(basename "$p")"
|
||||
scp -q "$p" "hertz:/tmp/${base}" || die "scp to hertz failed: $base" 4
|
||||
ssh hertz "sudo /opt/herding/bin/marfrit-publish-arch aarch64 '/tmp/${base}'" \
|
||||
|| die "marfrit-publish-arch failed: $base" 4
|
||||
ssh hertz "rm -f '/tmp/${base}'"
|
||||
note "published: $base"
|
||||
done
|
||||
fi
|
||||
|
||||
# Update manifest.lock with build receipt (append; don't rewrite the
|
||||
# existing fields)
|
||||
note "writing build receipt to manifest.lock..."
|
||||
python3 - <<PY
|
||||
import yaml, os, hashlib
|
||||
from datetime import datetime, timezone
|
||||
|
||||
lock_path = "$lock"
|
||||
out_dir = "$local_out"
|
||||
build_host = "$BUILD_HOST"
|
||||
skipped = $SKIP_PUBLISH
|
||||
|
||||
lk = yaml.safe_load(open(lock_path))
|
||||
epoch = os.environ.get("SOURCE_DATE_EPOCH")
|
||||
if epoch:
|
||||
built_at = datetime.fromtimestamp(int(epoch), tz=timezone.utc).isoformat()
|
||||
else:
|
||||
built_at = datetime.now(tz=timezone.utc).isoformat()
|
||||
|
||||
pkgs = []
|
||||
for fn in sorted(os.listdir(out_dir)):
|
||||
if not fn.endswith(".pkg.tar.zst"): continue
|
||||
fp = os.path.join(out_dir, fn)
|
||||
b2 = hashlib.blake2b(open(fp, "rb").read()).hexdigest()
|
||||
pkgs.append({"name": fn, "size": os.path.getsize(fp), "b2sum": b2})
|
||||
|
||||
lk["build"] = {
|
||||
"built_at": built_at,
|
||||
"built_on_host": build_host,
|
||||
"ka_build_version": $VERSION,
|
||||
"published": (not skipped),
|
||||
"packages": pkgs,
|
||||
}
|
||||
yaml.dump(lk, open(lock_path, "w"), sort_keys=True, default_flow_style=False)
|
||||
print(f" receipt: {len(pkgs)} package(s), built_at={built_at}, published={not skipped}")
|
||||
PY
|
||||
|
||||
note "done."
|
||||
+39
-2
@@ -27,6 +27,7 @@ import argparse
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
@@ -37,6 +38,17 @@ VERSION = 1
|
||||
SCHEMA_VERSION = 1
|
||||
COVER_LETTER = "0000-cover-letter.patch"
|
||||
|
||||
# git format-patch trailer: "-- \n<MAJOR>.<MINOR>(.<PATCH>)?\n" at EOF,
|
||||
# possibly with trailing blank line(s). Strip from each source patch so
|
||||
# that the cumulative is always well-formed regardless of include order.
|
||||
# See issue #31.
|
||||
_TRAILER_RE = re.compile(rb'\n-- \n\d+\.\d+(?:\.\d+)?\n+\Z')
|
||||
|
||||
# Canonical separator emitted between concatenated patches in the
|
||||
# cumulative. Trailing blank line keeps patch(1) happy when the next
|
||||
# patch starts with "From <sha>".
|
||||
_CANONICAL_TRAILER = b'-- \n2.54.0\n\n'
|
||||
|
||||
|
||||
def die(msg, code=1):
|
||||
print(f"ka-promote: error: {msg}", file=sys.stderr)
|
||||
@@ -124,11 +136,36 @@ def resolve_includes(includes, patches_root):
|
||||
return resolved
|
||||
|
||||
|
||||
def strip_trailer(data):
|
||||
"""Strip any trailing git format-patch sentinel from a patch.
|
||||
|
||||
Accepts patches in either canonical shape:
|
||||
- WITH trailer: "...\n-- \n2.54.0\n\n"
|
||||
- WITHOUT trailer: "...\n" (already stripped)
|
||||
|
||||
Returns data ending in a single newline so the caller can either
|
||||
append a canonical trailer (mid-cumulative) or leave it bare (last).
|
||||
"""
|
||||
stripped = _TRAILER_RE.sub(b'\n', data)
|
||||
if not stripped.endswith(b'\n'):
|
||||
stripped += b'\n'
|
||||
return stripped
|
||||
|
||||
|
||||
def write_cumulative(resolved, out_path):
|
||||
with open(out_path, "wb") as out:
|
||||
for r in resolved:
|
||||
n = len(resolved)
|
||||
for i, r in enumerate(resolved):
|
||||
with open(r["src"], "rb") as src:
|
||||
out.write(src.read())
|
||||
data = src.read()
|
||||
data = strip_trailer(data)
|
||||
out.write(data)
|
||||
# Mid-cumulative patches need a separator so patch(1) knows
|
||||
# where they end and the next "From <sha>" begins. Last
|
||||
# patch stays bare — a trailing orphan sentinel reads as
|
||||
# the start of a malformed new patch at EOF (issue #31).
|
||||
if i != n - 1:
|
||||
out.write(_CANONICAL_TRAILER)
|
||||
with open(out_path, "rb") as f:
|
||||
b2 = hashlib.blake2b(f.read()).hexdigest()
|
||||
size = os.path.getsize(out_path)
|
||||
|
||||
+11
-4
@@ -53,12 +53,19 @@ includes:
|
||||
- board/coolpi-cm5-genbook/0005-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Enable-USB-C-PD-charging-via-FUSB302.patch
|
||||
- board/coolpi-cm5-genbook/0008-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Add-lid-switch-and-USB3-PHY-lane-config.patch
|
||||
- board/coolpi-cm5-genbook/0011-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-wire-internal-microphone.patch
|
||||
# VP9 enablement for RK3588 rkvdec (issue #12, closed 2026-05-18).
|
||||
# Cherry-picked from D.V.A.B. Sarma's add-rkvdec-vdpu381-vp9-v8 branch
|
||||
# at github.com/dvab-sarma/android_kernel_rk_opi. Bit-exact HW==SW==libva
|
||||
# verified at -ss 30 on bbb_60s_720p.vp9.webm via all three decode paths
|
||||
# (kdirect / SW / libva); sha c8624d7c42db66525f53a02a515bc38d0a17ef39f692660cc7bebb1e2d2e1b48.
|
||||
# Apply order is STRICT (0003 depends on the rkvdec-vp9-common refactor
|
||||
# added in 0002, which depends on the helper rename in 0001).
|
||||
# See patches/driver/media/README.md for provenance + removal criteria.
|
||||
- driver/media/0001-rkvdec-vp9-rename-get_ref_buf-to-get_ref_buf_vp9.patch
|
||||
- driver/media/0002-rkvdec-move-vp9-functions-to-common-file.patch
|
||||
- driver/media/0003-rkvdec-add-vp9-support-for-vdpu381-variant.patch
|
||||
|
||||
# Explicitly NOT included this round (tracked for later sprints):
|
||||
# - VP9 enablement for RK3588 rkvdec (issue #6 ask 2). /dev/video0 only
|
||||
# advertises S265 + S264 today; vainfo lists 9 profiles, target is
|
||||
# 10. Requires identifying the VDPU381/383 patch chain + possible
|
||||
# DTS additions. RFC-stage work, scope unclear until research lands.
|
||||
# - AV1 decoder integration (issue #6 ask 3). Kernel side is fine
|
||||
# (/dev/video4 advertises AV1F). Backend libva-v4l2-request-fourier
|
||||
# needs iter39 for a third fd. Backend work, not kernel.
|
||||
|
||||
+48
-32
@@ -1,6 +1,6 @@
|
||||
# kernel-agent manifest for ohm (PineTab2 / Rockchip RK3566 + BES2600 SDIO WiFi/BT)
|
||||
#
|
||||
# Status: scaffolding from 2026-05-16. Patches/scopes are mirrored;
|
||||
# Status: scaffolding from 2026-05-16; per-series patchset converged 2026-05-21 (pkgrel=6). Patches/scopes are mirrored;
|
||||
# the build pipeline (cumulative-patch generation, makepkg invocation,
|
||||
# sign+publish) still relies on the hand-managed flow in
|
||||
# boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/.
|
||||
@@ -25,40 +25,54 @@ baseline:
|
||||
|
||||
# Scope-tagged patch includes. Resolves to patches/<scope>/<file>.patch.
|
||||
#
|
||||
# Series-ordering note: the current cumulative-patch generation order on
|
||||
# boltzmann is A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H — explicitly
|
||||
# NOT alphabetical. ka-promote MUST honor an apply_order field when
|
||||
# concatenating series into the build's per-job cumulative patch. The
|
||||
# legend mapping series-letter → series-name lives in the current
|
||||
# danctnix-besser-pkgbuild changelog on boltzmann; promote to this
|
||||
# manifest once auto-generation is wired.
|
||||
# 2026-05-18 audit: the per-series -danctnix mirrors in
|
||||
# patches/driver/bes2600/*-danctnix/ created by kernel-agent#17 use
|
||||
# DKMS-style root paths (bes2600/foo.c) rather than in-tree staging
|
||||
# paths (drivers/staging/bes2600/foo.c), and at least one has corrupted
|
||||
# mixed-prefix headers (a/drivers/staging/bes2600/... b/bes2600/...).
|
||||
# They do NOT apply cleanly against the linux-pinetab2 baseline.
|
||||
#
|
||||
# DanctNIX siblings (-danctnix suffix) are selected here because ohm
|
||||
# runs on the DanctNIX kernel base; the non-suffixed variants exist for
|
||||
# vanilla mainline consumers that ohm doesn't currently have.
|
||||
|
||||
# 2026-05-21 update: per-series reconstruction (besser#22) completed
|
||||
# 2026-05-21; pkgrel=6 (srcversion 0E16463F) on ohm soak-passed with
|
||||
# the bounce-buffer + join-confirm-reset additions. The per-series
|
||||
# manifest below is the authoritative set; cumulative-c5x-danctnix
|
||||
# remains as historical fallback only.
|
||||
#
|
||||
# Until the per-series mirrors are reconstructed (kernel-agent followup
|
||||
# issue), the bes2600 driver scope is satisfied by a single-file
|
||||
# cumulative captured from the working hand-managed
|
||||
# danctnix-besser-pkgbuild flow on boltzmann (see
|
||||
# patches/driver/bes2600/cumulative-c5x-danctnix/README.md). This is
|
||||
# the c5x stack as it shipped in pkgrel=3 on 2026-05-18.
|
||||
includes:
|
||||
# Default-on series (uncontroversial fixes that ohm already runs):
|
||||
- driver/bes2600/staging-prep-series-danctnix/
|
||||
- driver/bes2600/pm-state-resync-danctnix/
|
||||
- driver/bes2600/pm-timeout-silence-danctnix/
|
||||
- driver/bes2600/pm-wake-consume-state-danctnix/
|
||||
- driver/bes2600/pm-gate-on-handshake/
|
||||
- driver/bes2600/pm-detect-firmware-unsupported-danctnix/
|
||||
- driver/bes2600/scan-defer-backoff-tune-danctnix/
|
||||
- driver/bes2600/scan-defer-on-reject-danctnix/
|
||||
- driver/bes2600/lmac-recover-via-mmc-hw-reset-danctnix/
|
||||
# bes2600 driver (c5x stack as shipped in pkgrel=3) — single-file
|
||||
# interim cumulative; per-series reconstruction tracked separately.
|
||||
- driver/bes2600/cumulative-c5x-danctnix/
|
||||
# close besser#1 — refuse multi-channel 5 GHz scans at driver boundary.
|
||||
- driver/bes2600/scan-filter-5ghz-danctnix/
|
||||
# GCC 15.2.1 build-fix for arm_neon.h + SHADOW_CALL_STACK interaction.
|
||||
# Runtime no-op as long as the config has CONFIG_SHADOW_CALL_STACK=n
|
||||
# (current ohm setting). Kept in the manifest for the day SCS gets
|
||||
# re-enabled. See reference_arm64_scs_arm_neon_gcc15 memory.
|
||||
- arch/arm64/scs-arm-neon-build-fix/
|
||||
# close besser#18 — pending_record_lock SOFTIRQ-safe -> -unsafe inversion.
|
||||
# Mirror of marfrit/bes2600-dkms#11 (d95453c). 5-site spin_lock -> _bh.
|
||||
- driver/bes2600/queue-pending-record-lock-bh-danctnix/
|
||||
# bounce-buffer fix for SDIO TX DMA OOB (KFENCE-detected on pkgrel=4 soak);
|
||||
# the per-series mirror of marfrit/bes2600-dkms bes2600/tx-sdio-dma-oob.
|
||||
# cumulative-c5x-danctnix did NOT include this — it was the regression
|
||||
# surfaced during the per-series reconstruction.
|
||||
- driver/bes2600/tx-sdio-dma-oob-danctnix/
|
||||
- driver/bes2600/factory-series/
|
||||
- driver/bes2600/factory-thread-dev/
|
||||
- driver/bes2600/factory-drop-kernel-write-danctnix/
|
||||
- driver/bes2600/drop-dpd-file-paths-danctnix/
|
||||
- driver/bes2600/drop-orphan-file-io-danctnix/
|
||||
- driver/bes2600/remove-chardev-user-interface/
|
||||
- driver/bes2600/enable-testmode/
|
||||
# close besser#25 — wsm_reset + serialised unjoin on JOIN reject.
|
||||
# cw1200 ancestor port (drivers/net/wireless/st/cw1200/sta.c:1339-1344)
|
||||
# with bes2600-specific PASSIVE-gate compensation. pkgrel=6 verified.
|
||||
- driver/bes2600/join-confirm-reset-danctnix/
|
||||
|
||||
# Explicitly NOT included (decision logged):
|
||||
# - debian-copyright-fsf-address: Debian packaging metadata, not kernel
|
||||
# - bare (non-danctnix) variants of the above: ohm runs DanctNIX base
|
||||
# - bare (non-danctnix) variants of the per-series mirrors: same
|
||||
# root-path bug as the -danctnix variants per the 2026-05-18 audit
|
||||
|
||||
config:
|
||||
source: hand-managed config file in boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/config
|
||||
@@ -91,9 +105,11 @@ verify:
|
||||
- wlan0 + bt0 (BT/UART) present after boot
|
||||
- sdio_force_uhs=0 not needed (DMA-OOB-read fix in tx-sdio-dma-oob series)
|
||||
bar4_per_patch_probe: opt-in
|
||||
bar5_burn_in: opt-in
|
||||
- WiFi: 24h iperf3 to LAN host without rxhang
|
||||
- PM: lid-close → wake cycles × 100 without bes2600 confirm-loss
|
||||
bar5_burn_in:
|
||||
mode: opt-in
|
||||
tests:
|
||||
- "WiFi: 24h iperf3 to LAN host without rxhang"
|
||||
- "PM: lid-close → wake cycles × 100 without bes2600 confirm-loss"
|
||||
|
||||
build_host:
|
||||
primary: boltzmann # native aarch64 with ohm's identical .config
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 11:42:00 +0200
|
||||
Subject: [PATCH] arm64: xor-neon: restore -ffixed-x18 when SHADOW_CALL_STACK=y
|
||||
(GCC 15+ build fix)
|
||||
|
||||
GCC 15.2.1 enforces that -fsanitize=shadow-call-stack requires
|
||||
-ffixed-x18 inside arm_neon.h's #pragma GCC target() blocks. The
|
||||
existing CFLAGS_REMOVE_xor-neon.o line strips the kernel-wide
|
||||
-ffixed-x18 (it's part of CC_FLAGS_NO_FPU) and CC_FLAGS_FPU does not
|
||||
restore it, so xor-neon.c fails to build on stricter GCC versions
|
||||
when CONFIG_SHADOW_CALL_STACK=y.
|
||||
|
||||
Add an explicit -ffixed-x18 just for this object, gated on the
|
||||
SCS config so non-SCS builds are unaffected.
|
||||
|
||||
Build environment workaround; not a kernel-runtime bug.
|
||||
---
|
||||
arch/arm64/lib/Makefile | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
|
||||
index 1234567..2345678 100644
|
||||
--- a/arch/arm64/lib/Makefile
|
||||
+++ b/arch/arm64/lib/Makefile
|
||||
@@ -9,6 +9,10 @@ ifeq ($(CONFIG_KERNEL_MODE_NEON), y)
|
||||
obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o
|
||||
CFLAGS_xor-neon.o += $(CC_FLAGS_FPU)
|
||||
CFLAGS_REMOVE_xor-neon.o += $(CC_FLAGS_NO_FPU)
|
||||
+# GCC 15+ enforces that -fsanitize=shadow-call-stack requires -ffixed-x18
|
||||
+# even after a #pragma GCC pop_options inside arm_neon.h. CC_FLAGS_REMOVE
|
||||
+# above strips the kernel-wide -ffixed-x18 (part of CC_FLAGS_NO_FPU); add
|
||||
+# it back here so xor-neon.c still compiles when SHADOW_CALL_STACK=y.
|
||||
+CFLAGS_xor-neon.o += $(if $(CONFIG_SHADOW_CALL_STACK),-ffixed-x18)
|
||||
endif
|
||||
|
||||
lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o
|
||||
+4322
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
# cumulative-c5x-danctnix — interim single-file cumulative
|
||||
|
||||
**Series role**: ohm manifest's bes2600 driver patchset, c5x stack as
|
||||
shipped in `linux-pinetab2-danctnix-besser` pkgrel=3 on 2026-05-18.
|
||||
|
||||
## Why this is a single .patch and not split per-fix
|
||||
|
||||
The 12-ish per-series mirror PR (kernel-agent#17) of the boltzmann-side
|
||||
`marfrit/besser` series produced patches with DKMS-style paths
|
||||
(`bes2600/*` at root) rather than in-tree staging paths
|
||||
(`drivers/staging/bes2600/*`), and at least one entry has corrupted
|
||||
mixed-prefix headers (`a/drivers/staging/bes2600/foo.c b/bes2600/foo.c`).
|
||||
Those series do NOT apply cleanly to the linux-pinetab2 baseline.
|
||||
|
||||
Audit performed 2026-05-18 during ohm migration:
|
||||
- ka-promote ohm (using the per-series includes) produces a
|
||||
172 644-byte cumulative touching 27 file paths, of which 11 are
|
||||
bogus DKMS-style or mixed-prefix.
|
||||
- The hand-curated `0001-bes2600-besser-cumulative-series.patch` from
|
||||
the working `danctnix-besser-pkgbuild` (boltzmann) is 148 149 bytes
|
||||
touching 48 distinct in-tree staging files — and is what pkgrel=3
|
||||
actually builds with.
|
||||
|
||||
This single-file cumulative is staged here so the ohm migration can
|
||||
ship through the kernel-agent flow today without first reconstructing
|
||||
12 series-dirs. The proper per-series split is tracked separately —
|
||||
see kernel-agent issue (TBD) for the rewrite.
|
||||
|
||||
## Provenance
|
||||
|
||||
- Source file: `boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch`
|
||||
- Reflects c5x driver state in `marfrit/bes2600-dkms-mobian` branch as
|
||||
of 2026-05-08, applied against `drivers/staging/bes2600/` in-tree.
|
||||
- Series legend (A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H — NOT
|
||||
alphabetical) per the danctnix-besser-pkgbuild changelog comments.
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
From 3d833f8ccf31895a2ce7bf4fd4ef839e653b29bb Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Thu, 21 May 2026 09:25:12 +0200
|
||||
Subject: [PATCH 22/22] bes2600: reset firmware state on wsm_join_confirm
|
||||
failure
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
When wsm_join_confirm() returns status != WSM_STATUS_SUCCESS (ret 1),
|
||||
the driver cleared its bookkeeping but did not reset the firmware
|
||||
interface, leaving it in an intermediate post-rejection state. A rapid
|
||||
second JOIN attempt (e.g. wpa_supplicant retrying after the
|
||||
PREV_AUTH_NOT_VALID deauth that mac80211 emits to clean up) hits an
|
||||
inconsistent firmware context, causing bes2600_sdio_read_rx_batch to
|
||||
return SDIO error which cascades into wifi_force_close:
|
||||
|
||||
wsm_join_confirm ret 1
|
||||
deauthenticating from <bssid> by local choice (Reason: 2=PREV_AUTH_NOT_VALID)
|
||||
[~10 min later]
|
||||
bes2600_sdio_read_rx_batch sdio read error
|
||||
WARNING: at bes2600_tx_loop_set_enable / bes2600_chrdev_wifi_force_close
|
||||
|
||||
Two additions to the failure path in bes2600_join_work():
|
||||
|
||||
1. wsm_reset (WSM_REQ_ID_RESET, 0x000A) with reset_statistics=false.
|
||||
This returns the firmware to IDLE so the next association attempt
|
||||
starts from a known-clean state. bes2600_unjoin_work() performs the
|
||||
same reset, but gates it on join_status != PASSIVE; after a failed
|
||||
JOIN join_status stays PASSIVE, so that path never fires — call
|
||||
wsm_reset directly here instead.
|
||||
|
||||
Contract: wsm_reset takes only wsm_cmd_lock (not conf_lock, not
|
||||
wsm_oper_lock). wsm_oper_unlock was already called inside
|
||||
wsm_join_confirm() before wsm_join() returned -EINVAL, so there is
|
||||
no re-entrancy hazard. conf_lock is held at this call site, which is
|
||||
compatible with wsm_reset's locking requirements.
|
||||
|
||||
2. queue_work(workqueue, &priv->unjoin_work) instead of direct
|
||||
wsm_unlock_tx(). Serialises the next association attempt through
|
||||
the workqueue so it cannot race against lingering firmware-side
|
||||
effects of the failure. If unjoin_work is already queued, release
|
||||
TX immediately (matching cw1200 ancestor sta.c:1344 comment "Tx lock
|
||||
still held, unjoin will clear it.").
|
||||
|
||||
Ancestor reference: drivers/net/wireless/st/cw1200/sta.c, function
|
||||
cw1200_join_work(), lines 1339-1344. cw1200 queues unjoin_work on join
|
||||
failure for the same reason. bes2600 needs the direct wsm_reset in
|
||||
addition because its unjoin_work has the join_status gate that cw1200's
|
||||
cw1200_do_unjoin() does not.
|
||||
|
||||
Signed-off-by: Claude (noether) <claude@reauktion.de>
|
||||
---
|
||||
bes2600/sta.c | 47 +++++++++++++++++++++++++++++++++++++++++++----
|
||||
1 file changed, 43 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
|
||||
index 476d875..bf86835 100644
|
||||
--- a/drivers/staging/bes2600/sta.c
|
||||
+++ b/drivers/staging/bes2600/sta.c
|
||||
@@ -2225,9 +2225,10 @@ void bes2600_join_work(struct work_struct *work)
|
||||
struct wsm_template_frame probe_tmp = {
|
||||
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
|
||||
};
|
||||
- /*struct wsm_reset reset = {
|
||||
- .reset_statistics = true,
|
||||
- };*/
|
||||
+ struct wsm_reset join_fail_reset = {
|
||||
+ .reset_statistics = false,
|
||||
+ };
|
||||
+ bool join_failed = false;
|
||||
|
||||
|
||||
BUG_ON(queueId >= 4);
|
||||
@@ -2410,6 +2411,33 @@ void bes2600_join_work(struct work_struct *work)
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
cancel_delayed_work_sync(&priv->join_timeout);
|
||||
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
|
||||
+ /*
|
||||
+ * Firmware rejected WSM_JOIN (wsm_join_confirm ret 1).
|
||||
+ * Issue wsm_reset so the firmware returns to a clean
|
||||
+ * IDLE state before the next association attempt.
|
||||
+ *
|
||||
+ * Without this reset the firmware sits in an
|
||||
+ * intermediate post-reject state. A rapid second
|
||||
+ * JOIN (e.g. wpa_supplicant retrying after the
|
||||
+ * PREV_AUTH_NOT_VALID deauth that follows) hits an
|
||||
+ * inconsistent firmware context, causing
|
||||
+ * bes2600_sdio_read_rx_batch to return SDIO error
|
||||
+ * which cascades into wifi_force_close.
|
||||
+ *
|
||||
+ * cw1200 ancestor (drivers/net/wireless/st/cw1200/
|
||||
+ * sta.c:1339) queues unjoin_work on join failure for
|
||||
+ * the same reason; bes2600_unjoin_work gates its
|
||||
+ * wsm_reset on join_status != PASSIVE, so after a
|
||||
+ * failed JOIN (join_status stays PASSIVE) that path
|
||||
+ * never fires — call wsm_reset directly here instead.
|
||||
+ *
|
||||
+ * Contract: wsm_reset takes only wsm_cmd_lock; safe
|
||||
+ * to call while conf_lock is held. wsm_oper_unlock
|
||||
+ * was already called in wsm_join_confirm() before
|
||||
+ * wsm_join() returned the error.
|
||||
+ */
|
||||
+ WARN_ON(wsm_reset(hw_priv, &join_fail_reset, priv->if_id));
|
||||
+ join_failed = true;
|
||||
} else {
|
||||
/* Upload keys */
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
@@ -2434,7 +2462,18 @@ void bes2600_join_work(struct work_struct *work)
|
||||
up(&hw_priv->conf_lock);
|
||||
if (bss)
|
||||
cfg80211_put_bss(hw_priv->hw->wiphy, bss);
|
||||
- wsm_unlock_tx(hw_priv);
|
||||
+ /*
|
||||
+ * On join failure: queue unjoin_work so the next association
|
||||
+ * attempt is serialised after any lingering cleanup, matching
|
||||
+ * cw1200 sta.c:1344 "Tx lock still held, unjoin will clear it."
|
||||
+ * If unjoin_work is already queued, release TX immediately.
|
||||
+ */
|
||||
+ if (join_failed) {
|
||||
+ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
|
||||
+ wsm_unlock_tx(hw_priv);
|
||||
+ } else {
|
||||
+ wsm_unlock_tx(hw_priv);
|
||||
+ }
|
||||
}
|
||||
|
||||
void bes2600_join_timeout(struct work_struct *work)
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# bes2600/join-confirm-reset-danctnix
|
||||
|
||||
Danctnix-flavor patch closing besser#25 (wsm_join_confirm failure cascade).
|
||||
|
||||
## What it does
|
||||
|
||||
When firmware returns status 1 on a JOIN command (`wsm_join_confirm ret 1`),
|
||||
add a direct `wsm_reset(...)` call so the firmware returns to a clean IDLE
|
||||
state, plus `queue_work(workqueue, &priv->unjoin_work)` for serialisation of
|
||||
the next association attempt.
|
||||
|
||||
## Why it's a fork-divergence fix
|
||||
|
||||
`cw1200_join_work()` (cw1200 ancestor, `drivers/net/wireless/st/cw1200/sta.c:1339-1344`)
|
||||
queues `unjoin_work` on join failure: `cw1200_do_unjoin()` calls `wsm_reset`
|
||||
when `join_status == STA`.
|
||||
|
||||
bes2600's `bes2600_unjoin_work()` gates the same `wsm_reset` on
|
||||
`join_status != PASSIVE`. After a failed JOIN, `join_status` stays PASSIVE
|
||||
(only set to STA on success) — queuing `unjoin_work` alone is insufficient
|
||||
on bes2600. The danctnix variant carries a direct `wsm_reset` in the
|
||||
failure path *and* the queue_work serialisation.
|
||||
|
||||
## Observable effects (pkgrel=6 soak)
|
||||
|
||||
Beyond closing the cascade (besser#25 acceptance), this patch also
|
||||
collapsed the periodic ~600 ms latency jitter on ohm:
|
||||
|
||||
| | pkgrel=5 | pkgrel=6 |
|
||||
|---|---|---|
|
||||
| max RTT | 612 ms | 13.9 ms |
|
||||
| mdev | 103.5 ms | 1.55 ms |
|
||||
|
||||
The bgscan-driven roam-attempt to a 5 GHz BSSID followed by `wsm_join`
|
||||
reject was briefly stalling TX every minute even when the cascade did
|
||||
not fire.
|
||||
|
||||
## Upstream
|
||||
|
||||
- besser issue: marfrit/besser#25
|
||||
- bes2600-dkms branch (Mobian flavor): bes2600/wsm-join-confirm-reset
|
||||
(PR #12 against `cleanups`)
|
||||
- bes2600-dkms branch (danctnix flavor): bes2600/join-confirm-failure-reset
|
||||
(top commit `3d833f8`)
|
||||
- shipped as patch 0022 in danctnix-besser-pkgbuild kernel/ (pkgrel=6,
|
||||
srcversion 0E16463FA8D85F4704DE93F)
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
From d95453c98e31d7a47bc227aef5d0b426ac9e334b Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 16:58:49 +0200
|
||||
Subject: [PATCH] =?UTF-8?q?bes2600:=20take=20pending=5Frecord=5Flock=20wit?=
|
||||
=?UTF-8?q?h=20=5Fbh()=20to=20fix=20SOFTIRQ-safe=20=E2=86=92=20-unsafe=20i?=
|
||||
=?UTF-8?q?nversion=20(besser#18)?=
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
PROVE_LOCKING reports:
|
||||
|
||||
WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected
|
||||
kworker/u16:1 is trying to acquire:
|
||||
&hw_priv->tx_loop.pending_record_lock at bes2600_queue_clear+0x80
|
||||
and this task is already holding:
|
||||
&queue->lock at bes2600_queue_clear+0x60
|
||||
|
||||
which would create a new lock dependency:
|
||||
(&queue->lock){+.-.} -> (&hw_priv->tx_loop.pending_record_lock){+.+.}
|
||||
|
||||
but this new dependency connects a SOFTIRQ-irq-safe lock:
|
||||
(&queue->lock){+.-.}
|
||||
... which became SOFTIRQ-irq-safe at:
|
||||
bes2600_tx -> ieee80211_handle_wake_tx_queue -> tasklet_action
|
||||
to a SOFTIRQ-irq-unsafe lock:
|
||||
(&hw_priv->tx_loop.pending_record_lock){+.+.}
|
||||
... which became SOFTIRQ-irq-unsafe at:
|
||||
bes2600_queue_get_skb -> bes2600_join_work -> process_one_work
|
||||
|
||||
queue->lock is taken consistently with spin_lock_bh() at 22 sites;
|
||||
the nested acquisition of pending_record_lock at queue.c:289 (inside
|
||||
the outer queue->lock_bh held at line 285) had it implicitly BH-safe
|
||||
via the outer scope. But pending_record_lock is ALSO taken from
|
||||
non-BH-disabled contexts:
|
||||
|
||||
bes2600_queue_get_skb (queue.c:832) — process context via
|
||||
bes2600_join_work (workqueue), no outer queue->lock held
|
||||
bes2600_tx_loop_item_pending_check (tx_loop.c:112)
|
||||
— TX-loop context, no outer
|
||||
queue->lock held
|
||||
|
||||
When CPU0 holds pending_record_lock from one of those non-BH paths
|
||||
and a softirq fires that wants queue->lock, and CPU1 in softirq has
|
||||
queue->lock and is about to acquire pending_record_lock — classic AB-BA
|
||||
SOFTIRQ deadlock.
|
||||
|
||||
The fix is the conservative one: take pending_record_lock with _bh()
|
||||
at every site that's not already inside a queue->lock_bh-held scope.
|
||||
That makes the lock consistently SOFTIRQ-safe, eliminating the
|
||||
inversion. queue.c:289/295 stays as plain spin_lock because BH is
|
||||
already disabled by the outer queue->lock_bh acquired at queue.c:285.
|
||||
|
||||
Five sites converted:
|
||||
bes2600/queue.c:832 -- spin_lock -> spin_lock_bh
|
||||
bes2600/queue.c:839 -- spin_unlock -> spin_unlock_bh
|
||||
bes2600/queue.c:844 -- spin_unlock -> spin_unlock_bh
|
||||
bes2600/tx_loop.c:112 -- spin_lock -> spin_lock_bh
|
||||
bes2600/tx_loop.c:114 -- spin_unlock -> spin_unlock_bh
|
||||
|
||||
Contract:
|
||||
- Documentation/locking/locktypes.rst spelling: spin_lock_bh() is
|
||||
the canonical way to make a non-IRQ spinlock safe against
|
||||
softirq preemption that might re-enter the same lock.
|
||||
- Same shape as queue->lock in this driver and as is_drv->lock
|
||||
in the cw1200 ancestor.
|
||||
|
||||
Closes: besser#18
|
||||
Fixes: <bes2600 base import>
|
||||
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
---
|
||||
bes2600/queue.c | 6 +++---
|
||||
bes2600/tx_loop.c | 4 ++--
|
||||
2 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c
|
||||
index cc606c1..4016b76 100644
|
||||
--- a/drivers/staging/bes2600/queue.c
|
||||
+++ b/drivers/staging/bes2600/queue.c
|
||||
@@ -829,19 +829,19 @@ int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
|
||||
- spin_lock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
+ spin_lock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) {
|
||||
list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) {
|
||||
if (record_item->packetID == packetID) {
|
||||
list_del(&record_item->head);
|
||||
dev_kfree_skb(record_item->skb);
|
||||
kfree(record_item);
|
||||
- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
+ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
+ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
|
||||
item = &queue->pool[item_id];
|
||||
|
||||
diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c
|
||||
index e6cf072..0cf7ce1 100644
|
||||
--- a/drivers/staging/bes2600/tx_loop.c
|
||||
+++ b/drivers/staging/bes2600/tx_loop.c
|
||||
@@ -109,9 +109,9 @@ void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn)
|
||||
bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i],
|
||||
bes2600_tx_loop_item_pending_item);
|
||||
}
|
||||
- spin_lock(&hw_priv->tx_loop.pending_record_lock);
|
||||
+ spin_lock_bh(&hw_priv->tx_loop.pending_record_lock);
|
||||
bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item);
|
||||
- spin_unlock(&hw_priv->tx_loop.pending_record_lock);
|
||||
+ spin_unlock_bh(&hw_priv->tx_loop.pending_record_lock);
|
||||
|
||||
if (atomic_read(&hw_priv->bh_rx) > 0)
|
||||
wake_up(&hw_priv->bh_wq);
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# queue-pending-record-lock-bh-danctnix — close besser#18
|
||||
|
||||
Converts `pending_record_lock` to `spin_lock_bh()` at the 5 sites
|
||||
where it is taken in non-BH-disabled contexts (`bes2600_queue_get_skb`
|
||||
called from `bes2600_join_work`, and `bes2600_tx_loop_item_pending_check`).
|
||||
|
||||
Eliminates the PROVE_LOCKING SOFTIRQ-safe → SOFTIRQ-unsafe warning
|
||||
reported in besser#18: `&queue->lock` (taken with `_bh` everywhere,
|
||||
including the nested acquisition at `queue.c:289` that holds
|
||||
`pending_record_lock` as inner) was registered SOFTIRQ-irq-safe by
|
||||
lockdep, but `pending_record_lock` was sometimes taken without BH
|
||||
disable, creating an AB-BA deadlock window.
|
||||
|
||||
Provenance:
|
||||
- Source-of-truth commit on `marfrit/bes2600-dkms` branch
|
||||
`bes2600/queue-pending-record-lock-bh-fix`, commit `d95453c`.
|
||||
- This file is the same commit's `git format-patch` output with
|
||||
the DKMS-style `bes2600/foo.c` paths rewritten to in-tree
|
||||
`drivers/staging/bes2600/foo.c` paths via sed.
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 11:27:40 +0200
|
||||
Subject: [PATCH 1/2] bes2600: filter 5 GHz scans at the driver boundary
|
||||
(besser#1)
|
||||
|
||||
The BES2600 firmware refuses WSM start-scan for 5 GHz with status 2
|
||||
("rejected by policy"). This shows up in dmesg as the recurring
|
||||
|
||||
wsm_generic_confirm failed for request 0x0007.
|
||||
[SCAN] Scan failed (-22).
|
||||
|
||||
pattern (besser issue #1, ~14-16/h on ohm/PineTab2 baseline).
|
||||
|
||||
Trace shows every reject is the second of a back-to-back pair: mac80211
|
||||
splits multi-band hw_scan requests per band when the driver does not
|
||||
set IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't), then re-invokes
|
||||
drv_hw_scan from __ieee80211_scan_completed for each subsequent band.
|
||||
The 2.4 GHz iteration succeeds; the 5 GHz iteration is what the
|
||||
firmware rejects. See ieee80211_prep_hw_scan in net/mac80211/scan.c
|
||||
for the loop, and the existing memory reference_bes2600_5ghz_scan_reject
|
||||
for the firmware behaviour.
|
||||
|
||||
The 056a71a defer-on-reject patch already in this tree handles the
|
||||
BT-A2DP-coex branch and the consecutive-reject backoff, but it cannot
|
||||
prevent the per-band-loop reject: by the time defer_should_scan is
|
||||
consulted, the per-band call is already in flight, and the reject_count
|
||||
gets reset on every successful 2.4 GHz scan in between (which is
|
||||
~36% of attempts), so the threshold never trips.
|
||||
|
||||
The fix: refuse the 5 GHz iteration upfront in bes2600_hw_scan. The
|
||||
2.4 GHz scan still runs normally. The 5 GHz portion is reported as
|
||||
aborted to userspace -- same outcome as today, minus the dmesg storm
|
||||
and the wsm_generic_confirm WARN cascade.
|
||||
|
||||
5 GHz band registration is intentionally left in place: direct-BSSID
|
||||
association to a known 5 GHz AP still works (no scan is needed for
|
||||
that path), and a future firmware update that fixes the scan behaviour
|
||||
should not be foreclosed by changing band advertisement.
|
||||
|
||||
Contract: per include/net/mac80211.h ieee80211_ops.hw_scan, a negative
|
||||
return aborts the scan without requiring ieee80211_scan_completed().
|
||||
-EOPNOTSUPP is the semantically accurate code (operation is legal,
|
||||
driver can't service it on this band today).
|
||||
|
||||
Phase 3 evidence:
|
||||
- baseline N=3: rate ~14.3-23.6/h converged at 14.3/h (matches OP)
|
||||
- back-to-back scan gap: 6/6 rejected pairs <200us, 1/1 successful
|
||||
pair was 114ms (single-band-only, no 5 GHz leg)
|
||||
- defer log fires: 0/9 in 30-min window (056a71a structurally bypassed)
|
||||
|
||||
Predicted Phase 7 delta: Pattern A 14/h -> 0/h.
|
||||
---
|
||||
bes2600/scan.c | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||
index fb1d298..a81afb6 100644
|
||||
--- a/drivers/staging/bes2600/scan.c
|
||||
+++ b/drivers/staging/bes2600/scan.c
|
||||
@@ -238,6 +238,28 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||
/* Scan when P2P_GO corrupt firmware MiniAP mode */
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
return -EOPNOTSUPP;
|
||||
+
|
||||
+ /*
|
||||
+ * Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected
|
||||
+ * by policy"); see besser issue #1. mac80211 splits multi-band
|
||||
+ * hw_scan requests per-band when the driver does not set
|
||||
+ * IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see
|
||||
+ * ieee80211_hw_set() calls in bes2600_main.c), so each per-band call
|
||||
+ * has req->channels[] from one band only (see ieee80211_prep_hw_scan
|
||||
+ * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
|
||||
+ * boundary so userspace gets a clean aborted-scan for that portion
|
||||
+ * rather than waiting for the firmware reject to cascade up. 5 GHz
|
||||
+ * band registration stays intact so direct-BSSID association to a
|
||||
+ * known 5 GHz AP still works (no scan needed for that path).
|
||||
+ *
|
||||
+ * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
|
||||
+ * documentation, a negative return aborts the scan without requiring
|
||||
+ * ieee80211_scan_completed().
|
||||
+ */
|
||||
+ if (req->n_channels > 0 &&
|
||||
+ req->channels[0]->band == NL80211_BAND_5GHZ)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
#if 0
|
||||
if (work_pending(&priv->offchannel_work) ||
|
||||
(hw_priv->roc_if_id != -1)) {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
|
||||
From 8cd10f487c8144d462a510812ba0fa717b3e24df Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 15:56:34 +0200
|
||||
Subject: [PATCH 2/2] bes2600: scan-filter-5ghz: allow targeted single-channel
|
||||
scans (besser#1 follow-up)
|
||||
|
||||
The original Patch I refused EVERY 5 GHz scan request unconditionally
|
||||
(req->n_channels > 0 && band == NL80211_BAND_5GHZ). This eliminated
|
||||
the Pattern A storm but also broke 5 GHz association entirely:
|
||||
NM / wpa_supplicant iterates a freq_list when a connection profile
|
||||
specifies 802-11-wireless.band=a, issuing per-frequency single-channel
|
||||
scans to find the BSS before associating. Those single-channel scans
|
||||
were also refused by our guard, so the BSS was never seen and
|
||||
'Wi-Fi network could not be found' was the only outcome.
|
||||
|
||||
Tighten the guard: refuse only multi-channel 5 GHz scans (n_channels
|
||||
> 1), which is the per-band-sweep pattern mac80211 issues internally
|
||||
and the only one that triggers the firmware storm at the per-band
|
||||
loop boundary. Single-channel 5 GHz scans pass through to firmware,
|
||||
which generally accepts them -- and when they happen to be rejected,
|
||||
the failure is isolated and doesn't cascade.
|
||||
|
||||
Verified on ohm with pkgrel=3 (srcversion BEB625FA7443171EA8D55F7):
|
||||
- Pattern A count since boot: 0 (Phase 7 prediction still holds)
|
||||
- iw dev wlan0 scan freq 5180 -> allowed
|
||||
- iw dev wlan0 scan freq 5180 5200 ... -> refused -EOPNOTSUPP
|
||||
- NM 'nmcli connection up' with band=a -> associated to BSSID
|
||||
c0:25:06:e6:5b:33 on 5240 MHz / ch.48 in ~1 second
|
||||
- TX bitrate 150 Mbit/s MCS 7 40MHz short-GI (vs 72.2 Mbit/s
|
||||
HT20 on 2.4 GHz) -- ~2x throughput recovered
|
||||
|
||||
The change is a single byte (> 0 -> > 1) plus comment update; the
|
||||
test confirmation above is what motivates it.
|
||||
|
||||
Refs: besser#1 (closed but tracked for follow-up like this), original
|
||||
Patch I sha 093a503.
|
||||
---
|
||||
bes2600/scan.c | 16 ++++++++++++----
|
||||
1 file changed, 12 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||
index a81afb6..497523b 100644
|
||||
--- a/drivers/staging/bes2600/scan.c
|
||||
+++ b/drivers/staging/bes2600/scan.c
|
||||
@@ -248,15 +248,23 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||
* has req->channels[] from one band only (see ieee80211_prep_hw_scan
|
||||
* in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
|
||||
* boundary so userspace gets a clean aborted-scan for that portion
|
||||
- * rather than waiting for the firmware reject to cascade up. 5 GHz
|
||||
- * band registration stays intact so direct-BSSID association to a
|
||||
- * known 5 GHz AP still works (no scan needed for that path).
|
||||
+ * rather than waiting for the firmware reject to cascade up.
|
||||
+ *
|
||||
+ * Only the multi-channel case is refused (n_channels > 1): that's
|
||||
+ * the per-band-sweep pattern mac80211 issues internally and the
|
||||
+ * one that triggers the firmware storm at the per-band loop
|
||||
+ * boundary. Single-channel 5 GHz scans (BSS verification, NM's
|
||||
+ * per-freq iteration when 802-11-wireless.band=a is set) pass
|
||||
+ * through to firmware, which generally accepts them since the
|
||||
+ * storm is the back-to-back per-band issue, not a blanket 5 GHz
|
||||
+ * reject. This preserves 5 GHz association via the
|
||||
+ * "wpa_supplicant iterates freq_list per channel" path.
|
||||
*
|
||||
* Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
|
||||
* documentation, a negative return aborts the scan without requiring
|
||||
* ieee80211_scan_completed().
|
||||
*/
|
||||
- if (req->n_channels > 0 &&
|
||||
+ if (req->n_channels > 1 &&
|
||||
req->channels[0]->band == NL80211_BAND_5GHZ)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# scan-filter-5ghz-danctnix — close besser#1
|
||||
|
||||
Refuses multi-channel 5 GHz scan requests at the driver boundary with
|
||||
`-EOPNOTSUPP`, eliminating the WSM 0x0007 reject storm. Single-channel
|
||||
5 GHz scans still pass through (NM `802-11-wireless.band=a` BSS
|
||||
verification path stays functional).
|
||||
|
||||
Phase 7 baseline on ohm: Pattern A 14.3/h → 0/h (verified 2026-05-18,
|
||||
30 min window). 5 GHz association achieves 150 Mbit/s MCS 7 HT40 SGI vs
|
||||
72.2 on 2.4 GHz.
|
||||
|
||||
Single combined patch file because the two commits in the source
|
||||
(initial filter + `n_channels > 1` refinement) form a 2-commit
|
||||
follow-up series and git apply concatenation handles both. Splitting
|
||||
into two .patch files would mean a fragile dependency on cross-file
|
||||
sequencing inside the same series-dir.
|
||||
|
||||
Provenance: closes besser#1. Mirror of source-of-truth in
|
||||
`marfrit/bes2600-dkms` branch `bes2600/scan-filter-5ghz`.
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 11:27:40 +0200
|
||||
Subject: [PATCH 1/2] bes2600: filter 5 GHz scans at the driver boundary
|
||||
(besser#1)
|
||||
|
||||
The BES2600 firmware refuses WSM start-scan for 5 GHz with status 2
|
||||
("rejected by policy"). This shows up in dmesg as the recurring
|
||||
|
||||
wsm_generic_confirm failed for request 0x0007.
|
||||
[SCAN] Scan failed (-22).
|
||||
|
||||
pattern (besser issue #1, ~14-16/h on ohm/PineTab2 baseline).
|
||||
|
||||
Trace shows every reject is the second of a back-to-back pair: mac80211
|
||||
splits multi-band hw_scan requests per band when the driver does not
|
||||
set IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't), then re-invokes
|
||||
drv_hw_scan from __ieee80211_scan_completed for each subsequent band.
|
||||
The 2.4 GHz iteration succeeds; the 5 GHz iteration is what the
|
||||
firmware rejects. See ieee80211_prep_hw_scan in net/mac80211/scan.c
|
||||
for the loop, and the existing memory reference_bes2600_5ghz_scan_reject
|
||||
for the firmware behaviour.
|
||||
|
||||
The 056a71a defer-on-reject patch already in this tree handles the
|
||||
BT-A2DP-coex branch and the consecutive-reject backoff, but it cannot
|
||||
prevent the per-band-loop reject: by the time defer_should_scan is
|
||||
consulted, the per-band call is already in flight, and the reject_count
|
||||
gets reset on every successful 2.4 GHz scan in between (which is
|
||||
~36% of attempts), so the threshold never trips.
|
||||
|
||||
The fix: refuse the 5 GHz iteration upfront in bes2600_hw_scan. The
|
||||
2.4 GHz scan still runs normally. The 5 GHz portion is reported as
|
||||
aborted to userspace -- same outcome as today, minus the dmesg storm
|
||||
and the wsm_generic_confirm WARN cascade.
|
||||
|
||||
5 GHz band registration is intentionally left in place: direct-BSSID
|
||||
association to a known 5 GHz AP still works (no scan is needed for
|
||||
that path), and a future firmware update that fixes the scan behaviour
|
||||
should not be foreclosed by changing band advertisement.
|
||||
|
||||
Contract: per include/net/mac80211.h ieee80211_ops.hw_scan, a negative
|
||||
return aborts the scan without requiring ieee80211_scan_completed().
|
||||
-EOPNOTSUPP is the semantically accurate code (operation is legal,
|
||||
driver can't service it on this band today).
|
||||
|
||||
Phase 3 evidence:
|
||||
- baseline N=3: rate ~14.3-23.6/h converged at 14.3/h (matches OP)
|
||||
- back-to-back scan gap: 6/6 rejected pairs <200us, 1/1 successful
|
||||
pair was 114ms (single-band-only, no 5 GHz leg)
|
||||
- defer log fires: 0/9 in 30-min window (056a71a structurally bypassed)
|
||||
|
||||
Predicted Phase 7 delta: Pattern A 14/h -> 0/h.
|
||||
---
|
||||
bes2600/scan.c | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||
index fb1d298..a81afb6 100644
|
||||
--- a/drivers/staging/bes2600/scan.c
|
||||
+++ b/drivers/staging/bes2600/scan.c
|
||||
@@ -238,6 +238,28 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||
/* Scan when P2P_GO corrupt firmware MiniAP mode */
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
return -EOPNOTSUPP;
|
||||
+
|
||||
+ /*
|
||||
+ * Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected
|
||||
+ * by policy"); see besser issue #1. mac80211 splits multi-band
|
||||
+ * hw_scan requests per-band when the driver does not set
|
||||
+ * IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see
|
||||
+ * ieee80211_hw_set() calls in bes2600_main.c), so each per-band call
|
||||
+ * has req->channels[] from one band only (see ieee80211_prep_hw_scan
|
||||
+ * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
|
||||
+ * boundary so userspace gets a clean aborted-scan for that portion
|
||||
+ * rather than waiting for the firmware reject to cascade up. 5 GHz
|
||||
+ * band registration stays intact so direct-BSSID association to a
|
||||
+ * known 5 GHz AP still works (no scan needed for that path).
|
||||
+ *
|
||||
+ * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
|
||||
+ * documentation, a negative return aborts the scan without requiring
|
||||
+ * ieee80211_scan_completed().
|
||||
+ */
|
||||
+ if (req->n_channels > 0 &&
|
||||
+ req->channels[0]->band == NL80211_BAND_5GHZ)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
#if 0
|
||||
if (work_pending(&priv->offchannel_work) ||
|
||||
(hw_priv->roc_if_id != -1)) {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
|
||||
From 8cd10f487c8144d462a510812ba0fa717b3e24df Mon Sep 17 00:00:00 2001
|
||||
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||
Date: Mon, 18 May 2026 15:56:34 +0200
|
||||
Subject: [PATCH 2/2] bes2600: scan-filter-5ghz: allow targeted single-channel
|
||||
scans (besser#1 follow-up)
|
||||
|
||||
The original Patch I refused EVERY 5 GHz scan request unconditionally
|
||||
(req->n_channels > 0 && band == NL80211_BAND_5GHZ). This eliminated
|
||||
the Pattern A storm but also broke 5 GHz association entirely:
|
||||
NM / wpa_supplicant iterates a freq_list when a connection profile
|
||||
specifies 802-11-wireless.band=a, issuing per-frequency single-channel
|
||||
scans to find the BSS before associating. Those single-channel scans
|
||||
were also refused by our guard, so the BSS was never seen and
|
||||
'Wi-Fi network could not be found' was the only outcome.
|
||||
|
||||
Tighten the guard: refuse only multi-channel 5 GHz scans (n_channels
|
||||
> 1), which is the per-band-sweep pattern mac80211 issues internally
|
||||
and the only one that triggers the firmware storm at the per-band
|
||||
loop boundary. Single-channel 5 GHz scans pass through to firmware,
|
||||
which generally accepts them -- and when they happen to be rejected,
|
||||
the failure is isolated and doesn't cascade.
|
||||
|
||||
Verified on ohm with pkgrel=3 (srcversion BEB625FA7443171EA8D55F7):
|
||||
- Pattern A count since boot: 0 (Phase 7 prediction still holds)
|
||||
- iw dev wlan0 scan freq 5180 -> allowed
|
||||
- iw dev wlan0 scan freq 5180 5200 ... -> refused -EOPNOTSUPP
|
||||
- NM 'nmcli connection up' with band=a -> associated to BSSID
|
||||
c0:25:06:e6:5b:33 on 5240 MHz / ch.48 in ~1 second
|
||||
- TX bitrate 150 Mbit/s MCS 7 40MHz short-GI (vs 72.2 Mbit/s
|
||||
HT20 on 2.4 GHz) -- ~2x throughput recovered
|
||||
|
||||
The change is a single byte (> 0 -> > 1) plus comment update; the
|
||||
test confirmation above is what motivates it.
|
||||
|
||||
Refs: besser#1 (closed but tracked for follow-up like this), original
|
||||
Patch I sha 093a503.
|
||||
---
|
||||
bes2600/scan.c | 16 ++++++++++++----
|
||||
1 file changed, 12 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||
index a81afb6..497523b 100644
|
||||
--- a/drivers/staging/bes2600/scan.c
|
||||
+++ b/drivers/staging/bes2600/scan.c
|
||||
@@ -248,15 +248,23 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||
* has req->channels[] from one band only (see ieee80211_prep_hw_scan
|
||||
* in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
|
||||
* boundary so userspace gets a clean aborted-scan for that portion
|
||||
- * rather than waiting for the firmware reject to cascade up. 5 GHz
|
||||
- * band registration stays intact so direct-BSSID association to a
|
||||
- * known 5 GHz AP still works (no scan needed for that path).
|
||||
+ * rather than waiting for the firmware reject to cascade up.
|
||||
+ *
|
||||
+ * Only the multi-channel case is refused (n_channels > 1): that's
|
||||
+ * the per-band-sweep pattern mac80211 issues internally and the
|
||||
+ * one that triggers the firmware storm at the per-band loop
|
||||
+ * boundary. Single-channel 5 GHz scans (BSS verification, NM's
|
||||
+ * per-freq iteration when 802-11-wireless.band=a is set) pass
|
||||
+ * through to firmware, which generally accepts them since the
|
||||
+ * storm is the back-to-back per-band issue, not a blanket 5 GHz
|
||||
+ * reject. This preserves 5 GHz association via the
|
||||
+ * "wpa_supplicant iterates freq_list per channel" path.
|
||||
*
|
||||
* Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
|
||||
* documentation, a negative return aborts the scan without requiring
|
||||
* ieee80211_scan_completed().
|
||||
*/
|
||||
- if (req->n_channels > 0 &&
|
||||
+ if (req->n_channels > 1 &&
|
||||
req->channels[0]->band == NL80211_BAND_5GHZ)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bash
|
||||
# ka-build test suite — dry-run paths only.
|
||||
#
|
||||
# Phase-1 deliverable per issue #34. The full makepkg path is exercised
|
||||
# manually on boltzmann (parity test against the most recent hand-built
|
||||
# linux-fresnel-fourier pkg); not in this suite because:
|
||||
# - Needs real ssh to boltzmann + ~30 min build wall time
|
||||
# - Hermetic sandbox would need a mock marfrit-publish-arch on hertz
|
||||
# Future-work: add a `--mock-build-host` flag + fixture builder so this
|
||||
# can run in CI.
|
||||
#
|
||||
# What this suite covers:
|
||||
# - Argument parsing + required-host check
|
||||
# - manifest.yaml read + package.name / build_host.primary extraction
|
||||
# - Refuses if manifest.lock missing (ka-promote not run)
|
||||
# - Refuses if PKGBUILD missing
|
||||
# - Refuses on patch drift between kernel-agent and marfrit-packages
|
||||
# - Happy-path dry-run on fresnel (all 6 patches match)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
packages_repo="${PACKAGES_REPO_FOR_TESTS:-${HOME}/src/marfrit-packages}"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
results=()
|
||||
note() { printf ' %s\n' "$*"; }
|
||||
ok() { results+=("PASS $1"); pass=$((pass+1)); note "PASS"; }
|
||||
ko() { results+=("FAIL $1: $2"); fail=$((fail+1)); note "FAIL: $2"; }
|
||||
|
||||
# Reset build/ before running so we exercise the "no manifest.lock yet" path
|
||||
rm -rf "$repo_root/build/fresnel"
|
||||
|
||||
echo
|
||||
echo "Running ka-build test suite from $repo_root"
|
||||
echo
|
||||
|
||||
# ----- 1. requires host arg -----
|
||||
echo "::: requires host arg"
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -eq 2 ] && echo "$out" | grep -q "host is required"; then ok "requires host arg"; else ko "requires host arg" "exit=$rc out=$out"; fi
|
||||
|
||||
# ----- 2. unknown flag -----
|
||||
echo "::: unknown flag rejected"
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" fresnel --nonsense 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -ne 0 ] && echo "$out" | grep -q "unknown flag"; then ok "unknown flag rejected"; else ko "unknown flag rejected" "exit=$rc out=$out"; fi
|
||||
|
||||
# ----- 3. refuses if manifest.lock missing -----
|
||||
echo "::: refuses if manifest.lock missing (ka-promote not run)"
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" fresnel --dry-run --packages-repo "$packages_repo" 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -eq 2 ] && echo "$out" | grep -q "no manifest.lock"; then ok "refuses no-lock"; else ko "refuses no-lock" "exit=$rc out=$out"; fi
|
||||
|
||||
# Now run ka-promote so the rest can proceed
|
||||
"$repo_root/bin/ka-promote" fresnel >/dev/null
|
||||
|
||||
# ----- 4. refuses if PKGBUILD missing -----
|
||||
echo "::: refuses if PKGBUILD missing (--packages-repo wrong)"
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" fresnel --dry-run --packages-repo /tmp/non-existent-mp 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -eq 2 ]; then ok "refuses bad packages-repo"; else ko "refuses bad packages-repo" "exit=$rc out=$out"; fi
|
||||
|
||||
# ----- 5. happy-path dry-run -----
|
||||
echo "::: happy-path dry-run (fresnel, real packages-repo)"
|
||||
if [ ! -f "$packages_repo/arch/linux-fresnel-fourier/PKGBUILD" ]; then
|
||||
note "SKIP: $packages_repo/arch/linux-fresnel-fourier/PKGBUILD not present"
|
||||
results+=("SKIP happy-path dry-run (PKGBUILD missing locally)")
|
||||
else
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" fresnel --dry-run --packages-repo "$packages_repo" 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -eq 0 ] && echo "$out" | grep -q "patches OK (6 files)"; then ok "happy-path dry-run"; else ko "happy-path dry-run" "exit=$rc out=$out"; fi
|
||||
fi
|
||||
|
||||
# ----- 6. patch drift detection -----
|
||||
echo "::: patch drift detection (mutate a copied patch, expect exit 3)"
|
||||
if [ ! -d "$packages_repo/arch/linux-fresnel-fourier" ]; then
|
||||
note "SKIP: $packages_repo/arch/linux-fresnel-fourier not present"
|
||||
results+=("SKIP patch drift detection")
|
||||
else
|
||||
sandbox=$(mktemp -d -t ka-build-drift.XXXXXX)
|
||||
cp -r "$packages_repo/arch/linux-fresnel-fourier" "$sandbox/linux-fresnel-fourier"
|
||||
mkdir -p "$sandbox/arch"
|
||||
mv "$sandbox/linux-fresnel-fourier" "$sandbox/arch/linux-fresnel-fourier"
|
||||
# Mutate one patch so its sha256 differs from manifest.lock's recorded sha
|
||||
echo "drift" >> "$sandbox/arch/linux-fresnel-fourier/0001-arm64-dts-rk3399-pinebook-pro-add-OC-OPP-tables-1704-2184.patch"
|
||||
set +e
|
||||
out=$("$repo_root/bin/ka-build" fresnel --dry-run --packages-repo "$sandbox" 2>&1)
|
||||
rc=$?
|
||||
set -e
|
||||
rm -rf "$sandbox"
|
||||
if [ "$rc" -eq 3 ] && echo "$out" | grep -q "DRIFT:"; then ok "patch drift detection"; else ko "patch drift detection" "exit=$rc out=$out"; fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "===================="
|
||||
printf '%s\n' "${results[@]}"
|
||||
echo "===================="
|
||||
echo "passed: $pass"
|
||||
echo "failed: $fail"
|
||||
[ "$fail" -eq 0 ] || exit 1
|
||||
@@ -12,8 +12,10 @@ set -euo pipefail
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
fixtures="${repo_root}/tests/ka-promote/fixtures"
|
||||
|
||||
# Phase-3 ground truth — recorded 2026-05-18, fresnel cumulative b2sum.
|
||||
FRESNEL_EXPECTED_B2SUM=4d9d93c655ea701b587bf1383c794f41b1aeb3bc32bca69ce3488852ec2c1474a2f47585608598b39ac05671490b8df63c5bc7d093f87e1afd5a92f908891b67
|
||||
# Phase-3 ground truth — re-recorded 2026-05-19 after issue #31 fix
|
||||
# (write_cumulative now strips per-input trailers + emits canonical
|
||||
# separators between, but not after, concatenated patches).
|
||||
FRESNEL_EXPECTED_B2SUM=9c21751cc48ab57cdf48058cc4309752de169c567bbb898c342ff3e4a5cc79add53e3fd4217c2ae2ae7c16b0f19518cf1791907367e1ea9ef16458e1e90c05e0
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
@@ -110,6 +112,47 @@ echo
|
||||
echo "Running ka-promote test suite from $repo_root"
|
||||
echo
|
||||
|
||||
# ----- unit: strip_trailer + write_cumulative shape (issue #31) -----
|
||||
echo "::: strip_trailer + cumulative shape (issue #31)"
|
||||
python3 - "$repo_root" <<'PY'
|
||||
import importlib.util, pathlib, sys, tempfile, os
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
from importlib.machinery import SourceFileLoader
|
||||
mod = SourceFileLoader("ka_promote", str(root/"bin"/"ka-promote")).load_module()
|
||||
|
||||
# strip_trailer accepts both shapes and yields newline-terminated body
|
||||
assert mod.strip_trailer(b"...body...\n-- \n2.54.0\n\n") == b"...body...\n"
|
||||
assert mod.strip_trailer(b"...body...\n-- \n2.53.0\n\n") == b"...body...\n"
|
||||
assert mod.strip_trailer(b"...body...\n-- \n2.20\n\n") == b"...body...\n"
|
||||
assert mod.strip_trailer(b"...body...\n") == b"...body...\n"
|
||||
assert mod.strip_trailer(b"...body...") == b"...body...\n"
|
||||
# Multiple trailing blanks after the version still strip
|
||||
assert mod.strip_trailer(b"x\n-- \n2.54.0\n\n\n") == b"x\n"
|
||||
|
||||
# write_cumulative: 3 inputs (mix of with-/without-trailer), check ordering
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
p1 = os.path.join(d, "a.patch"); open(p1,"wb").write(b"PA\n-- \n2.54.0\n\n")
|
||||
p2 = os.path.join(d, "b.patch"); open(p2,"wb").write(b"PB\n") # already bare
|
||||
p3 = os.path.join(d, "c.patch"); open(p3,"wb").write(b"PC\n-- \n2.40.1\n\n")
|
||||
out = os.path.join(d, "out.patch")
|
||||
resolved = [{"src": p1}, {"src": p2}, {"src": p3}]
|
||||
mod.write_cumulative(resolved, out)
|
||||
body = open(out,"rb").read()
|
||||
assert body == b"PA\n-- \n2.54.0\n\nPB\n-- \n2.54.0\n\nPC\n", repr(body)
|
||||
# Last patch (PC) must NOT carry an orphan trailer at EOF
|
||||
assert not body.rstrip(b"\n").endswith(b"2.40.1"), \
|
||||
f"last patch's trailer leaked into cumulative: {body[-40:]!r}"
|
||||
print("PASS")
|
||||
PY
|
||||
if [ $? -eq 0 ]; then
|
||||
results+=("PASS strip_trailer + cumulative shape (issue #31)")
|
||||
pass=$((pass+1))
|
||||
else
|
||||
results+=("FAIL strip_trailer + cumulative shape (issue #31)")
|
||||
fail=$((fail+1))
|
||||
fi
|
||||
echo
|
||||
|
||||
# Use the real fleet/fresnel.yaml — copy into a sandbox so the test is hermetic.
|
||||
mkdir -p /tmp/ka-promote-parity-fixture
|
||||
cp "$repo_root/fleet/fresnel.yaml" /tmp/ka-promote-parity-fixture/fresnel.yaml
|
||||
|
||||
Reference in New Issue
Block a user