Files
monok8s/docs/ask-deepresearch-4.md
2026-04-30 22:05:23 +08:00

13 KiB
Raw Blame History

filter-kconfig-fragment.sh for Reproducible Kernel-Config Filtering

Executive summary

I did not find a file named filter-kconfig-fragment.sh in the uploaded project files or in the supplied ASK/kernel tarballs. What is present is the standard kernel-side machinery you would use around such a filter: the Linux kernels own scripts/kconfig/merge_config.sh for fragment merging, scripts/config for imperative .config edits, and documented Kconfig controls such as KCONFIG_WARN_UNKNOWN_SYMBOLS, KCONFIG_WERROR, and KCONFIG_ALLCONFIG. The kernel tree itself also uses merge_config.sh followed by olddefconfig when it builds from config fragments. citeturn3view1turn0search2turn3view2

A small pre-filter is still useful, because neither merge_config.sh nor scripts/config is meant to silently rewrite a fragment for a different kernel generation. merge_config.sh merges and warns; scripts/config edits .config; Kconfig enforces symbol existence and dependency rules. When a fragment contains a symbol that simply does not exist in the target tree, such as CONFIG_NETFILTER_XTABLES_LEGACY in your reported 6.12-based tree, a pre-filter avoids an avoidable mismatch by dropping only the absent symbol while preserving ordering, comments, and all valid settings. That is the narrow job of the replacement script below. citeturn3view1turn3view2turn3view6

The replacement script in this report is POSIX sh, offline-safe, requires only standard Unix tools (find, grep, sed, mktemp, cat), validates inputs, checks for scripts/kconfig/merge_config.sh, preserves comments and ordering, emits only symbols that exist in the target Kconfig tree, and exits non-zero if nothing usable remains after filtering. It is designed specifically for Docker builder stages and reproducible kernel-config flows. citeturn3view1turn3view2turn3view4

What I found and what to use instead

The Linux kernel already ships the two main utilities you should treat as authoritative.

merge_config.sh is the standard fragment-merging tool in the kernel tree. Its own header says it “takes a list of config fragment values, and merges them one by one,” and that it warns about overridden values and symbols that did not make it into the final .config because of dependencies or symbol removal. The kernel build system also invokes scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) ... and then runs olddefconfig for fragment-based config targets. citeturn3view1turn0search2

scripts/config is the standard imperative editor for .config. Its built-in usage text documents --enable, --disable, --module, --set-str, --set-val, --undefine, --state, and --file, and also states that it does not validate .config immediately; that validation happens at the next make step. The kernel docs also contain concrete examples of ./scripts/config -e ... and -m ... being used to enable required options. citeturn3view0turn3view5

Kconfig itself exposes the exact controls you want for drift detection: KCONFIG_WARN_UNKNOWN_SYMBOLS warns on unrecognized config symbols, KCONFIG_WERROR turns those warnings into errors, and KCONFIG_ALLCONFIG provides a documented “mini-config” mechanism for all*config targets. The same docs also recommend make listnewconfig to surface newly introduced symbols. citeturn3view2turn5view0

For external module builds, the kernel docs also matter here: modules_prepare can prepare a tree for external modules, but it does not produce Module.symvers if CONFIG_MODVERSIONS matters, so a full kernel build is still required in that case. That is the main reason a strict builder pipeline should validate kernel config and preparation before invoking out-of-tree module builds. citeturn3view4

Minimal portable replacement script

Assumptions for this script:

  • KERNEL_DIR is a Linux kernel source tree root.
  • The tree contains scripts/kconfig/merge_config.sh.
  • Config symbols are defined in Kconfig* files somewhere under KERNEL_DIR.
  • The fragment format is standard Kconfig fragment syntax: CONFIG_FOO=... and # CONFIG_FOO is not set.
  • The builder environment has POSIX sh, find, grep, sed, mktemp, cat, and rm.

scripts/filter-kconfig-fragment.sh

#!/bin/sh
# filter-kconfig-fragment.sh
#
# Purpose:
#   Emit an "effective" Kconfig fragment that preserves comments and ordering
#   but drops CONFIG symbols that do not exist in the target kernel tree.
#
# Usage:
#   filter-kconfig-fragment.sh KERNEL_DIR FRAGMENT_FILE > effective.config
#
# Exit codes:
#   2  invalid usage / missing required files
#   3  no Kconfig files were found in KERNEL_DIR
#   4  fragment had no surviving CONFIG lines after filtering

set -eu

usage() {
    echo "usage: $0 KERNEL_DIR FRAGMENT_FILE" >&2
    exit 2
}

[ "$#" -eq 2 ] || usage

KERNEL_DIR=$1
FRAGMENT_FILE=$2
MERGE_SCRIPT=$KERNEL_DIR/scripts/kconfig/merge_config.sh

