* [PATCH v1 01/10] kconfig-checker: Add schema for rule files
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 02/10] kconfig-checker: Introduce PREEMPT_RT configuration rules Ahmed S. Darwish
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
The kconfig-checker YAML rule files describes which kernel .config symbols
are expected to be enabled or disabled. Introduce a schema for such files.
Describe and add rational for each element in the schema as well.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/schema.yml | 115 +++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
create mode 100644 tools/kconfig-checker/schema.yml
diff --git a/tools/kconfig-checker/schema.yml b/tools/kconfig-checker/schema.yml
new file mode 100644
index 000000000000..a7f1b0a78cc7
--- /dev/null
+++ b/tools/kconfig-checker/schema.yml
@@ -0,0 +1,115 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+"$schema": "https://json-schema.org/draft-07/schema"
+
+title: Schema for kconfig-checker rule files
+type: object
+required: [rules]
+additionalProperties: false
+
+properties:
+ #
+ # Optional base URL for the documentation page used by rationale
+ # links. See `docs_hint`.
+ #
+ docs_url:
+ type: string
+ format: uri
+
+ rules:
+ type: array
+ minItems: 1
+ items:
+ type: object
+ oneOf:
+ - required: [name]
+ - required: [name_pattern]
+ required: [expected, severity]
+ additionalProperties: false
+
+ properties:
+ #
+ # Exact kernel configuration symbol to check.
+ #
+ # Example: CONFIG_PREEMPT_RT
+ #
+ name:
+ type: string
+ pattern: '^CONFIG_[A-Z0-9_]+$'
+
+ #
+ # Regex matching one or more kernel configuration symbols.
+ #
+ # Example: CONFIG_DEBUG_.*
+ #
+ name_pattern:
+ type: string
+ format: regex
+
+ #
+ # Expected value for the matching kernel configuration option.
+ #
+ # Values:
+ #
+ # y option is expected to be enabled (built-in or module).
+ # n option is expected to be disabled or unset.
+ # allowed option is permitted and must not be reported as a violation.
+ # This is used to exempt a specific symbol from a broader
+ # `name_pattern` rule with `expected: n`.
+ #
+ expected:
+ type: string
+ enum: [y, n, allowed]
+
+ #
+ # Issue severity to report when the configuration rule is not met.
+ #
+ # Higher severities should be reserved for rules affecting correctness.
+ #
+ severity:
+ type: string
+ enum: [fatal, high, medium, low, info]
+
+ #
+ # Optional documentation anchor for this rule.
+ #
+ # The rationale URL is built from `docs_url` plus an HTML fragment
+ # identifier.
+ #
+ # If `docs_hint` is set to `config`, then the HTML fragment id is
+ # derived from the kernel configuration symbol name:
+ #
+ # CONFIG_DRM -> #config-drm
+ #
+ # For example, with below docs_url:
+ #
+ # https://docs.kernel.org/core-api/real-time/kernel-configuration.html
+ #
+ # and the symbol entry:
+ #
+ # - name: CONFIG_DRM
+ # expected: n
+ # severity: info
+ # docs_hint: config
+ #
+ # the rationale URL becomes:
+ #
+ # https://docs.kernel.org/core-api/real-time/kernel-configuration.html#config-drm
+ #
+ # If `docs_hint` is any other string, that string is used directly as
+ # the HTML fragment identifier.
+ #
+ docs_hint:
+ type: string
+ anyOf:
+ - const: config
+ - pattern: '^\S+$' # URI fragments cannot have spaces
+
+ #
+ # Optional human-readable explanation for the rule.
+ #
+ # This text should explain why the option is expected to have the given
+ # value, and may span multiple lines.
+ #
+ rationale:
+ type: string
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 02/10] kconfig-checker: Introduce PREEMPT_RT configuration rules
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 01/10] kconfig-checker: Add schema for rule files Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 03/10] kconfig-checker: Add the configuration checker code Ahmed S. Darwish
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Introduce a kconfig-checker PREEMPT_RT rules file, matching what is at
Documentation/core-api/real-time/kernel-configuration.rst.
This will allow the tool to mechanically check the kernel's .config at a
later step.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/rules/preempt_rt.yml | 169 +++++++++++++++++++++
1 file changed, 169 insertions(+)
create mode 100644 tools/kconfig-checker/rules/preempt_rt.yml
diff --git a/tools/kconfig-checker/rules/preempt_rt.yml b/tools/kconfig-checker/rules/preempt_rt.yml
new file mode 100644
index 000000000000..5c1b80adc846
--- /dev/null
+++ b/tools/kconfig-checker/rules/preempt_rt.yml
@@ -0,0 +1,169 @@
+# PREEMPT_RT .config sanitization
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# List all config options in alphabetic order, per group
+---
+
+docs_url: https://docs.kernel.org/core-api/real-time/kernel-configuration.html
+rules:
+ - name: CONFIG_CPU_FREQ
+ expected: y
+ severity: high
+ docs_hint: config
+
+ - name: CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE
+ expected: y
+ severity: high
+ docs_hint: config
+
+ - name_pattern: CONFIG_CPU_FREQ_GOV_(CONSERVATIVE|ONDEMAND|POWERSAVE|USERSPACE)
+ expected: n
+ severity: medium
+ docs_hint: non-performance-cpu-frequency-governors
+
+ - name: CONFIG_CPU_FREQ_GOV_SCHEDUTIL
+ expected: n
+ severity: medium
+ docs_hint: non-performance-cpu-frequency-governors
+
+ - name: CONFIG_CPU_IDLE
+ expected: y
+ severity: info
+ docs_hint: config
+
+ - name: CONFIG_DRM
+ expected: n
+ severity: info
+ docs_hint: config
+
+ - name: CONFIG_EFI_DISABLE_RUNTIME
+ expected: y
+ severity: medium
+ docs_hint: config
+
+ - name: CONFIG_NO_HZ
+ expected: n
+ severity: medium
+ docs_hint: config-no-hz-config-no-hz-full
+
+ - name: CONFIG_NO_HZ_FULL
+ expected: n
+ severity: medium
+ docs_hint: config-no-hz-config-no-hz-full
+
+ - name: CONFIG_PREEMPT_RT
+ expected: y
+ severity: fatal
+ docs_hint: config
+
+ #
+ # Tracing
+ #
+
+ - name: CONFIG_DYNAMIC_FTRACE
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_FUNCTION_GRAPH_TRACER
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_HWLAT_TRACER
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_IRQSOFF_TRACER
+ expected: n
+ severity: high
+ docs_hint: config-irqsoff-tracer-and-config-preempt-tracer
+
+ - name: CONFIG_KPROBE_EVENTS
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_PREEMPT_TRACER
+ expected: n
+ severity: high
+ docs_hint: config-irqsoff-tracer-and-config-preempt-tracer
+
+ - name: CONFIG_SCHED_TRACER
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_TRACING
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ - name: CONFIG_UPROBE_EVENTS
+ expected: y
+ severity: info
+ docs_hint: config-tracing-and-tracing-options
+
+ #
+ # Debugging
+ #
+
+ - name_pattern: CONFIG_DEBUG_.*
+ expected: n
+ severity: medium
+ docs_hint: kernel-debug-options
+
+ - name: CONFIG_DEBUG_ATOMIC_SLEEP
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_BOOT_PARAMS
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_BUGVERBOSE
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_FS
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_FS_ALLOW_ALL
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_INFO
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_INFO_NONE
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_KERNEL
+ expected: allowed
+ severity: low
+
+ - name: CONFIG_DEBUG_MISC
+ expected: allowed
+ severity: low
+ rationale:
+ A menu option which enables the display of other debug symbols.
+
+ - name: CONFIG_LOCKUP_DETECTOR
+ expected: n
+ severity: high
+ docs_hint: config
+
+ - name: CONFIG_PROVE_LOCKING
+ expected: n
+ severity: high
+ docs_hint: config
+
+ - name_pattern: CONFIG_SLUB_DEBUG.*
+ expected: n
+ severity: high
+ docs_hint: kernel-debug-options
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 03/10] kconfig-checker: Add the configuration checker code
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 01/10] kconfig-checker: Add schema for rule files Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 02/10] kconfig-checker: Introduce PREEMPT_RT configuration rules Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 04/10] kconfig: Introduce checkconfig target Ahmed S. Darwish
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Introduce a kernel configuration checker.
Validate the passed kernel .config against a YAML rules file and report
problematic configuration issues by severity.
For correctness, readability, and long-term maintainability, type-annotate
all the Python code and split the tool's logic into a clean set of classes.
Create a proper Python objects data model for the kconfig checker to work
with. Confine all direct YAML dictionary access to YAML files loading,
validation, and data model construction.
Document the tool's development rules on top.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/check.py | 558 +++++++++++++++++++++++++++++++++
1 file changed, 558 insertions(+)
create mode 100755 tools/kconfig-checker/check.py
diff --git a/tools/kconfig-checker/check.py b/tools/kconfig-checker/check.py
new file mode 100755
index 000000000000..476cd13e59eb
--- /dev/null
+++ b/tools/kconfig-checker/check.py
@@ -0,0 +1,558 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Kernel .config checker
+#
+# Main usage:
+#
+# $ check.py list-rules
+# $ check.py check -c path/to/linux/.config -r preempt_rt
+#
+# Notes:
+#
+# - All code is to be fully type-annotated. Before introducing changes,
+# please verify type-safety using:
+#
+# $ mypy --strict --no-warn-return-any check.py
+#
+# - The minimum required Python version must not be newer than what is
+# required by the kernel build at Documentation/process/changes.rst.
+#
+# - The tool must not depend on being run inside the kernel source tree.
+# This simplifies invocation, testing and possible future packaging.
+#
+# - The YAML rule files are only treated as a serialization format. After
+# loading and schema validation, such rules are parsed into an internal
+# data model: Severity, Rule, SymbolRule, PatternRule, Rules, and other
+# related python objects.
+#
+# All checker logic must operate on that typed model, and not on the raw
+# YAML dictionaries.
+#
+# - Minimize external dependencies: this tool is also intended to be run
+# from distribution kernel-package scripts in the future.
+#
+# Script exit codes:
+#
+# os.EX_CONFIG passed kernel .config does not match rules
+# os.EX_NOINPUT input file(s) could not be opened
+# os.EX_DATAERR YAML/schema/rule data is malformed
+# os.EX_UNAVAILABLE Python version or dependency is unavailable
+#
+
+from __future__ import annotations
+
+import argparse
+import os
+import re
+import sys
+import textwrap
+
+from dataclasses import dataclass
+from enum import Enum, auto
+from pathlib import Path
+from typing import Any, NoReturn, Optional
+
+#
+# Initial sanity checks:
+#
+
+def error(message: str, code: int) -> NoReturn:
+ print(f'Error: {message}', file=sys.stderr)
+ sys.exit(code)
+
+if sys.version_info < (3, 9):
+ error('Python 3.9 or later is required', os.EX_UNAVAILABLE)
+
+try:
+ import jsonschema # type: ignore
+ import yaml as yaml_m # type: ignore
+except ModuleNotFoundError as e:
+ error(f'Missing Python module: {e.name}', os.EX_UNAVAILABLE)
+
+#
+# Actual script:
+#
+
+class KernelConfig:
+ """.config file parser"""
+
+ def __init__(self, file: Path):
+ try:
+ self.lines = file.read_text().splitlines()
+ except OSError as e:
+ error(f'Could not open {file}: {e.strerror}', os.EX_NOINPUT)
+
+ self.symbols = self._parse_symbols()
+
+ def _parse_symbols(self) -> dict[str, str]:
+ """Extract CONFIG_* symbol assignments from .config"""
+ symbol_set = re.compile(r'^(CONFIG_[A-Za-z0-9_]+)=([ym])$')
+ symbol_unset = re.compile(r'^# (CONFIG_[A-Za-z0-9_]+) is not set$')
+
+ symbols: dict[str, str] = {}
+ for line in self.lines:
+ line = line.rstrip('\n')
+
+ if m := symbol_set.match(line):
+ symbols[m.group(1)] = m.group(2)
+ continue
+
+ if m := symbol_unset.match(line):
+ symbols[m.group(1)] = 'n'
+
+ return symbols
+
+ def get_symbol_value(self, sym_name: str) -> Optional[str]:
+ """Return a .config symbol value: 'y', 'm', or 'n'"""
+ return self.symbols.get(sym_name)
+
+ def get_matching_symbols(self, pa: re.Pattern[str]) -> list[str]:
+ """Get all CONFIG_* symbol names matching passed regex"""
+ return [ sym for sym in self.symbols if pa.search(sym) ]
+
+YamlDict = dict[str, Any]
+
+class Helpers:
+ """YAML file access helpers"""
+
+ @staticmethod
+ def load_yaml(yaml_f: Path) -> YamlDict:
+ try:
+ with open(yaml_f, 'r') as f:
+ return yaml_m.safe_load(f)
+ except OSError as e:
+ error(f'Could not open {yaml_f}: {e.strerror}', os.EX_NOINPUT)
+ except yaml_m.YAMLError as e:
+ error(f'Invalid YAML syntax at {yaml_f}:\n {e}', os.EX_DATAERR)
+
+ @staticmethod
+ def validate_yaml(yaml: YamlDict, schema: YamlDict, f_name: str) -> None:
+ try:
+ jsonschema.validate(yaml, schema)
+ except jsonschema.exceptions.ValidationError as e:
+ error(
+ f'{f_name} does not match its schema\n'
+ f'Reason: {e.message}',
+ os.EX_DATAERR
+ )
+
+class Schema:
+ """Rules file schema, in json-schema.org format"""
+
+ def __init__(self, schema_f: Path):
+ self.schema = Helpers.load_yaml(schema_f)
+ # Validate the schema itself against draft-07 metaschema
+ jsonschema.Draft7Validator.check_schema(self.schema)
+
+ def as_dict(self) -> YamlDict:
+ return self.schema
+
+class ExpectedValue(Enum):
+ """Expected values for a .config symbol"""
+
+ y = auto() # 'y' implies a built-in or a module; as in schema
+ n = auto()
+ allowed = auto()
+
+class Severity(Enum):
+ """Rule-check severity"""
+
+ fatal = auto()
+ high = auto()
+ medium = auto()
+ low = auto()
+ info = auto()
+
+class Rule:
+ """Single rule entry"""
+
+ expected: ExpectedValue
+ severity: Severity
+ rationale: Optional[str] = None
+ docs_hint: Optional[str] = None
+
+ def __init__(self, rule: YamlDict) -> None:
+ self.expected = ExpectedValue[rule['expected']]
+ self.severity = Severity[rule['severity']]
+ self.rationale = rule.get('rationale')
+ self.docs_hint = rule.get('docs_hint')
+ self._normalize_docs_hint()
+
+ def _normalize_docs_hint(self) -> None:
+ """Transform the hint to a URI fragment identifier"""
+ if self.docs_hint:
+ self.docs_hint = '#' + self.docs_hint
+
+class SymbolRule(Rule):
+ """Rule entry for one .config symbol"""
+
+ symbol: str
+
+ def __init__(self, rule: YamlDict):
+ self.symbol = rule['name']
+ super().__init__(rule)
+
+ def _normalize_docs_hint(self) -> None:
+ """Transform the hint to a docs.kernel.org URI fragment identifier"""
+ if self.docs_hint == 'config':
+ self.docs_hint = self.symbol.lower().replace('_', '-')
+
+ super()._normalize_docs_hint()
+
+class PatternRule(Rule):
+ """Rule entry for a regex pattern of .config symbols"""
+
+ symbol_regex: re.Pattern[str]
+
+ def __init__(self, rule: YamlDict):
+ regex = rule['name_pattern']
+ try:
+ self.symbol_regex = re.compile(regex)
+ except re.error as e:
+ error(f'Invalid symbol regex {regex}: {e}', os.EX_DATAERR)
+
+ super().__init__(rule)
+
+@dataclass
+class Rules:
+ """The .config checker rules repository / data model"""
+
+ rules: list[Rule]
+ docs_url: Optional[str] = None
+
+ def add_rule(self, rule: Rule) -> None:
+ """Add given rule to the existing rules repository"""
+ if isinstance(rule, SymbolRule):
+ symbol = rule.symbol
+ if self.get_symbol_rule(symbol):
+ error(f'Duplicate rule name: {symbol}', os.EX_DATAERR)
+
+ if isinstance(rule, PatternRule):
+ pattern = rule.symbol_regex.pattern
+ if self.get_pattern_rule(pattern):
+ error(f'Duplicate rule pattern: {pattern}', os.EX_DATAERR)
+
+ self.rules.append(rule)
+
+ def get_docs_url_for(self, rule: Rule) -> Optional[str]:
+ """Return full documentation URL for a rule, if available"""
+ if not self.docs_url or not rule.docs_hint:
+ return None
+
+ return f'{self.docs_url}{rule.docs_hint}'
+
+ def get_symbol_rule(self, kconfig_symbol: str) -> Optional[SymbolRule]:
+ """Return symbol rule matching passed .config symbol, if any"""
+ for rule in self.rules:
+ if isinstance(rule, SymbolRule):
+ if rule.symbol == kconfig_symbol:
+ return rule
+
+ return None
+
+ def get_pattern_rule(self, pattern: str) -> Optional[PatternRule]:
+ """Return pattern rule matching passed .config symbol, if any"""
+ for rule in self.rules:
+ if isinstance(rule, PatternRule):
+ if rule.symbol_regex.pattern == pattern:
+ return rule
+
+ return None
+
+class RulesFile:
+ """YAML rules file access"""
+
+ yaml: YamlDict
+
+ def __init__(self, yaml_f: Path, schema: Schema):
+ self.yaml = Helpers.load_yaml(yaml_f)
+ Helpers.validate_yaml(self.yaml, schema.as_dict(), yaml_f.name)
+
+ def parse(self) -> Rules:
+ """Parse the YAML rules into a python Rules object"""
+ rules = Rules([])
+
+ for rule in self.yaml.get('rules', []):
+ if 'name' in rule:
+ rules.add_rule(SymbolRule(rule))
+ continue
+
+ if 'name_pattern' in rule:
+ rules.add_rule(PatternRule(rule))
+ continue
+
+ rules.docs_url = self.yaml.get('docs_url')
+ return rules
+
+@dataclass
+class Issue:
+ message: str
+ docs_url: Optional[str]
+ severity: Severity
+ rationale: Optional[str]
+
+@dataclass
+class SymbolIssue(Issue):
+ """Checker issue concerning a single .config symbol"""
+ symbol: str
+
+@dataclass
+class PatternIssue(Issue):
+ """Checker issue concerning a regex pattern of .config symbols"""
+ symbols: list[str]
+
+class Issues:
+ """Repository of all issues found by the checker"""
+
+ def __init__(self, issues_l: list[Issue]):
+ # Index all issues by severity
+ self.issues: dict[Severity, list[Issue]] = {
+ severity: [] for severity in Severity
+ }
+ for issue in issues_l:
+ self.issues[issue.severity].append(issue)
+
+ def by_severity(self, severity: Severity) -> list[Issue]:
+ return self.issues[severity]
+
+@dataclass
+class Report:
+ """kconfig checker sanity report. Main script output"""
+
+ issues: Issues
+
+ def _print_header(self, header: str) -> None:
+ print(header)
+ print('=' * len(header))
+ print()
+
+ def _print_symbol(self, issue: SymbolIssue) -> None:
+ print(f'* {issue.symbol} is {issue.message}')
+ print()
+
+ def _print_pattern(self, issue: PatternIssue) -> None:
+ print(f'* Below options are {issue.message}:\n')
+ for symbol in issue.symbols:
+ print(f' {symbol}')
+ print()
+
+ def _print_issue(self, issue: Issue) -> None:
+ if isinstance(issue, SymbolIssue):
+ self._print_symbol(issue)
+ elif isinstance(issue, PatternIssue):
+ self._print_pattern(issue)
+
+ def _print_rationale(self, issue: Issue) -> None:
+ if not issue.docs_url and not issue.rationale:
+ return
+
+ print(' Rationale:')
+
+ if issue.docs_url:
+ print(' ', issue.docs_url)
+ if issue.rationale:
+ print()
+
+ if issue.rationale:
+ print(textwrap.fill(
+ issue.rationale,
+ width=75,
+ initial_indent=' ',
+ subsequent_indent=' ',
+ ))
+
+ print()
+
+ def print(self) -> None:
+ """Print the checker's .config report, in order of severity"""
+ print()
+ for severity in Severity:
+ issues = self.issues.by_severity(severity)
+ if not issues:
+ continue
+
+ self._print_header('Configuration Issues with severity = ' +
+ severity.name.upper())
+
+ for issue in issues:
+ self._print_issue(issue)
+ self._print_rationale(issue)
+
+@dataclass
+class Checker:
+ """The actual kconfig checker machinery"""
+
+ kconfig: KernelConfig
+ rules: Rules
+
+ def _find_issue_symbol(self, rule: SymbolRule) -> Optional[SymbolIssue]:
+ """Check the .config against passed rule. Return found issues, if any"""
+ value = self.kconfig.get_symbol_value(rule.symbol)
+ docs_url = self.rules.get_docs_url_for(rule)
+
+ if rule.expected == ExpectedValue.y:
+ if value is None:
+ return SymbolIssue(
+ 'not in .config',
+ docs_url,
+ rule.severity,
+ rule.rationale,
+ rule.symbol,
+ )
+ if value == 'n':
+ return SymbolIssue(
+ 'unset',
+ docs_url,
+ rule.severity,
+ rule.rationale,
+ rule.symbol,
+ )
+
+ if rule.expected == ExpectedValue.n:
+ if value in ('y', 'm'):
+ return SymbolIssue(
+ 'set',
+ docs_url,
+ rule.severity,
+ rule.rationale,
+ rule.symbol,
+ )
+
+ return None
+
+ def _find_issue_pattern(self, pattern_rule: PatternRule) -> Optional[PatternIssue]:
+ """Check the .config against a pattern rule. Return found issues, if any."""
+ if pattern_rule.expected == ExpectedValue.allowed:
+ return None
+
+ violation_type = 'unset' if pattern_rule.expected == ExpectedValue.y else 'set'
+ docs_url = self.rules.get_docs_url_for(pattern_rule)
+
+ violating_symbols = []
+
+ for symbol in self.kconfig.get_matching_symbols(pattern_rule.symbol_regex):
+ symbol_value = self.kconfig.get_symbol_value(symbol)
+
+ if pattern_rule.expected == ExpectedValue.y and symbol_value == 'n':
+ # Exclude the config symbol if it's explicitly disabled elsewhere
+ symbol_rule = self.rules.get_symbol_rule(symbol)
+ if symbol_rule and symbol_rule.expected == ExpectedValue.n:
+ continue
+
+ violating_symbols.append(symbol)
+
+ if pattern_rule.expected == ExpectedValue.n and symbol_value in ('y', 'm'):
+ # Exclude the config symbol if it's explicitly allowed elsewhere
+ symbol_rule = self.rules.get_symbol_rule(symbol)
+ if symbol_rule and symbol_rule.expected in (ExpectedValue.y, ExpectedValue.allowed):
+ continue
+
+ violating_symbols.append(symbol)
+
+ if not violating_symbols:
+ return None
+
+ return PatternIssue(
+ violation_type,
+ docs_url,
+ pattern_rule.severity,
+ pattern_rule.rationale,
+ violating_symbols,
+ )
+
+ def _find_issue(self, rule: Rule) -> Optional[Issue]:
+ """Check the .config against passed rule. Return found issues, if any"""
+ if isinstance(rule, SymbolRule):
+ return self._find_issue_symbol(rule)
+
+ if isinstance(rule, PatternRule):
+ return self._find_issue_pattern(rule)
+
+ return None
+
+ def find_issues(self) -> Optional[Report]:
+ """Check the .config file against required rules"""
+ issues: list[Issue] = []
+ for rule in self.rules.rules:
+ if issue := self._find_issue(rule):
+ issues.append(issue)
+
+ if issues:
+ return Report(Issues(issues))
+
+ return None
+
+def parse_args(rules: list[str]) -> argparse.Namespace:
+ parser = argparse.ArgumentParser(
+ prog='check.py',
+ description='Kernel .config options checker',
+ )
+
+ subparsers = parser.add_subparsers(
+ dest='command',
+ required=True,
+ )
+
+ subparsers.add_parser(
+ 'list-rules',
+ help='return available .config rule checking profiles',
+ )
+
+ check_parser = subparsers.add_parser(
+ 'check',
+ help='check a .config file against a rule profile',
+ )
+
+ check_parser.add_argument(
+ '-c',
+ '--config',
+ metavar='KCONFIG_FILE',
+ type=Path,
+ required=True,
+ help='.config file to be checked',
+ )
+
+ check_parser.add_argument(
+ '-r',
+ '--rules',
+ metavar='RULES',
+ choices=rules,
+ required=True,
+ help='rules file to check against',
+ )
+
+ return parser.parse_args()
+
+def main() -> None:
+ base_dir = Path(__file__).resolve().parent
+ rules_dir = base_dir / 'rules'
+
+ rules_l = sorted(path.stem for path in rules_dir.glob('*.yml'))
+ args = parse_args(rules_l)
+
+ if args.command == 'list-rules':
+ print(*rules_l, sep=', ')
+ return
+
+ kconfig_f = args.config.resolve()
+ kconfig = KernelConfig(kconfig_f)
+
+ schema_f = base_dir / 'schema.yml'
+ schema = Schema(schema_f)
+
+ rules_f = rules_dir / f'{args.rules}.yml'
+ rules = RulesFile(rules_f, schema).parse()
+
+ checker = Checker(kconfig, rules)
+ report = checker.find_issues()
+
+ if report:
+ report.print()
+ error(f'{kconfig_f} does not match required rules', os.EX_CONFIG)
+
+ print(f'Success: {kconfig_f} matches required rules')
+
+if __name__ == '__main__':
+ main()
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 04/10] kconfig: Introduce checkconfig target
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (2 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 03/10] kconfig-checker: Add the configuration checker code Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 05/10] Documentation: Describe kconfig-checker usage and design Ahmed S. Darwish
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Add a checkconfig target to validate the current kernel .config against a
set of rules. Select the rules using a mandatory RULES= make variable.
Example usage:
make RULES=preempt_rt checkconfig
Use the kconfig-checker to perform all sanity checks.
Document the new target at the 'make help' text.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
scripts/kconfig/Makefile | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 5baf1c44ffa2..d67a23730259 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -125,6 +125,26 @@ testconfig: $(obj)/conf
$(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no)
clean-files += tests/.cache
+checkconfig := $(PYTHON3) -B $(srctree)/tools/kconfig-checker/check.py
+list_checkconfig_rules = \
+ echo -n >&2 "Available RULES: "; \
+ $(checkconfig) list-rules >&2; \
+ echo >&2
+
+PHONY += checkconfig
+checkconfig:
+ $(Q)if [ -z "$(RULES)" ]; then \
+ echo >&2 "Usage: make RULES=<profile> checkconfig"; \
+ $(list_checkconfig_rules); \
+ exit 2; \
+ fi
+ $(Q)if ! $(checkconfig) list-rules | grep -qw "$(RULES)"; then \
+ echo >&2 "Unknown RULES profile = $(RULES)"; \
+ $(list_checkconfig_rules); \
+ exit 2; \
+ fi
+ $(Q)$(checkconfig) check -r "$(RULES)" -c "$(KCONFIG_CONFIG)"
+
# Help text used by make help
help:
@echo 'Configuration targets:'
@@ -154,6 +174,9 @@ help:
@echo ' default value without prompting'
@echo ' tinyconfig - Configure the tiniest possible kernel'
@echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)'
+ @echo ' checkconfig - Check current config against sanity rules specified by RULES'
+ @echo ' Example usage: "make RULES=preempt_rt checkconfig"'
+ @echo ' (requires python3, PyYAML, and jsonschema)'
@echo ''
@echo 'Configuration topic targets:'
@$(foreach f, $(all-config-fragments), \
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 05/10] Documentation: Describe kconfig-checker usage and design
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (3 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 04/10] kconfig: Introduce checkconfig target Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 06/10] kconfig-checker: Split schema to support component reuse Ahmed S. Darwish
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Describe the kconfig-checker design, invocation, and external dependencies.
For PREEMPT_RT users, mention that the checker can now be used for
validating a kernel's .config against PREEMPT_RT requirements.
For the kconfig-checker external dependencies error handling code,
reference the newly added documentation as well.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
Documentation/admin-guide/README.rst | 24 +++++++++
.../real-time/kernel-configuration.rst | 3 ++
Documentation/tools/index.rst | 1 +
Documentation/tools/kconfig_checker.rst | 50 +++++++++++++++++++
tools/kconfig-checker/check.py | 7 ++-
5 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 Documentation/tools/kconfig_checker.rst
diff --git a/Documentation/admin-guide/README.rst b/Documentation/admin-guide/README.rst
index 77fec1de6dc8..77ffe4dcb85b 100644
--- a/Documentation/admin-guide/README.rst
+++ b/Documentation/admin-guide/README.rst
@@ -256,6 +256,30 @@ Configuring the kernel
should probably answer 'n' to the questions for "development",
"experimental", or "debugging" features.
+Checking the kernel configuration
+---------------------------------
+
+The kconfig build system can validate the generated kernel ``.config``
+against a selected rules profile. Running ``make checkconfig`` prints
+usage information and the available profiles::
+
+ Usage: make RULES=<profile> checkconfig
+ Available RULES: preempt_rt
+
+For example, to validate the current configuration against the PREEMPT_RT
+profile, run::
+
+ make RULES=preempt_rt checkconfig
+
+``checkconfig`` reports kconfig options whose values do not match the
+selected rules profile. Each diagnostic includes a severity, such as
+``FATAL`` or ``INFO``, plus the suggested configuration change.
+
+Apply the reported changes as needed and run ``make checkconfig`` again,
+against the same rules, until no relevant diagnostics remain.
+
+For more information, see Documentation/tools/kconfig_checker.rst
+
Compiling the kernel
--------------------
diff --git a/Documentation/core-api/real-time/kernel-configuration.rst b/Documentation/core-api/real-time/kernel-configuration.rst
index d7f08e7b8760..15245c34a053 100644
--- a/Documentation/core-api/real-time/kernel-configuration.rst
+++ b/Documentation/core-api/real-time/kernel-configuration.rst
@@ -14,6 +14,9 @@ Introduction
This document lists the kernel configuration options that might affect a
real-time kernel's worst-case latency. It is intended for system integrators.
+You can validate your kernel's ``.config`` against the guidelines below using
+the ``kconfig-checker`` tool. See Documentation/tools/kconfig_checker.rst
+
Configuration options
=====================
diff --git a/Documentation/tools/index.rst b/Documentation/tools/index.rst
index 1adf4a6f909b..8912a627a7ac 100644
--- a/Documentation/tools/index.rst
+++ b/Documentation/tools/index.rst
@@ -10,6 +10,7 @@ more additions are needed here:
.. toctree::
:maxdepth: 1
+ kconfig_checker
rtla/index
rv/index
python
diff --git a/Documentation/tools/kconfig_checker.rst b/Documentation/tools/kconfig_checker.rst
new file mode 100644
index 000000000000..73717ac56ea9
--- /dev/null
+++ b/Documentation/tools/kconfig_checker.rst
@@ -0,0 +1,50 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+Kernel configuration checker
+============================
+
+``kconfig-checker`` is a Python tool for checking a kernel ``.config`` file
+against a set of rules; e.g., PREEMPT_RT configuration requirements. It
+gives users and system integrators a sanity report showing whether the
+passed kernel configuration satisfies a known profile.
+
+Invocation
+==========
+
+The checker can be invoked from anywhere in the file system::
+
+ check.py list-rules
+
+ check.py check -c path/to/.config -r preempt_rt
+
+As described in Documentation/admin-guide/README.rst, it is also used by
+``make checkconfig``.
+
+External dependencies
+=====================
+
+The checker requires the ``PyYAML`` and ``jsonschema`` python packages.
+Install them using your distribution's package manager, for example:
+
+.. table::
+ :align: left
+
+ ============ ================== ======================
+ Distribution PyYAML jsonschema
+ ============ ================== ======================
+ Debian ``python3-yaml`` ``python3-jsonschema``
+ Red Hat ``python3-pyyaml`` ``python3-jsonschema``
+ Arch Linux ``python-yaml`` ``python-jsonschema``
+ ============ ================== ======================
+
+Rule files
+==========
+
+The checker's rule files are written in YAML, and are stored in::
+
+ tools/kconfig-checker/rules/
+
+The schema for these files is fully documented in::
+
+ tools/kconfig-checker/schema.yml
diff --git a/tools/kconfig-checker/check.py b/tools/kconfig-checker/check.py
index 476cd13e59eb..ef49c28b9dab 100755
--- a/tools/kconfig-checker/check.py
+++ b/tools/kconfig-checker/check.py
@@ -69,7 +69,12 @@ try:
import jsonschema # type: ignore
import yaml as yaml_m # type: ignore
except ModuleNotFoundError as e:
- error(f'Missing Python module: {e.name}', os.EX_UNAVAILABLE)
+ docs = 'Documentation/tools/kconfig_checker.rst'
+ error(
+ f'Missing Python module: {e.name}\n'
+ f'Check {docs} for how to install required dependencies',
+ os.EX_UNAVAILABLE
+ )
#
# Actual script:
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 06/10] kconfig-checker: Split schema to support component reuse
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (4 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 05/10] Documentation: Describe kconfig-checker usage and design Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 07/10] kconfig-checker: Introduce overlays schema Ahmed S. Darwish
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Normal rule files require every rule to define both 'expected' and
'severity'. Overlay files are different: an overlay may update only
selected fields of an existing rule, so a future overlay schema will permit
these fields to be omitted.
Move the common rule definitions into schemas/base.yml and add a strict
schemas/rules.yml wrapper for complete rule files.
Teach the checker to build a Draft7Validator with local file:// URIs so
that rules.yml can resolve base.yml.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/check.py | 52 ++++++++++++++++---
.../{schema.yml => schemas/base.yml} | 3 +-
tools/kconfig-checker/schemas/rules.yml | 20 +++++++
3 files changed, 65 insertions(+), 10 deletions(-)
rename tools/kconfig-checker/{schema.yml => schemas/base.yml} (97%)
create mode 100644 tools/kconfig-checker/schemas/rules.yml
diff --git a/tools/kconfig-checker/check.py b/tools/kconfig-checker/check.py
index ef49c28b9dab..4f79ec487da7 100755
--- a/tools/kconfig-checker/check.py
+++ b/tools/kconfig-checker/check.py
@@ -80,6 +80,8 @@ except ModuleNotFoundError as e:
# Actual script:
#
+from jsonschema import Draft7Validator
+
class KernelConfig:
""".config file parser"""
@@ -133,9 +135,9 @@ class Helpers:
error(f'Invalid YAML syntax at {yaml_f}:\n {e}', os.EX_DATAERR)
@staticmethod
- def validate_yaml(yaml: YamlDict, schema: YamlDict, f_name: str) -> None:
+ def validate_yaml(yaml: YamlDict, validator: Draft7Validator, f_name: str) -> None:
try:
- jsonschema.validate(yaml, schema)
+ validator.validate(yaml)
except jsonschema.exceptions.ValidationError as e:
error(
f'{f_name} does not match its schema\n'
@@ -144,12 +146,45 @@ class Helpers:
)
class Schema:
- """Rules file schema, in json-schema.org format"""
+ """Rules file schema, in json-schema.org draft-07 format"""
+
+ # Rules schema refer to definitions from this base schema
+ BASE_SCHEMA = 'base.yml'
def __init__(self, schema_f: Path):
- self.schema = Helpers.load_yaml(schema_f)
- # Validate the schema itself against draft-07 metaschema
- jsonschema.Draft7Validator.check_schema(self.schema)
+ base_schema_f = schema_f.parent / self.BASE_SCHEMA
+
+ # Use file:// URIs so jsonschema can resolve local '$ref' entries
+ base_schema_uri = base_schema_f.resolve().as_uri()
+ schema_uri = schema_f.resolve().as_uri()
+
+ base_schema = self._load(base_schema_f)
+ self.schema = self._load(schema_f)
+
+ self.validator = self._create_validator(
+ base_schema_uri, base_schema,
+ schema_uri, self.schema,
+ )
+
+ @staticmethod
+ def _create_validator(base_schema_uri: str, base_schema: YamlDict,
+ schema_uri: str, schema: YamlDict) -> Draft7Validator:
+ """Create a schema validator which can resolve local '$ref' URIs"""
+ resolver = jsonschema.RefResolver(
+ base_uri=schema_uri,
+ referrer=schema,
+ store={ base_schema_uri: base_schema },
+ )
+
+ return Draft7Validator(schema, resolver=resolver)
+
+ @staticmethod
+ def _load(path: Path) -> YamlDict:
+ """Load and validate the schema itself against its metaschema"""
+ schema = Helpers.load_yaml(path)
+
+ Draft7Validator.check_schema(schema)
+ return schema
def as_dict(self) -> YamlDict:
return self.schema
@@ -273,7 +308,7 @@ class RulesFile:
def __init__(self, yaml_f: Path, schema: Schema):
self.yaml = Helpers.load_yaml(yaml_f)
- Helpers.validate_yaml(self.yaml, schema.as_dict(), yaml_f.name)
+ Helpers.validate_yaml(self.yaml, schema.validator, yaml_f.name)
def parse(self) -> Rules:
"""Parse the YAML rules into a python Rules object"""
@@ -533,6 +568,7 @@ def parse_args(rules: list[str]) -> argparse.Namespace:
def main() -> None:
base_dir = Path(__file__).resolve().parent
rules_dir = base_dir / 'rules'
+ schema_dir = base_dir / 'schemas'
rules_l = sorted(path.stem for path in rules_dir.glob('*.yml'))
args = parse_args(rules_l)
@@ -544,7 +580,7 @@ def main() -> None:
kconfig_f = args.config.resolve()
kconfig = KernelConfig(kconfig_f)
- schema_f = base_dir / 'schema.yml'
+ schema_f = schema_dir / 'rules.yml'
schema = Schema(schema_f)
rules_f = rules_dir / f'{args.rules}.yml'
diff --git a/tools/kconfig-checker/schema.yml b/tools/kconfig-checker/schemas/base.yml
similarity index 97%
rename from tools/kconfig-checker/schema.yml
rename to tools/kconfig-checker/schemas/base.yml
index a7f1b0a78cc7..1cc19752ebd6 100644
--- a/tools/kconfig-checker/schema.yml
+++ b/tools/kconfig-checker/schemas/base.yml
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft-07/schema"
-title: Schema for kconfig-checker rule files
+title: Base schema for kconfig-checker rule files
type: object
required: [rules]
additionalProperties: false
@@ -24,7 +24,6 @@ properties:
oneOf:
- required: [name]
- required: [name_pattern]
- required: [expected, severity]
additionalProperties: false
properties:
diff --git a/tools/kconfig-checker/schemas/rules.yml b/tools/kconfig-checker/schemas/rules.yml
new file mode 100644
index 000000000000..90f0eeef497f
--- /dev/null
+++ b/tools/kconfig-checker/schemas/rules.yml
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+"$schema": "https://json-schema.org/draft-07/schema#"
+
+title: Schema for kconfig-checker rule files
+
+allOf:
+ - $ref: "base.yml"
+
+ #
+ # The base schema leaves 'expected' and 'severity' optional so that
+ # overlay files can override only selected fields. In a full rules
+ # file though, these fields are mandatory for each rule.
+ #
+ - type: object
+ properties:
+ rules:
+ type: array
+ items:
+ required: [expected, severity]
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 07/10] kconfig-checker: Introduce overlays schema
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (5 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 06/10] kconfig-checker: Split schema to support component reuse Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 08/10] kconfig-checker: Add support for rule overlays Ahmed S. Darwish
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Keep `expected` and `severity` as optional for every rule.
This allows overlay files to be partial by design: they may override only
selected fields of an existing rule.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/schemas/base.yml | 4 ++--
tools/kconfig-checker/schemas/overlay.yml | 28 +++++++++++++++++++++++
2 files changed, 30 insertions(+), 2 deletions(-)
create mode 100644 tools/kconfig-checker/schemas/overlay.yml
diff --git a/tools/kconfig-checker/schemas/base.yml b/tools/kconfig-checker/schemas/base.yml
index 1cc19752ebd6..6217eaba3e41 100644
--- a/tools/kconfig-checker/schemas/base.yml
+++ b/tools/kconfig-checker/schemas/base.yml
@@ -53,8 +53,8 @@ properties:
# y option is expected to be enabled (built-in or module).
# n option is expected to be disabled or unset.
# allowed option is permitted and must not be reported as a violation.
- # This is used to exempt a specific symbol from a broader
- # `name_pattern` rule with `expected: n`.
+ # This is to be used either by overlays, or to exempt a specific
+ # symbol from a broader `name_pattern` rule with `expected: n`.
#
expected:
type: string
diff --git a/tools/kconfig-checker/schemas/overlay.yml b/tools/kconfig-checker/schemas/overlay.yml
new file mode 100644
index 000000000000..4ac9a364a344
--- /dev/null
+++ b/tools/kconfig-checker/schemas/overlay.yml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+"$schema": "https://json-schema.org/draft-07/schema#"
+
+title: Schema for kconfig-checker overlay files
+
+#
+# Full rule files require 'expected' and 'severity' for every rule.
+# Overlay files are partial by design: they may override only selected
+# fields of an existing rule, so such fields are not required here.
+#
+# For example, preempt_rt.yml:
+#
+# rules:
+# - name: CONFIG_DRM
+# expected: n
+# severity: info
+# docs_hint: config
+#
+# and a custom-overlay.yml:
+#
+# rules:
+# - name: CONFIG_DRM
+# expected: allow
+#
+
+allOf:
+ - $ref: "base.yml"
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 08/10] kconfig-checker: Add support for rule overlays
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (6 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 07/10] kconfig-checker: Introduce overlays schema Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 09/10] Documentation: Describe kconfig-checker overlays Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 10/10] kconfig: Introduce OVERLAY= for make checkconfig Ahmed S. Darwish
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
An kconfig-checker overlay is a YAML file applied on top of an existing
rules file.
Overlays are intended for distribution kernel packaging scripts, CI
environments, and board support packages; where the base rules may need
local adjustments. Overlays may override any rule's field. They may
also update the documentation URL or introduce totally new rules.
Add support for such an overlay mechanism at the kconfig checker.
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
tools/kconfig-checker/check.py | 89 +++++++++++++++++++++++++++++++++-
1 file changed, 88 insertions(+), 1 deletion(-)
diff --git a/tools/kconfig-checker/check.py b/tools/kconfig-checker/check.py
index 4f79ec487da7..38ae4ed32817 100755
--- a/tools/kconfig-checker/check.py
+++ b/tools/kconfig-checker/check.py
@@ -7,7 +7,7 @@
# Main usage:
#
# $ check.py list-rules
-# $ check.py check -c path/to/linux/.config -r preempt_rt
+# $ check.py check -c path/to/linux/.config -r preempt_rt [-o path/to/overlay.yml]
#
# Notes:
#
@@ -225,6 +225,48 @@ class Rule:
if self.docs_hint:
self.docs_hint = '#' + self.docs_hint
+ def overlay(self, overlay: YamlDict) -> None:
+ """Overlay passed yaml structure over existing data"""
+ if 'expected' in overlay:
+ self.expected = ExpectedValue[overlay['expected']]
+
+ if 'severity' in overlay:
+ self.severity = Severity[overlay['severity']]
+
+ if 'rationale' in overlay:
+ self.rationale = overlay['rationale']
+
+ if 'docs_hint' in overlay:
+ self.docs_hint = overlay['docs_hint']
+ self._normalize_docs_hint()
+
+ @staticmethod
+ def _ensure_required_fields(rule: YamlDict, rule_id: str) -> None:
+ """Ensure that passed rule has all required fields"""
+
+ # Normal rule files require every rule to define both 'expected'
+ # and 'severity'.
+ #
+ # Overlay files are different: an overlay may update only selected
+ # fields of an existing rule, so the overlay schema permits these
+ # fields to be omitted.
+ #
+ # However, an overlay may also introduce a completely new rule,
+ # where such a rule has no existing base rule to inherit from.
+ # Manually check all required fields existence in that case.
+
+ if 'expected' not in rule:
+ error(
+ f'Required field `expected` is missing from {rule_id}',
+ os.EX_DATAERR
+ )
+
+ if 'severity' not in rule:
+ error(
+ f'Required field `severity` is missing from {rule_id}',
+ os.EX_DATAERR
+ )
+
class SymbolRule(Rule):
"""Rule entry for one .config symbol"""
@@ -232,6 +274,8 @@ class SymbolRule(Rule):
def __init__(self, rule: YamlDict):
self.symbol = rule['name']
+
+ super()._ensure_required_fields(rule, f'symbol {self.symbol}')
super().__init__(rule)
def _normalize_docs_hint(self) -> None:
@@ -253,6 +297,7 @@ class PatternRule(Rule):
except re.error as e:
error(f'Invalid symbol regex {regex}: {e}', os.EX_DATAERR)
+ super()._ensure_required_fields(rule, f'pattern {regex}')
super().__init__(rule)
@dataclass
@@ -333,6 +378,36 @@ class Issue:
severity: Severity
rationale: Optional[str]
+class OverlayFile:
+ """YAML overlay file access"""
+
+ overlays: YamlDict
+
+ def __init__(self, overlay_f: Path, schema: Schema):
+ self.overlays = Helpers.load_yaml(overlay_f)
+ Helpers.validate_yaml(self.overlays, schema.validator, overlay_f.name)
+
+ def apply(self, rules: Rules) -> Rules:
+ if 'docs_url' in self.overlays:
+ rules.docs_url = self.overlays['docs_url']
+
+ for overlay in self.overlays.get('rules', []):
+ if 'name' in overlay:
+ if rule := rules.get_symbol_rule(overlay['name']):
+ rule.overlay(overlay)
+ else:
+ rules.add_rule(SymbolRule(overlay))
+ continue
+
+ if 'name_pattern' in overlay:
+ if prule := rules.get_pattern_rule(overlay['name_pattern']):
+ prule.overlay(overlay)
+ else:
+ rules.add_rule(PatternRule(overlay))
+ continue
+
+ return rules
+
@dataclass
class SymbolIssue(Issue):
"""Checker issue concerning a single .config symbol"""
@@ -563,6 +638,14 @@ def parse_args(rules: list[str]) -> argparse.Namespace:
help='rules file to check against',
)
+ check_parser.add_argument(
+ '-o',
+ '--overlay',
+ metavar='OVERLAY_FILE',
+ type=Path,
+ help='file overlay over picked rules',
+ )
+
return parser.parse_args()
def main() -> None:
@@ -586,6 +669,10 @@ def main() -> None:
rules_f = rules_dir / f'{args.rules}.yml'
rules = RulesFile(rules_f, schema).parse()
+ if args.overlay:
+ schema = Schema(schema_dir / 'overlay.yml')
+ rules = OverlayFile(args.overlay, schema).apply(rules)
+
checker = Checker(kconfig, rules)
report = checker.find_issues()
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 09/10] Documentation: Describe kconfig-checker overlays
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (7 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 08/10] kconfig-checker: Add support for rule overlays Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
2026-11-06 9:19 ` [PATCH v1 10/10] kconfig: Introduce OVERLAY= for make checkconfig Ahmed S. Darwish
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
Documentation/tools/kconfig_checker.rst | 57 +++++++++++++++++++++++--
1 file changed, 54 insertions(+), 3 deletions(-)
diff --git a/Documentation/tools/kconfig_checker.rst b/Documentation/tools/kconfig_checker.rst
index 73717ac56ea9..152bc0c634ea 100644
--- a/Documentation/tools/kconfig_checker.rst
+++ b/Documentation/tools/kconfig_checker.rst
@@ -18,8 +18,12 @@ The checker can be invoked from anywhere in the file system::
check.py check -c path/to/.config -r preempt_rt
-As described in Documentation/admin-guide/README.rst, it is also used by
-``make checkconfig``.
+An optional overlay may be supplied with ``-o``::
+
+ check.py check -c path/to/.config -r preempt_rt -o path/to/overlay.yml
+
+As described in Documentation/admin-guide/README.rst, the checker is also
+used by ``make checkconfig``.
External dependencies
=====================
@@ -47,4 +51,51 @@ The checker's rule files are written in YAML, and are stored in::
The schema for these files is fully documented in::
- tools/kconfig-checker/schema.yml
+ tools/kconfig-checker/base.yml
+ tools/kconfig-checker/rules.yml
+
+Overlays
+========
+
+An overlay is a YAML file applied on top of an existing mainline rule
+profile. Overlays are intended for local policy, distribution kernel
+packaging, CI environments, and board support packages.
+
+An overlay may override fields of an existing rule. For example, if the
+mainline PREEMPT_RT profile contains these rules:
+
+.. code-block:: yaml
+
+ rules:
+
+ - name: CONFIG_DRM
+ expected: n
+ severity: info
+ docs_hint: config
+
+ - name: CONFIG_EFI_DISABLE_RUNTIME
+ expected: y
+ severity: medium
+ docs_hint: config
+
+A board support package may override such rules with an overlay like:
+
+.. code-block:: yaml
+
+ rules:
+
+ - name: CONFIG_DRM
+ expected: allowed
+ rationale:
+ This product requires a display controller and accepts the latency
+ cost.
+
+ - name: CONFIG_EFI_DISABLE_RUNTIME
+ expected: n
+ rationale:
+ The efi_runtime workqueue is pinned to housekeeping CPU#0.
+
+.. note::
+
+ If a rule is generally useful to kernel users, please submit it to the
+ mainline profile instead of carrying it as a local overlay.
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v1 10/10] kconfig: Introduce OVERLAY= for make checkconfig
2026-11-06 9:19 [PATCH v1 00/10] tools: Introduce a kernel configuration checker Ahmed S. Darwish
` (8 preceding siblings ...)
2026-11-06 9:19 ` [PATCH v1 09/10] Documentation: Describe kconfig-checker overlays Ahmed S. Darwish
@ 2026-11-06 9:19 ` Ahmed S. Darwish
9 siblings, 0 replies; 11+ messages in thread
From: Ahmed S. Darwish @ 2026-11-06 9:19 UTC (permalink / raw)
To: Clark Williams, Steven Rostedt, Ingo Molnar, Nathan Chancellor,
Nicolas Schier
Cc: Sebastian Andrzej Siewior, Frederic Weisbecker,
Greg Kroah-Hartman, Jonathan Corbet, John Ogness, Derek Barbosa,
Zbigniew Jędrzejewski-Szmek, Daan De Meyer, Luca Boccassi,
linux-rt-devel, debian-kernel, linux-kbuild, linux-doc,
linux-kernel, Ahmed S. Darwish
For the kconfig checkconfig target, allow passing a rules overlay file
through an OVERLAY= parameter. This is mostly intended for distribution
kernel packaging scripts, CI systems, and board support packages.
Document this parameter at the kernel's administration guide.
Note, the detailed overlay semantics are already explained at
Documentation/tools/kconfig_checker.rst
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
Documentation/admin-guide/README.rst | 7 ++++++-
scripts/kconfig/Makefile | 6 ++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/README.rst b/Documentation/admin-guide/README.rst
index 77ffe4dcb85b..b62689bcda6b 100644
--- a/Documentation/admin-guide/README.rst
+++ b/Documentation/admin-guide/README.rst
@@ -263,7 +263,7 @@ The kconfig build system can validate the generated kernel ``.config``
against a selected rules profile. Running ``make checkconfig`` prints
usage information and the available profiles::
- Usage: make RULES=<profile> checkconfig
+ Usage: make RULES=<profile> [OVERLAY=<overlay>] checkconfig
Available RULES: preempt_rt
For example, to validate the current configuration against the PREEMPT_RT
@@ -278,6 +278,11 @@ selected rules profile. Each diagnostic includes a severity, such as
Apply the reported changes as needed and run ``make checkconfig`` again,
against the same rules, until no relevant diagnostics remain.
+The ``OVERLAY=`` parameter specifies an overlay file whose rules overrides
+the built-in mainline profile selected by ``RULES=``. It is intended for
+distributions kernel packaging scripts, CI systems, and board support
+packages.
+
For more information, see Documentation/tools/kconfig_checker.rst
Compiling the kernel
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index d67a23730259..faaa8c16ab03 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -126,6 +126,8 @@ testconfig: $(obj)/conf
clean-files += tests/.cache
checkconfig := $(PYTHON3) -B $(srctree)/tools/kconfig-checker/check.py
+checkconfig_overlay = $(if $(OVERLAY),-o "$(OVERLAY)")
+
list_checkconfig_rules = \
echo -n >&2 "Available RULES: "; \
$(checkconfig) list-rules >&2; \
@@ -134,7 +136,7 @@ list_checkconfig_rules = \
PHONY += checkconfig
checkconfig:
$(Q)if [ -z "$(RULES)" ]; then \
- echo >&2 "Usage: make RULES=<profile> checkconfig"; \
+ echo >&2 "Usage: make RULES=<profile> [OVERLAY=<overlay>] checkconfig"; \
$(list_checkconfig_rules); \
exit 2; \
fi
@@ -143,7 +145,7 @@ checkconfig:
$(list_checkconfig_rules); \
exit 2; \
fi
- $(Q)$(checkconfig) check -r "$(RULES)" -c "$(KCONFIG_CONFIG)"
+ $(Q)$(checkconfig) check -r "$(RULES)" -c "$(KCONFIG_CONFIG)" $(checkconfig_overlay)
# Help text used by make help
help:
--
2.56.0
^ permalink raw reply [flat|nested] 11+ messages in thread