[ -d "$KERNEL_DIR" ] || {
    echo "error: KERNEL_DIR is not a directory: $KERNEL_DIR" >&2
    exit 2
}

[ -r "$FRAGMENT_FILE" ] || {
    echo "error: FRAGMENT_FILE is not readable: $FRAGMENT_FILE" >&2
    exit 2
}

[ -s "$FRAGMENT_FILE" ] || {
    echo "error: FRAGMENT_FILE is empty: $FRAGMENT_FILE" >&2
    exit 2
}

[ -r "$MERGE_SCRIPT" ] || {
    echo "error: required kernel utility missing: $MERGE_SCRIPT" >&2
    exit 2
}

TMP_KCONFIGS=$(mktemp)
TMP_OUT=$(mktemp)
trap 'rm -f "$TMP_KCONFIGS" "$TMP_OUT"' EXIT HUP INT TERM

# Index all Kconfig files once. Searching the whole tree is simpler and more
# robust than hardcoding a small directory subset.
find "$KERNEL_DIR" -type f \( -name 'Kconfig' -o -name 'Kconfig*' \) | sort > "$TMP_KCONFIGS"

[ -s "$TMP_KCONFIGS" ] || {
    echo "error: no Kconfig files found under $KERNEL_DIR" >&2
    exit 3
}

symbol_exists() {
    # Accept either CONFIG_FOO or FOO.
    sym=$1
    case "$sym" in
        CONFIG_*) sym=${sym#CONFIG_} ;;
    esac

    # We treat both "config FOO" and "menuconfig FOO" as symbol definitions.
    # GNU grep and busybox grep both support -E.
    while IFS= read -r kf; do
        if grep -Eq "^[[:space:]]*(menu)?config[[:space:]]+$sym([[:space:]]|\$)" "$kf"; then
            return 0
        fi
    done < "$TMP_KCONFIGS"

    return 1
}

kept_symbols=0

while IFS= read -r line || [ -n "$line" ]; do
    # Preserve blank lines exactly.
    if [ -z "$line" ]; then
        printf '\n' >> "$TMP_OUT"
        continue
    fi

    case "$line" in
        '# 'CONFIG_*' is not set')
            # Example: "# CONFIG_FOO is not set"
            sym=$(printf '%s\n' "$line" | sed -n 's/^# \(CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*\) is not set$/\1/p')
            if [ -n "$sym" ] && symbol_exists "$sym"; then
                printf '%s\n' "$line" >> "$TMP_OUT"
                kept_symbols=$((kept_symbols + 1))
            fi
            ;;
        CONFIG_*=*)
            # Example: "CONFIG_FOO=y" or "CONFIG_BAR=\"abc\""
            sym=${line%%=*}
            if symbol_exists "$sym"; then
                printf '%s\n' "$line" >> "$TMP_OUT"
                kept_symbols=$((kept_symbols + 1))
            fi
            ;;
        '#'*)
            # Preserve comments and headings.
            printf '%s\n' "$line" >> "$TMP_OUT"
            ;;
        *)
            # Preserve any other non-empty line verbatim; fragments normally
            # shouldn't contain these, but preserving them is safer than rewriting.
            printf '%s\n' "$line" >> "$TMP_OUT"
            ;;
    esac
done < "$FRAGMENT_FILE"

if [ "$kept_symbols" -eq 0 ]; then
    echo "error: no CONFIG entries remain after filtering $FRAGMENT_FILE" >&2
    exit 4
fi

cat "$TMP_OUT"

This script is intentionally narrow. It does not try to solve dependency resolution, force values, or replace merge_config.sh. It only answers one question: “Does this symbol exist anywhere in the target kernels Kconfig tree?” That is the right preflight step when a fragment spans multiple kernel generations and you need a portable Docker-safe filter before Kconfig proper enforces dependencies. citeturn3view1turn3view2turn3view6

Integration examples

Dockerfile usage

COPY scripts/filter-kconfig-fragment.sh /usr/local/bin/filter-kconfig-fragment.sh
COPY docker/kernel-extra.config /tmp/kernel-extra.config

RUN chmod +x /usr/local/bin/filter-kconfig-fragment.sh && \
    test -d /opt/kernel && \
    /usr/local/bin/filter-kconfig-fragment.sh /opt/kernel /tmp/kernel-extra.config \
      > /tmp/kernel-extra.effective.config && \
    /opt/kernel/scripts/kconfig/merge_config.sh -m /opt/kernel/.config /tmp/kernel-extra.effective.config && \
    KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 \
    make -C /opt/kernel olddefconfig && \
    make -C /opt/kernel modules_prepare

If CONFIG_MODVERSIONS=y or your external modules depend on Module.symvers, replace the final modules_prepare with a full kernel build step for the relevant targets. The kernel docs explicitly warn that modules_prepare does not produce Module.symvers. citeturn3view4

Makefile usage

KERNEL_DIR      ?= /opt/kernel
KERNEL_FRAGMENT ?= docker/kernel-extra.config
KERNEL_EFFECTIVE?= build/kernel-extra.effective.config
KCONFIG_FILTER  ?= scripts/filter-kconfig-fragment.sh

$(KERNEL_EFFECTIVE): $(KERNEL_FRAGMENT) $(KCONFIG_FILTER)
	mkdir -p $(dir $@)
	$(KCONFIG_FILTER) $(KERNEL_DIR) $(KERNEL_FRAGMENT) > $@

kernel-merge: $(KERNEL_EFFECTIVE)
	$(KERNEL_DIR)/scripts/kconfig/merge_config.sh -m $(KERNEL_DIR)/.config $(KERNEL_EFFECTIVE)
	KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 $(MAKE) -C $(KERNEL_DIR) olddefconfig

kernel-prepare: kernel-merge
	$(MAKE) -C $(KERNEL_DIR) modules_prepare

Why the filter is needed

This filter exists to handle kernel-version mismatches in fragments, not to bypass Kconfig. A fragment can be perfectly valid for kernel line A and partially invalid for kernel line B because symbols were renamed, split, removed, or introduced later. Your concrete example, CONFIG_NETFILTER_XTABLES_LEGACY, is exactly that class of problem: the symbol may be meaningful in one kernel generation and absent in another. Filtering it out before merge_config.sh and olddefconfig prevents a false failure from one stale line while still allowing Kconfig to validate all remaining, real symbols and dependencies. Kconfigs own docs explain that symbols live in a dependency-aware tree and that visibility and legality depend on those relationships, which is why the filter should check existence only and leave dependency resolution to Kconfig itself. citeturn3view6turn3view2

flowchart LR
    A[kernel-extra.config] --> B[filter-kconfig-fragment.sh]
    B --> C[kernel-extra.effective.config]
    C --> D[merge_config.sh -m .config effective.fragment]
    D --> E[make olddefconfig]
    E --> F[make modules_prepare or full kernel build]

Quick checks and validation commands

Use these checks before or after wiring the script into Docker:

Check whether a symbol exists in the target tree

grep -Rnw --include='Kconfig*' \
  -E '^[[:space:]]*(menu)?config[[:space:]]+NETFILTER_XTABLES_LEGACY([[:space:]]|$)' \
  "$KERNEL_DIR" || echo "missing"

Show which lines survive filtering

scripts/filter-kconfig-fragment.sh "$KERNEL_DIR" docker/kernel-extra.config \
  | grep -E '^(CONFIG_|# CONFIG_)'

Confirm the filtered fragment is non-empty

scripts/filter-kconfig-fragment.sh "$KERNEL_DIR" docker/kernel-extra.config \
  > /tmp/kernel-extra.effective.config && \
test -s /tmp/kernel-extra.effective.config

Validate after merge and olddefconfig

"$KERNEL_DIR"/scripts/kconfig/merge_config.sh -m "$KERNEL_DIR"/.config /tmp/kernel-extra.effective.config
KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 \
make -C "$KERNEL_DIR" olddefconfig

Inspect config drift

make -C "$KERNEL_DIR" listnewconfig

The kernel docs explicitly recommend listnewconfig when older configs are reused against newer kernels, and they document KCONFIG_WARN_UNKNOWN_SYMBOLS / KCONFIG_WERROR for exactly the “unknown symbol in config input” problem this workflow is guarding against. citeturn5view0turn3view2

Sources and notes

I did not find filter-kconfig-fragment.sh in the provided project files, so the replacement above is intentionally conservative and built around official kernel interfaces instead of inventing a parallel config system. The authoritative equivalents are: the kernels own scripts/kconfig/merge_config.sh, whose header states that it merges config fragments and warns about overridden or unresolved values; scripts/config, whose built-in usage documents --enable, --disable, --module, --file, and related options; the Kconfig documentation for KCONFIG_WARN_UNKNOWN_SYMBOLS, KCONFIG_WERROR, KCONFIG_ALLCONFIG, and listnewconfig; the Kconfig language docs for symbol definitions and dependency semantics; and the external-modules docs for modules_prepare and the Module.symvers caveat. The reproducible-builds kernel docs are also relevant when the surrounding Docker pipeline sets SOURCE_DATE_EPOCH, KBUILD_BUILD_TIMESTAMP, KBUILD_BUILD_USER, and KBUILD_BUILD_HOST. citeturn3view1turn3view0turn3view2turn5view0turn3view6turn3view4turn3view3