* [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM
@ 2026-07-21 20:56 Jim Cromie
2026-07-21 20:56 ` [PATCH v7 01/29] params: fix a pr_debug(" %p ") use - already in MM-* Jim Cromie
` (28 more replies)
0 siblings, 29 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, stable, Arnd Bergmann, Louis Chauvet
This series fixes problems which broke CONFIG_DRM_USE_DYNAMIC_DEBUG=Y.
Briefly, the bug was that the handling of drm.debug settings occurs
when drm.ko is up/ready, long before drm drivers and helper modules
are up/ready. So they missed the enablement.
The fix is to split DECLARE_DYNDBG_CLASSMAP in 2; a client-server
scheme where drm calls DYNAMIC_DEBUG_CLASSMAP_DEFINE, and drivers call
DYNAMIC_DEBUG_CLASSMAP_USE. Now when a driver is modprobed, dyndbg
sees the _USE record, follows it to the classmap _DEFINition, finds
drm's drm.debug param which the classmap is wired to, and applies its
setting to the driver module.
The patchset, by chunks:
1. tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
moved to front
available from start to validate each following commit
"fingerprint" based.
2. DRM setup
enable code under test thru entire series
remove BROKEN on CONFIG_DRM_USE_DYNAMIC_DEBUG
fix ccflags-y in Makefile - build with -DDYNAMIC_DEBUG_MODULE
probably omit this for merge
3. vmlinux fixes for i386 misalignment crash
cleanups needed,added too.
4. cleanups, preps, etc
unused code removal
callchain refactors, struct re-structuring,
verbose debug reduction
5. The API fix
new __dyndbg_class_users section
new test_dynamic_debug_submod.ko
selftests - reproduce the drm-drivers scenario
Not included:
a few drm-core adaptation patches
75+ 1-liner-ish DRM patches to +DYNAMIC_DEBUG_CLASSMAP_USE
patches validating DYNAMIC_DEBUG_CLASSMAP_* args at compile-time, etc
There are 2 user-visible changes I should note:
1. change an ERROR condition displayed in dynamic_debug/control,
from "class:unknown, _id:1" to "class:_UNKNOWN_ id:1"
This only happens if a classmap is incorrectly defined. Such a
problem should be caught in review, and should be SHOUTED about. And
since classmaps are BROKEN for DRM (its only user), this affects no
users. Followon patches will catch more classmap definition errors,
but not this particular one.
2. change builtin module names, displayed in dynamic_debug/control,
from simple "[main]" to subsystem "[init/main]" etc. This corrects an
existing naming ambiguity, which is disallowed for loadable modules by
the module loader. To preserve legacy query behavior, "module main"
will select all of "[*/main]".
NB: the current ambiguity prevents cleanly adding classmaps to
builtins named "[main]". Such an addition is quite unlikely, so this
change could/can be dropped, but it seemed proper to point it out and
correct it.
3. revert a change in classmaps-v1 (2022) which enlarged __drm_debug
from (unsigned) int to long int. 64 categories of drm-debug is well
past reasonable and useful. The param is now a u32, for clarity.
NB: 63 class-ids allows 2+ classmaps per module.
PS: shortened cc list to stay under gmail's 500/day limit,
pls cc folks you think should see this.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Changes in v7:
- rebased onto v7.2-rc4
- applies clean to mm-nonmm-unstable@376804729797
- move selftest script to front, always there for validating, bisecting.
- substantial rewrite of test script. now fingerprint based.
- move DRM patches to front, for more thorough build-tests
- drop statements-to-expressions, the compile error no longer exists.
- sashiko fixes - all, I hope
- Link to v6: https://lore.kernel.org/r/20260707-dd-maint-2-v6-0-381f3edb0045@gmail.com
Changes in v6:
previous versions split the 1st chunk into a separate submission, in
an attempt to get past gmail's 500 msgs/day limit, and to ease review.
This complicated application; although the 2nd chunk had the b4
dependency on the 1st, this fact was missed by sashiko, which
therefore couldnt apply it.
A followon series adds compile-time and runtime checks to fail-fast if
classmaps are used incorrectly.
Changes in v5:
move KBUILD_MODFILE ahead of array-slice, to address sashiko
complaints which were fixed by later patches in V4.
Changes in v4:
Dyndbg previously used KBUILD_MODNAME to provide module-name, this
works well for loadable modules (module loader requires unique module
names), but for builtin modules, is effectively kbasename, and is not
guaranteed unique.
So we get 4 modules named "main": init/main, kernel/power/main,
kernel/base/power/main. This ambiguity is visible in user-space since
the beginning of dyndbg.
Now suppose kernel/{,base}/power/main want to define classmaps to
categorize the various pr-debugs they have. The current code finds a
module's classmaps by strcmp on modname, so init/main will match
against classmaps defined by both kernel/{,base}/power/main.
The current code will also map "main" classes to kernel/*/power/main,
so they will probably work at first, but 2 independent classmaps can
both use class-ids 0-N, but will conflict if they're both used by a
module. Then we have classmap overlaps and unpredictable results.
v3:
- move #includes to top of files,
- drop redundant ALIGN(8) in dydnbg.lds.S: DYNDBG_SECTIONS macro
- add Reviewed-by tag (thx Petr)
v2:
- avoid BOUNDED_SECTION in modules, dont need _start & _end symbols.
- sets 0 address to the sections, not just whatever current is.
---
Jim Cromie (29):
params: fix a pr_debug(" %p ") use - already in MM-*
selftests/dyndbg: Add kselftest script to verify dynamic-debug
drm: Fix incorrect ccflags-y spelling inside Makefile
drm: fix config dependent unused variable warning.
drm: Mark CONFIG_DRM_USE_DYNAMIC_DEBUG as unBROKEN
vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
vmlinux.lds.h: drop unused HEADERED_SECTION* macros
vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
vmlinux.lds.h: remove redundant ALIGN(8) directives
dyndbg.lds.S: fix lost dyndbg sections in modules
dyndbg: factor ddebug_match_desc out from ddebug_change
dyndbg: add stub macro for DECLARE_DYNDBG_CLASSMAP
dyndbg: reword "class unknown," to "class:_UNKNOWN_"
dyndbg-API: remove DD_CLASS_TYPE_(DISJOINT|LEVEL)_NAMES and code
dyndbg: drop NUM_TYPE_ARGS
dyndbg: bump num-tokens in a query-cmd from 9 to 15
dyndbg: reduce verbose/debug clutter
lib/parser: add match_wildcard_hyphen() for agnostic matching
dyndbg: use KBUILD_MODFILE for unique builtin module names
dyndbg: refactor param_set_dyndbg_classes and below
dyndbg: tighten fn-sig of ddebug_apply_class_bitmap
dyndbg: replace classmap list with an array-slice
dyndbg: macrofy a 2-index for-loop pattern
dyndbg: pin class param storage to u32
dyndbg,module: make proper substructs in _ddebug_info
dyndbg: move mod_name down from struct ddebug_table to _ddebug_info
dyndbg: hoist classmap-filter-by-modname up to ddebug_add_module
dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP
selftests/dyndbg: enable FT_classmap_inheritance
Documentation/admin-guide/dynamic-debug-howto.rst | 42 +-
MAINTAINERS | 2 +
drivers/gpu/drm/Kconfig.debug | 1 -
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_print.c | 7 +-
include/asm-generic/bounded_sections.lds.h | 32 +
include/asm-generic/dyndbg.lds.h | 22 +
include/asm-generic/vmlinux.lds.h | 68 +-
include/drm/drm_print.h | 2 +-
include/linux/dynamic_debug.h | 257 +++++--
include/linux/parser.h | 1 +
kernel/module/main.c | 15 +-
kernel/params.c | 4 +-
lib/Kconfig.debug | 24 +-
lib/Makefile | 3 +
lib/dynamic_debug.c | 607 +++++++++------
lib/parser.c | 58 +-
lib/test_dynamic_debug.c | 145 ++--
lib/test_dynamic_debug_submod.c | 14 +
scripts/module.lds.S | 2 +
tools/testing/selftests/dynamic_debug/Makefile | 10 +
tools/testing/selftests/dynamic_debug/config | 8 +
.../selftests/dynamic_debug/dyndbg_selftest.sh | 847 +++++++++++++++++++++
.../dynamic_debug/syslog_hash_validation.sh | 384 ++++++++++
24 files changed, 2089 insertions(+), 469 deletions(-)
---
base-commit: 6946cd5d0aa4dd10a414ddcb7a10844fdb0ad345
change-id: 20260521-dd-maint-2-76c542079420
Best regards,
--
Jim Cromie <jim.cromie@gmail.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v7 01/29] params: fix a pr_debug(" %p ") use - already in MM-*
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug Jim Cromie
` (27 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, stable
NB: this is already in mm, but I need it in this patchset because
kernel/params is subjected to testing by the selftest script, and the
test depends upon its stability.
kernel/params.c has a pr_debug() which prints the address of a
parameter setter function, this is not legitimately useful. Since the
value is known and useful to the user, print that instead.
Cc: stable@vger.kernel.org
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
kernel/params.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/params.c b/kernel/params.c
index a668863a4bb6..5c8a05921e28 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -136,8 +136,8 @@ static int parse_one(char *param,
if (!val &&
!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
return -EINVAL;
- pr_debug("handling %s with %p\n", param,
- params[i].ops->set);
+ pr_debug("handling %s with value '%s'\n", param,
+ val ? val : "no-arg");
kernel_param_lock(params[i].mod);
if (param_check_unsafe(¶ms[i]))
err = params[i].ops->set(val, ¶ms[i]);
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
2026-07-21 20:56 ` [PATCH v7 01/29] params: fix a pr_debug(" %p ") use - already in MM-* Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 21:06 ` sashiko-bot
2026-07-21 20:56 ` [PATCH v7 03/29] drm: Fix incorrect ccflags-y spelling inside Makefile Jim Cromie
` (26 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Add a kselftest script to attempt full validation of dynamic-debug
behavior. The script tests query grammar as documented, responses to
bad input, and proper/expected effects on both the control-file
display of current state, and on pr_debug logging behavior.
NOTE: This script was finished last, then rebased to front; It gives
an easy functional test thru the series, not just a does-it-boot. It
preserves but disables some tests to pass as a baseline; there are 3
Basics: FT_grammar_ok(), FT_grammar_errs(), FT_basic_queries().
The Canonical Test:
A naive dyndbg test might look like:
echo "module main +mfp" > /proc/dynamic_debug/control
local ct=$(grep -c " =pmf " /proc/dynamic_debug/control)
(( $ct == $expected)) || FAIL
But you'll find that you've changed 4 different modules, and your
count is off. Ad-hoc testing is hard, and fundamentally trades
test clarity against thoroughness, precision and brittleness.
Here, the canonical test tries harder:
1. Observe the prior dyndbg state-of-interest.
2. Send $cmd to change state-of-interest.
a: echo $cmd > control
b: echo 1 > "/sys/module/test_dynamic_debug/parameters/do_classes"
3. Observe the results, in control-file or dmesg
The key here is 'observe' means `md5sum $state-of-interest`. This
gives us total "checksum" precision, and with a little care,
$state-of-interest solves the brittleness.
If a test should cause logging:
1. Sends a unique 'START_of_label' message directly to syslog/dmesg.
2. Sends the command or parameter configuration, as in b: above.
3. Sends a unique 'END_of_label' message directly to syslog/dmesg.
4. read dmesg, extract START..END
This bookended logging allows the script to isolate and reliably
extract the precise dmesg/syslog slice between the markers, and
fingerprint the state-of-interest cryptographically. Its not truly
isolated from busy-kernel messages, unless its a test-vm.
Comprehensive Feature Test (FT_*) Script Mapping:
The Feature Test (FT_*) functions test major aspects of dynamic-debug,
they're in 3 categories:
1: Zero-Dependency Grammar & Core Parser Tests
These tests throw legal & illegal commands at the >control file, but
don't attempt to select any real pr_debug callsite.
* FT_grammar_ok:
Verifies successful query grammar parses (exact line, open line
range, closed line range, and colon-delimited file:line/file:func
syntax) using side-effect-free empty placeholder flags ('+_').
* FT_grammar_errs:
Verifies core query parser error-handling and EINVAL paths across
multiple verbosity levels (0..3), catching even minor message drift.
2: Built-in Feature & Name Equivalence Tests (Kernel Core)
These tests verify the core features compiled directly into the kernel
image. They validate "[main]" vs "[init/main]" resolution added recently.
* FT_basic_queries:
Verifies basic, direct queries (module, func, format, and clear flags
'=_') targeting the builtin kernel/params engine.
These tests are disabled until KBUILD_MODFILE:
* FT_path_module_queries:
Verifies path-based and wildcard module query controls (such as
module 'init/main', module '*/main') targeting builtin startup
callsites. This specifically validates the resolution of the old
'module main' ambiguity (which selects 5-6 different built-in modules).
* FT_hyphen_underscore:
Verifies literal name versus kbasename hyphen/underscore equivalence
(e.g., kvm_intel vs. kvm-intel), proving that both queries select
identical ranges-of-interest. This validates recent kernel fixes
establishing name-normalization equivalence inside the query engine,
ensuring that character substitutions work as they do in modprobe.
* FT_comma_terminators:
Verifies dynamic-debug's comma-to-space query tokenization and
multi-query splitting on '@' delimiters.
3: Tests which require test_dynamic_debug
These tests verify complex classmap configurations, multi-module
setups, and load-time/runtime parameter callback equivalence.
* FT_test_classes:
Verifies classmap-based query enablers and class configurations on a
modular target, proving dynamic runtime class configurations via
/proc/dynamic_debug/control.
* FT_classmap_inheritance:
Verifies multi-module classmap propagation and class inheritance
checks between a parent and submodule sharing a classmap. This
actively demonstrates a key systems distinction: one-time bare class
queries (dyndbg=class...) do NOT inherit at load-time, whereas
classmap module parameters (p_disjoint_bits, p_level_num) are
persistent and successfully propagate state to submodules upon loading.
These tests recapitulate the scenario where classmaps-v1 [1] hit
regressions and was marked BROKEN.
* FT_modprobe_w_param:
Verifies load-time parameter callback initialization (via modprobe
$param=$val) and subsequent runtime sysfs-write unsetting callbacks
sequentially, by looping over verbose levels, and varying
(p_disjoint_bits, p_level_num) and (do_classes, do_bulk).
NB: within each FT_, tests are numbered and cataloged. This isolates
each FT_* test's sequence numbers from each other.
Main Test Runner (dyndbg_selftest.sh) Support Functions:
* ddcmd("$query", ["$range"], ["$action"]):
The core test primitive. Writes a query string to the control file.
- ["$range"]: Optional slice filter pattern (triggers transition
verification R1 on non-empty values).
- ["$action"]: Expected outcome action ('pass' default, 'fail' asserts
return code 1 and logs dmesg, 'log' asserts 0 and logs dmesg).
* ddcmd_err("$query"):
Semantic error query wrapper. Invokes ddcmd expected to fail.
* ddcmd_load("$query", "$range", "$param_path", "$val"):
Workload-driven syslog verification helper.
- "$param_path": Sysfs parameter path of workload trigger.
- "$val": Integer trigger value written to workload param.
* verify_modprobe_param_logging("$param", "$val", "$tag"):
Dynamic parameter test primitive. Coordinates load-time modprobe and
runtime sysfs unsetting in a single sequence.
- "$param": Module parameter name to configure at load-time.
- "$val": Initial value or composite bitmask/level integer.
- "$tag": Suffix used to construct the dmesg golden record label.
* slice_and_hash_ddctrl("$grep_pattern"):
Local control-file wrapper.
* ifrmmod("$module"):
Defensive module unloader.
* handle_exit_code("$lineno", "$func", "$exit_code", ["$expected_code"]):
Core exit code verifier.
- "$lineno": Caller line number ($BASH_LINENO).
- "$func": Caller function name ($FUNCNAME).
- ["$expected_code"]: Expected exit code, default 0.
Verification Library (syslog_hash_validation.sh) Support Functions:
* log_start() / log_stop():
Slicing syslog capture bookends. Marks the start and end of a
stimulus-triggered test execution by writing tags to /dev/kmsg.
* rdi_resolve_label():
Active sequence resolver. Leverages Bash call-stack reflection
(FUNCNAME) to determine active FT_ test functions and automatically
re-index sequence numbers.
* slice_by_grep("$pattern", ["$file"]):
Text slice extractor. Narrows the scope of a captured log or file.
- <pattern>: Regex pattern to narrow the capture scope.
- [file_path]: Target file path to slice (defaults to dmesg).
* verify_file_slice(<slice_pattern>, [file_path], [extra_args]):
Standard file transition verifier. Captures a state slice, auto-
resolves the label, and verifies its cryptographic hash.
- <slice_pattern>: Regex pattern defining the capture scope.
- [file_path]: Target file to slice, defaulting to control-file.
- [extra_args]: Optional tag appended to the golden record.
* verify_dmesg_slice(<label>, ["$start"], ["$end"], ["$filter"],
["$extra_args"]):
Active dmesg syslog slice verifier.
- ["$start"]: Starting bookend marker, defaulting to START_of_label.
- ["$end"]: Ending bookend marker, defaulting to END_of_label.
- ["$filter"]: Optional filter grep regex to isolate target prints.
- ["$extra_args"]: Suffix tag, defaulting to "dmesg".
* capture_before("$range_pattern", ["$file"]):
Pre-stimulus snapshot helper. Saves a snapshot of a target slice.
- ["$file"]: Target file to snapshot, defaulting to control-file.
* verify_after_change(["$extra_args"]):
Post-stimulus delta verifier. Compares post-state slices to pre-state
snapshots, generating a portable, line-number-free unified diff.
- ["$extra_args"]: Optional tag, defaulting to the pre-state pattern.
* verify_fingerprint("$label", "$extra_args", "$computed_hash", "$desc"):
Exact-label cryptographic matching engine.
- "$desc": Human-readable trace description type (e.g. "Dmesg Log").
* audit_golden_records():
Self-auditing reporting utility. Called at the end of the script, it
identifies fingerprint/result entries that weren't encountered in the
run, and which are probably stale entries.
What happens when tests fail/drift ?
The script tests against kernel/params (params module) for several reasons;
it is stable, it is always built-in, since a kernel can't read boot-options
without it, and modprobes cause it to run enabled pr_debugs.
Those tests left it enabled for the modprobe tests, which exposed a
pr_debug("%p"...) latent in kernel/params.
-----------------------------------
: DRIFT for 'FT_basic_queries.6'
Range: "\[kernel/params\]"
Stimulus: module params =_ # clear params
module params +ml # set flags
module params func parse_args +fs # other flags
Expected: 'baea1247680e' (baea1247680e8151c121539f4b90a6d8)
Got: '4b4d46577a1c' (4b4d46577a1cd930c7a6d5298f6ca24c)
Add or replace this line in GOLDEN_RECORDS():
#K= 4b4d46577a1cd930c7a6d5298f6ca24c FT_basic_queries.6 \
"\[kernel/params\]"
--- Captured Invariant File Change Diff Output ---
@@
-kernel/params.c:139 [kernel/params]parse_one =_ "handling %s with %p\n"
-kernel/params.c:152 [kernel/params]parse_one =_ "doing %s: %s='%s'\n"
-kernel/params.c:156 [kernel/params]parse_one =_ "Unknown argument '%s'\n"
-kernel/params.c:175 [kernel/params]parse_args =_ "doing %s, parsing ARGS: '%s'\n"
+kernel/params.c:139 [kernel/params]parse_one =ml "handling %s with %p\n"
+kernel/params.c:152 [kernel/params]parse_one =ml "doing %s: %s='%s'\n"
+kernel/params.c:156 [kernel/params]parse_one =ml "Unknown argument '%s'\n"
+kernel/params.c:175 [kernel/params]parse_args =mfsl "doing %s, parsing ARGS: '%s'\n"
-----------------------------------
Similarly, the engine catches dmesg syslog format drifts under the
load-time and runtime parameter interfaces (R2 capturing):
: DRIFT for 'FT_modprobe_w_param.7'
Range: dmesg
Stimulus: modprobe test_dynamic_debug p_level_num=3
Expected: '6e811e3b169a' (6e811e3b169acf94410fbda0747b4e78)
Got: '36c4f8b6363b' (36c4f8b6363b79ead3ab5a25e1795525)
Add or replace this line in GOLDEN_RECORDS():
#K= 36c4f8b6363b79ead3ab5a25e1795525 FT_modprobe_w_param.7 dmesg
--- Captured Invariant Dmesg Log Output ---
dyndbg: 34 debug prints in module test_dynamic_debug
kernel/params:parse_args: doing test_dynamic_debug, parsing ARGS: 'p_level_num=3'
kernel/params:parse_one: handling p_level_num with 000000005c252bc5
dyndbg: p_level_num: total matches: 2
test_dd: V1 msg
test_dd: V2 msg
-----------------------------------
NOTES:
By default script runs with V=0 envar, and runs silently on success.
With V=1, script runs show status, like:
# BASIC_TESTS
✔ Verified 'FT_basic_queries.1' (24d85e3b86f3) [via: 'module params +mf']
✔ Verified 'FT_basic_queries.2' (958898bcd973) [via: 'module params +l']
✔ Verified 'FT_basic_queries.3' (130118da5a29) [via: 'module params -m']
✔ Verified 'FT_basic_queries.4' (da6bd1c6a299) [via: 'module params =_']
✔ Verified 'FT_basic_queries.5' (82572e8d20c4) [via: 'module params +mf @ module params func parse_args +sl']
✔ Verified 'FT_basic_queries.6' (baea1247680e) [via: 'module params =_ # clear params
module params +ml # set flags
module params func parse_args +fs # other flags']
With V=2, script runs show full context, like:
✔ Verified 'FT_grammar_errs.44' (2d3af67031a3) [via: 'module foobar +x']
--- Captured Invariant Dmesg Log Output (FT_grammar_errs.44) ---
dyndbg: read 17 bytes from userspace
dyndbg: query 0: "module foobar +x"
dyndbg: split into words: "module" "foobar" "+x"
dyndbg: unknown flag 'x'
dyndbg: flags parse failed
dyndbg: query parse failed
dyndbg: processed 1 queries, with 0 matches, 1 errs
-----------------------------------
# BASIC_TESTS
✔ Verified 'FT_basic_queries.1' (24d85e3b86f3) [via: 'module params +mf']
--- Captured Invariant File Change Diff Output (FT_basic_queries.1) ---
@@
-kernel/params.c:139 [kernel/params]parse_one =_ "handling %s with value '%s'\n"
-kernel/params.c:152 [kernel/params]parse_one =_ "doing %s: %s='%s'\n"
-kernel/params.c:156 [kernel/params]parse_one =_ "Unknown argument '%s'\n"
-kernel/params.c:175 [kernel/params]parse_args =_ "doing %s, parsing ARGS: '%s'\n"
+kernel/params.c:139 [kernel/params]parse_one =mf "handling %s with value '%s'\n"
+kernel/params.c:152 [kernel/params]parse_one =mf "doing %s: %s='%s'\n"
+kernel/params.c:156 [kernel/params]parse_one =mf "Unknown argument '%s'\n"
+kernel/params.c:175 [kernel/params]parse_args =mf "doing %s, parsing ARGS: '%s'\n"
-----------------------------------
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
- selftests/dyndbg: drop builtin enablements in FT_modprobe_w_param()
- selftests/dyndbg: V=2 output needs #K=$fingerprint too
-----------------------------------
✔ Proven Runtime Unset: echo 0 > p_disjoint_bits successfully cleared callsites
✔ Verified 'FT_modprobe_w_param.42' (146a1294f452) [via: 'modprobe test_dynamic_debug p_level_num=3; echo 1 > /sys/module/test_dynamic_debug/parameters/do_prints']
--- Captured Invariant Dmesg Log Output (FT_modprobe_w_param.42) ---
#K= 146a1294f452e51c12837bbd3ea723fa FT_modprobe_w_param.42
kernel/params:parse_args: doing test_dynamic_debug, parsing ARGS: 'p_level_num=3'
kernel/params:parse_one: handling p_level_num with value '3'
test_dd: V1 msg
test_dd: V2 msg
test_dd: V1 msg
test_dd: V2 msg
-----------------------------------
The new element/line above is the #K= record, which is the format used
in the GOLDEN-RECORDS. Having this captured output, with the #K=rec,
saved off to a reference file somewhere, should help when dealing with
a drift report:
-----------------------------------
: DRIFT for 'FT_modprobe_w_param.42'
Range: dmesg
Stimulus: modprobe test_dynamic_debug p_level_num=3; echo 1 > /sys/module/test_dynamic_debug/parameters/do_prints
Expected: '146a1294f452' (146a1294f452e51c12837bbd3ea723fa)
Got: '14cf091423c6' (14cf091423c69a188ba45cc39414c1b9)
Add or replace this line in GOLDEN_RECORDS():
#K= 14cf091423c69a188ba45cc39414c1b9 FT_modprobe_w_param.42 dmesg
--- Captured Invariant Dmesg Log Output ---
test_dd: V1 msg
test_dd: V2 msg
test_dd: V1 msg
test_dd: V2 msg
-----------------------------------
NB: this drift report came from a CONFIG_DYNAMIC_DEBUG_CORE=y only
build, where the GOLDEN_RECORDS were done on a CONFIG_DYNAMIC_DEBUG=y
build, which had the builtin pr-debugs present and enabled. This
resulted in a commit to remove the enablement of those builtin
pr_debugs.
---
MAINTAINERS | 1 +
tools/testing/selftests/dynamic_debug/Makefile | 10 +
tools/testing/selftests/dynamic_debug/config | 8 +
.../selftests/dynamic_debug/dyndbg_selftest.sh | 799 +++++++++++++++++++++
.../dynamic_debug/syslog_hash_validation.sh | 384 ++++++++++
5 files changed, 1202 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index a674e36529f7..dd6e351d2a98 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9182,6 +9182,7 @@ S: Maintained
F: include/linux/dynamic_debug.h
F: lib/dynamic_debug.c
F: lib/test_dynamic_debug.c
+F: tools/testing/selftests/dynamic_debug/
DYNAMIC INTERRUPT MODERATION
M: Tal Gilboa <talgi@nvidia.com>
diff --git a/tools/testing/selftests/dynamic_debug/Makefile b/tools/testing/selftests/dynamic_debug/Makefile
new file mode 100644
index 000000000000..d998f485a9bc
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# borrowed from Makefile for user memory selftests
+
+# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
+all:
+
+TEST_PROGS := dyndbg_selftest.sh
+TEST_FILES := syslog_hash_validation.sh
+
+include ../lib.mk
diff --git a/tools/testing/selftests/dynamic_debug/config b/tools/testing/selftests/dynamic_debug/config
new file mode 100644
index 000000000000..ec478b17873d
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/config
@@ -0,0 +1,8 @@
+
+# basic tests ref the builtin params module
+CONFIG_DYNAMIC_DEBUG=y
+
+# more testing is possible with these,
+# but insisting on them here skips testing entirely for such configs
+# CONFIG_TEST_DYNAMIC_DEBUG=m
+# CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD=m
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
new file mode 100755
index 000000000000..1dafeaabc976
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -0,0 +1,799 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Standard kselftest exit codes
+ksft_pass=0
+ksft_fail=1
+ksft_skip=4
+
+ESC=$'\033'
+RED="${ESC}[0;31m"
+GREEN="${ESC}[0;32m"
+YELLOW="${ESC}[0;33m"
+BLUE="${ESC}[0;34m"
+MAGENTA="${ESC}[0;35m"
+CYAN="${ESC}[0;36m"
+NC="${ESC}[0;0m"
+CUMULATIVE_DDCMDS="init"
+error_msg=""
+V=${V:=0} # invoke as V=1 $0 for global verbose
+K=${K:=0} # K=1,2 to tolerate/pass on checksum mismatches
+
+# Sanitize V to ensure it is a valid integer
+if [[ ! "$V" =~ ^[0-9]+$ ]]; then
+ V=0
+fi
+
+function v_echo {
+ [ "${V:-0}" -ge 1 ] && echo -e "$@"
+}
+
+[ -e /proc/dynamic_debug/control ] || {
+ echo -e "${RED}: this test requires CONFIG_DYNAMIC_DEBUG=y ${NC}"
+ exit $ksft_skip # nothing to test here, no good reason to fail.
+}
+
+lsmod >/dev/null 2>&1 || {
+ echo -e "${RED}: lsmod requires /proc/modules ${NC}"
+ exit $ksft_skip # maybe later we can do more
+}
+
+# need info to avoid failures due to untestable configs
+
+[ -f "$KCONFIG_CONFIG" ] || KCONFIG_CONFIG=".config"
+if [ -f "$KCONFIG_CONFIG" ]; then
+ v_echo "# consulting KCONFIG_CONFIG: $KCONFIG_CONFIG"
+ grep -q "CONFIG_DYNAMIC_DEBUG=y" $KCONFIG_CONFIG ; LACK_DD_BUILTIN=$?
+ grep -q "CONFIG_TEST_DYNAMIC_DEBUG=m" $KCONFIG_CONFIG ; LACK_TMOD=$?
+else
+ # if no config, try runtime probes
+ modprobe -n test_dynamic_debug 2>/dev/null ; LACK_TMOD=$?
+ # assume builtin dyndbg if control exists (checked above)
+ LACK_DD_BUILTIN=0
+fi
+
+# Clean up any leftover loaded test modules at initialization
+grep -q "^test_dynamic_debug_submod " /proc/modules 2>/dev/null && rmmod test_dynamic_debug_submod
+grep -q "^test_dynamic_debug " /proc/modules 2>/dev/null && rmmod test_dynamic_debug
+
+# ==============================================================================
+# TESTING STRATEGY 1.
+# Change and observe control-file settings:
+# ddcmd: ie echo $dd_query_cmd > /proc/dynamic_debug/control
+# read back control, count changes due to query_cmd
+# ==============================================================================
+
+function ddcmd () {
+ # ddcmd <query_args> [range_pattern] [pass|fail|log]
+ local args="$1"
+ local range_pattern="$2"
+ local action="${3:-pass}"
+
+ # Verify if range transition check is requested (non-empty pattern)
+ local do_range=0
+ if [ -n "$range_pattern" ]; then
+ do_range=1
+ fi
+
+ local exp_exit_code=0
+ local do_log=0
+
+ if [ "$action" = "fail" ]; then
+ exp_exit_code=1
+ do_log=1
+ elif [ "$action" = "log" ]; then
+ exp_exit_code=0
+ do_log=1
+ fi
+
+ # Update cumulative state-machine lineage
+ if [[ "$args" == *"=_"* ]]; then
+ CUMULATIVE_DDCMDS="$args"
+ else
+ CUMULATIVE_DDCMDS="${CUMULATIVE_DDCMDS}; $args"
+ fi
+
+ # Update local bookend command tracker
+ if [ "$IN_BOOKEND" -eq 1 ]; then
+ if [ -z "$LOCAL_DDCMDS" ]; then
+ LOCAL_DDCMDS="$args"
+ else
+ LOCAL_DDCMDS="${LOCAL_DDCMDS}; $args"
+ fi
+ else
+ LOCAL_DDCMDS="$args"
+ fi
+
+ # 1. Automatically start dmesg capture if requested (fail or log)
+ [ "$do_log" -eq 1 ] && log_start
+
+ # 2. Automatically take pre-state file snapshot if requested (non-empty range)
+ [ "$do_range" -eq 1 ] && capture_before "$range_pattern"
+
+ output=$( (echo "$args" > /proc/dynamic_debug/control) 2>&1)
+ exit_code=$?
+ error_msg=$(echo "$output" | cut -d ":" -f 5 | sed -e 's/^[[:space:]]*//')
+
+ # Handle the exit code check
+ handle_exit_code $BASH_LINENO $FUNCNAME $exit_code $exp_exit_code
+
+ # Close and verify dmesg capture if we started one
+ [ "$do_log" -eq 1 ] && log_stop
+
+ # Close and verify range transition diff if we started one (non-empty range)
+ [ "$do_range" -eq 1 ] && verify_after_change
+}
+
+function ddcmd_err () {
+ # ddcmd_err <query_args>
+ # Semantic wrapper for parser syntax & error validation
+ ddcmd "$1" "" fail
+}
+
+function ddcmd_load () {
+ # ddcmd_load <query_args> <range_pattern> <workload_param_path> <workload_val>
+ # Semantic wrapper for end-to-end filter setup and live workload logging
+ local query="$1"
+ local range="$2"
+ local param_path="$3"
+ local val="$4"
+
+ # 1. Setup the control filters (using positional ddcmd range-check)
+ echo "$query" "$range"
+ ddcmd "$query" "$range"
+
+ # 2. Execute the workload and capture syslog prints
+ log_start
+ echo "$val" > "$param_path"
+ log_stop
+}
+
+function handle_exit_code() {
+ local exp_exit_code=0
+ [ $# == 4 ] && exp_exit_code=$4
+ if [ "$3" -ne $exp_exit_code ]; then
+ echo -e "${RED}: $BASH_SOURCE:$1 $2() " \
+ "expected to exit with code $exp_exit_code, got $3${NC}"
+ [ "$3" == 1 ] && echo "Error: '$error_msg'"
+ exit $ksft_fail
+ fi
+}
+
+# ==============================================================================
+# TESTING STRATEGY 2.
+# do 1 to setup test expectations.
+# run logging-workload
+# capture output
+# hash-validate it against GOLDEN_SAMPLE db (at file end)
+#
+# ==============================================================================
+# Source hash-based validation and state verification helper library
+DIR="$(dirname "$(readlink -f "$0")")"
+. "$DIR/syslog_hash_validation.sh"
+
+# Define target validation file path
+CONTROL_FILE="/proc/dynamic_debug/control"
+
+# App-specific wrappers mapping to generic library helpers
+function slice_and_hash_ddctrl {
+ local slice=$(slice_by_grep "$1" "$CONTROL_FILE")
+ echo "$slice" | tr -d '\r' | md5sum | cut -d' ' -f1
+}
+
+function verify_modprobe_param_logging {
+ # $1 - parameter name (e.g. do_classes)
+ # $2 - parameter value (e.g. 1)
+ # $3 - short descriptive tag (e.g. classes)
+ local param="$1"
+ local val="$2"
+ local tag="$3"
+
+ # Make sure both modules are completely unloaded to trigger a fresh load
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+
+ # Explicitly register the modprobe command as the local stimulus!
+ LOCAL_DDCMDS="modprobe test_dynamic_debug ${param}=${val}"
+
+ # 1. Capture and verify the load-time (modprobe) dmesg logs (R2)
+ log_start
+ modprobe test_dynamic_debug "${param}=${val}"
+
+ # If it is a state-controlling parameter, trigger the print-workload 'do_classes=1'
+ # inside the same syslog dmesg capture bookends to verify their actual pr_debug logging!
+ if [ "$param" = "p_disjoint_bits" ] || [ "$param" = "p_level_num" ]; then
+ LOCAL_DDCMDS="${LOCAL_DDCMDS}; echo 1 > "
+ LOCAL_DDCMDS="${LOCAL_DDCMDS}/sys/module/test_dynamic_debug/parameters/do_classes"
+ echo 1 > /sys/module/test_dynamic_debug/parameters/do_classes
+ fi
+
+ log_stop
+
+ # 2. If it is a state-controlling parameter, verify runtime unsetting (S2 ➔ R1)
+ if [ "$param" = "p_disjoint_bits" ] || [ "$param" = "p_level_num" ]; then
+ # Dynamically write 0 to unset the parameter at runtime via sysfs
+ LOCAL_DDCMDS="echo 0 > /sys/module/test_dynamic_debug/parameters/${param}"
+ echo 0 > "/sys/module/test_dynamic_debug/parameters/${param}"
+
+ # Verify that the resulting control-file state is completely cleared
+ local hash_unset=$(slice_and_hash_ddctrl '\[test_dynamic_debug\]')
+ local default_hash="fd89900c8614f23c3a6e8a8d45aa3280"
+ # Pristine fully-disabled slice hash
+
+ if [ "$hash_unset" != "$default_hash" ]; then
+ echo -e "${RED}: Runtime unsetting failed for ${param}! " \
+ "Expected default-disabled, got ${hash_unset}${NC}"
+ exit $ksft_fail
+ else
+ [ "$V" -ge 2 ] && echo -e "${GREEN}✔ Proven Runtime Unset: " \
+ "echo 0 > ${param} successfully cleared callsites${NC}"
+ fi
+ fi
+}
+
+# ==============================================================================
+function ifrmmod {
+ [ "${LACK_TMOD:-0}" -eq 1 ] && return
+ grep -q "^$1 " /proc/modules 2>/dev/null && rmmod $1
+}
+
+# ==============================================================================
+# FEATURE TESTS (FT_*)
+#
+# test legal queries which should execute and return 0 (success)
+# so we dont look for errors in dmesg
+function FT_grammar_ok {
+ v_echo "${GREEN}# GRAMMAR_OK_TESTS ${NC}"
+ ddcmd "+_"
+ ddcmd "-_"
+
+ # use 4 keywords (max 9 words inc flags)
+ ddcmd "module foo file bar.c func buz class D2_CORE +_" # 4 keywords
+ #ddcmd "module foo file bar.c func buz class D2 line 100 +_" # 5 keywords
+
+ # 3. Dedicated lineno range grammar assertions (side-effect-free proofs)
+ ddcmd "line 42 +_" # test exact line syntax
+ ddcmd "line 10- +_" # test open-ended line range (starting at 10)
+ ddcmd "line -100 +_" # test open-ended line range (ending at 100)
+ ddcmd "line 10-100 +_" # test closed-interval line range
+
+ # 4. Dedicated colon-delimited file:line and file:func assertions
+ ddcmd "file a_file.c:1-100 +_" # test file:linerange syntax
+ ddcmd "file b_file.c:30 +_" # test file:exact_line syntax
+ ddcmd "file c_file.c:c_func +_" # test file:function_name syntax
+ ddcmd "file c_file.c:start_* +_" # test file:wildcard_function syntax
+
+ # 5. Advanced formatting and separator checks (side-effect-free proofs)
+ ddcmd "format \"space\\040here\" +_" # test format query with octal escape
+ #ddcmd "module,foo +_" # test comma token separator syntax
+ ddcmd "func *my_func* +_" # test wildcard func syntax
+ ddcmd "file drivers/usb/* +_" # test wildcard file path syntax
+}
+
+# test grammar, no actual sites chosen/changed
+# use dyndbg's embedded comments in queries
+function FT_grammar_errs {
+ v_echo "${GREEN}# GRAMMAR_ERROR_TESTS ${NC}"
+ ddcmd =_
+ local verbose
+
+ # Reset before loop
+ echo 0 > /sys/module/dynamic_debug/parameters/verbose
+
+ # Sequence verbose level 0..3 to verify error diagnostics across all verbosity states!
+ for verbose in 1 2 3; do
+ echo $verbose > /sys/module/dynamic_debug/parameters/verbose
+
+ ddcmd_err 'module foo format "parse +p' # unclosed double quote
+
+ # comments in queries tell the error in the logs
+ ddcmd_err "module foo unknown_keyword value # no flag err"
+ ddcmd_err "module foo %pm # bad flag-op "
+ ddcmd_err "module foo +pfmHKDD # bad flags after good "
+
+ ddcmd_err "w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 w13 w 14 w15 w16 # too many tokens"
+ ddcmd_err "func w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 +p # bad keyword w3"
+ ddcmd_err "module foo line =_ # no line val"
+
+ #
+ ddcmd_err "func foo func bar =_ # func used 2x"
+ ddcmd_err "module foo module baz =_ # module used 2x"
+ ddcmd_err "class D2_CORE class D2_KMS +p # class used 2x"
+ ddcmd_err "module foo +x # unrecognized flag character"
+
+ # line value errs
+ ddcmd_err "line 10 line 20 +l # line used 2x"
+ ddcmd_err "line 10a +pl # line value trailing garbage"
+ ddcmd_err "line 100-10 +pf # line range error (last < 1st)"
+ done
+
+ # Reset to default verbose level 0 at the end of basic errors
+ echo 0 > /sys/module/dynamic_debug/parameters/verbose
+ ddcmd =_
+}
+
+# these queries run against builtin module: params, and change flags.
+# the state-of-interest, in control file, is found by path: kernel/params.c
+function FT_basic_queries {
+ v_echo "${GREEN}# BASIC_TESTS ${NC}"
+ if [ $LACK_DD_BUILTIN -eq 1 ]; then
+ echo "SKIP - test requires params, which is a builtin module"
+ return
+ fi
+ ddcmd =_ # zero everything
+
+ ddcmd "module params +mf" 'kernel/params.c'
+ ddcmd "module params +l" 'kernel/params.c'
+ ddcmd "module params -m" 'kernel/params.c'
+ ddcmd "module params =_" 'kernel/params.c'
+
+ # multi-query commands split on ; on a single line
+ ddcmd "module params +mf ; module params func parse_args +sl" 'kernel/params.c'
+
+ # 4. Verify multi-cmd input, newline separated, with embedded comments
+ ddcmd =_ # reset before multiline query to capture full transition
+ ddcmd "module params =_ # clear params
+ module params +ml # set flags
+ module params func parse_args +fs # other flags" 'kernel/params.c'
+
+ ddcmd =_
+}
+
+function FT_path_module_queries {
+ v_echo "${GREEN}# TEST_PATH_MODULE_QUERIES ${NC}"
+ ddcmd =_
+
+ # Find how many 'main' modules we have in total (by basename)
+ # Use a precise OR pattern to match exactly [main] or [*/main] and avoid irqdomain
+ local total_main=$(grep -c "\[main\]\|\[[^]]*/main\]" /proc/dynamic_debug/control)
+ v_echo "# found $total_main total 'main' modules"
+
+ if [ $total_main -eq 0 ]; then
+ echo "SKIP - no 'main' modules found to test slashes"
+ return
+ fi
+
+ # Verify a robust, cross-query state-interaction handshake between
+ # narrow path and wide wildcard/basename queries. This dynamically
+ # proves they interact with the exact same underlying callsites!
+
+ # 1. Turn ON specific path, verified under '[init/main]' range
+ ddcmd "module 'init/main' +p" "init/main.c"
+
+ # 2. Turn OFF using wide wildcard query,
+ ddcmd "module '*/main' =_" "init/main.c"
+
+ # 3. Turn ON using wide unscoped basename,
+ ddcmd "module 'main' +p" "init/main.c"
+
+ # 4. Turn OFF using specific narrow path,
+ ddcmd "module 'init/main' =_" "init/main.c"
+}
+
+function FT_hyphen_underscore {
+ v_echo "${GREEN}# TEST_HYPHEN_UNDERSCORE ${NC}"
+ ddcmd =_
+
+ # Find a module with a hyphen in its name (e.g., from the control file)
+ local mod_with_hyphen
+ mod_with_hyphen=$(awk -F'[][]' \
+ '/^[^#:]+:[0-9]+/ { if ($2 ~ /-/) { print $2; exit } }' \
+ /proc/dynamic_debug/control)
+
+ if [ -z "$mod_with_hyphen" ]; then
+ echo "SKIP - no module with hyphen found in /proc/dynamic_debug/control"
+ return
+ fi
+
+ v_echo "# testing hyphen/underscore equivalence for module: $mod_with_hyphen"
+ local mod_with_underscore=$(echo "$mod_with_hyphen" | tr '-' '_')
+ local base_hyphen=$(basename "$mod_with_hyphen")
+ local slice_pattern="\[[^]]*$base_hyphen\]"
+
+ # 1. Enable using literal hyphen name, and record the state fingerprint
+ v_echo "# trying hyphen name: $mod_with_hyphen"
+ ddcmd "module $mod_with_hyphen +p"
+ # verify_file_slice "$slice_pattern"
+ local hash_hyphen=$(slice_and_hash_ddctrl "$slice_pattern")
+
+ # 2. Disable and enable using underscore name, record the state fingerprint
+ ddcmd =_
+ v_echo "# trying underscore name: $mod_with_underscore"
+ ddcmd "module $mod_with_underscore +p"
+ # verify_file_slice "$slice_pattern"
+ local hash_underscore=$(slice_and_hash_ddctrl "$slice_pattern")
+
+ # Real-time mathematical proof of hyphen/underscore name equivalence!
+ if [ "$hash_hyphen" != "$hash_underscore" ]; then
+ echo -e "${RED}: Hyphen/Underscore equivalence check failed! " \
+ "Fingerprints do not match.${NC}"
+ echo -e "Hyphen name state hash: $hash_hyphen"
+ echo -e "Underscore name state hash: $hash_underscore"
+ exit $ksft_fail
+ else
+ v_echo "${GREEN}: Proven: Hyphen/Underscore literal name equivalence matches!${NC}"
+ fi
+
+ # Try kbasename with hyphen (if it has a path)
+ if [ "$base_hyphen" != "$mod_with_hyphen" ]; then
+ ddcmd =_
+ v_echo "# trying hyphen kbasename: $base_hyphen"
+ ddcmd "module $base_hyphen +pmf"
+ # verify_file_slice "$slice_pattern" # omitted: slice contains dynamic
+ # module info which drifts across different targets
+ local hash_base_hyphen=$(slice_and_hash_ddctrl "$slice_pattern")
+
+ # Prove kbasename hyphen name matches literal path hyphen name (with different flags)!
+ v_echo "# trying full path hyphen with pmf flags"
+ ddcmd =_
+ ddcmd "module $mod_with_hyphen +pmf"
+ local hash_path_pmf=$(slice_and_hash_ddctrl "$slice_pattern")
+ if [ "$hash_path_pmf" != "$hash_base_hyphen" ]; then
+ echo -e "${RED}: Hyphen kbasename check failed! " \
+ "Fingerprints do not match full-path hyphen enablement.${NC}"
+ exit $ksft_fail
+ else
+ v_echo "${GREEN}: Proven: Hyphen kbasename matches " \
+ "full-path hyphen enablement!${NC}"
+ fi
+ fi
+
+ # 4. Try kbasename with underscore
+ local base_underscore=$(echo "$base_hyphen" | tr '-' '_')
+ ddcmd =_
+ v_echo "# trying underscore kbasename: $base_underscore"
+ ddcmd "module $base_underscore +pmf"
+ # verify_file_slice "$slice_pattern" # omitted: slice contains dynamic
+ # module info which drifts across different targets
+ local hash_base_underscore=$(slice_and_hash_ddctrl "$slice_pattern")
+
+ # Real-time mathematical proof of hyphen/underscore kbasename equivalence!
+ if [ "$hash_base_hyphen" != "$hash_base_underscore" ] && \
+ [ -n "$hash_base_hyphen" ]; then
+ echo -e "${RED}: Hyphen/Underscore kbasename equivalence check " \
+ "failed! Fingerprints do not match.${NC}"
+ exit $ksft_fail
+ elif [ -n "$hash_base_hyphen" ]; then
+ v_echo "${GREEN}: Proven: Hyphen/Underscore kbasename " \
+ "equivalence matches!${NC}"
+ fi
+
+ ddcmd =_
+}
+
+# test parsing on spaces, commas. testing agains builtin [kernel/params]
+function FT_comma_terminators {
+ v_echo "${GREEN}# COMMA_TERMINATOR_TESTS ${NC}"
+ if [ $LACK_DD_BUILTIN -eq 1 ]; then
+ echo "SKIP - test requires params, which is a builtin module"
+ return
+ fi
+ ddcmd "module params =_"
+
+ # 1. Verify transition after commas-as-spaces query and splitting on @
+ # Use non-printing decorator flags (+mf) to avoid print-enabling (+p) and
+ # prevent syslog pollution
+ #ddcmd "module,params,=_ @ module,params,+mf" 'kernel/params.c'
+
+ # 2. Verify transition after ignored-commas query
+ ddcmd ",module ,, , params, -p" 'kernel/params.c'
+
+ # 3. Verify transition after quoted-commas query
+ ddcmd " , module ,,, , params, -m" 'kernel/params.c'
+
+ ddcmd =_
+}
+
+# testing classmap-based query enablers and class configurations
+function FT_test_classes {
+ v_echo "${GREEN}# TEST_CLASSES - classmap-based query enablers and class configs ${NC}"
+
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+ ddcmd =_
+
+ # 1. Verify initial multi-query enablement state via file slice
+ modprobe test_dynamic_debug \
+ dyndbg="class,D2_CORE,+pf;class,D2_KMS,+ps;class,D2_ATOMIC,+pm"
+ verify_file_slice '\[test_dynamic_debug\]'
+
+ # 2. Verify state transition and live-printing end-to-end via ddcmd_load!
+ ddcmd_load "class,D2_CORE,+pmf@class,D2_KMS,+pls@class,D2_ATOMIC,+pml" \
+ '\[test_dynamic_debug\]' \
+ "/sys/module/test_dynamic_debug/parameters/do_classes" "1"
+
+ ifrmmod test_dynamic_debug
+}
+
+function FT_classmap_inheritance {
+ v_echo "${GREEN}# TEST_MOD_SUBMOD ${NC}"
+
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+ ddcmd =_
+
+ # modprobe with class enablements
+ modprobe test_dynamic_debug \
+ dyndbg=class,D2_CORE,+pf@class,D2_KMS,+pt@class,D2_ATOMIC,+pm
+
+ verify_file_slice '\[test_dynamic_debug\]'
+
+ modprobe test_dynamic_debug_submod
+ verify_file_slice '\[test_dynamic_debug_submod\]'
+
+ # Verify 3 class changes
+ ddcmd "class,D2_CORE,+pmf @ class,D2_KMS,+plt @ class,D2_ATOMIC,+pml" \
+ 'test_dynamic_debug' "# add some prefixes"
+
+ # now work the classmap-params
+ # fresh start, to clear all above flags (test-fn limits)
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+ modprobe test_dynamic_debug_submod # get supermod too
+
+ echo 1 > /sys/module/test_dynamic_debug/parameters/p_disjoint_bits
+ echo 4 > /sys/module/test_dynamic_debug/parameters/p_level_num
+ # 2 mods * ( V1-3 + D2_CORE )
+ verify_file_slice 'test_dynamic_debug'
+ echo 3 > /sys/module/test_dynamic_debug/parameters/p_disjoint_bits
+ echo 0 > /sys/module/test_dynamic_debug/parameters/p_level_num
+ # 2 mods * ( D2_CORE, D2_DRIVER )
+ verify_file_slice 'test_dynamic_debug'
+ echo 0x16 > /sys/module/test_dynamic_debug/parameters/p_disjoint_bits
+ echo 0 > /sys/module/test_dynamic_debug/parameters/p_level_num
+ # 2 mods * ( D2_DRIVER, D2_KMS, D2_ATOMIC )
+ verify_file_slice 'test_dynamic_debug'
+
+ # recap DRM_USE_DYNAMIC_DEBUG regression
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+ # set super-mod params at load-time
+ modprobe test_dynamic_debug p_disjoint_bits=0x16 p_level_num=5
+ verify_file_slice '\[test_dynamic_debug\]'
+ modprobe test_dynamic_debug_submod
+ # see them picked up by submod
+ verify_file_slice 'test_dynamic_debug'
+
+ # Real-time mathematical proof that load-time (modprobe) parameter parsing
+ # and runtime (sysfs write) parameter configurations are perfectly equivalent!
+ local hash_modprobe=$(slice_and_hash_ddctrl '\[test_dynamic_debug\]')
+
+ # Fresh load with default parameters, then configure them dynamically at runtime
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+ modprobe test_dynamic_debug
+ modprobe test_dynamic_debug_submod
+ echo 0x16 > /sys/module/test_dynamic_debug/parameters/p_disjoint_bits
+ echo 5 > /sys/module/test_dynamic_debug/parameters/p_level_num
+
+ local hash_sysfs=$(slice_and_hash_ddctrl '\[test_dynamic_debug\]')
+ if [ "$hash_modprobe" != "$hash_sysfs" ]; then
+ echo -e "${RED}: Load-time vs runtime parameter equivalence check failed!${NC}"
+ exit $ksft_fail
+ else
+ v_echo "${GREEN}: Proven: parameter load-time (modprobe) " \
+ "and runtime (sysfs write) are equivalent!${NC}"
+ fi
+
+ # --- Live Content Fingerprinting Phase ---
+ log_start
+ echo 1 > /sys/module/test_dynamic_debug/parameters/do_classes
+ echo 1 > /sys/module/test_dynamic_debug_submod/parameters/do_classes
+ log_stop
+
+ ifrmmod test_dynamic_debug_submod
+ ifrmmod test_dynamic_debug
+}
+
+function FT_modprobe_w_param {
+ v_echo "${GREEN}# TEST_MODPROBES ${NC}"
+ ddcmd =_
+ local verbose
+
+ for verbose in 1 2 3 4 0; do
+ echo $verbose > /sys/module/dynamic_debug/parameters/verbose
+
+ # Verify each parameter load sequence with 100% DRY modularity
+ verify_modprobe_param_logging "do_prints" "1" "classes_verb${verbose}"
+
+ #verify_modprobe_param_logging "do_classes" "1" "classes_verb${verbose}"
+ #verify_modprobe_param_logging "do_bulk" "1" "bulk_verb${verbose}"
+
+ # Sequence composite bitmasks to verify disjoint bit transitions
+ for mask in "0x05" "0x12" "0x1f" "0x00"; do
+ verify_modprobe_param_logging "p_disjoint_bits" "$mask" \
+ "disjoint_${mask}_verb${verbose}"
+ done
+
+ # Sequence levels to verify both growing and shrinking verbose transitions
+ for lvl in "3" "5" "4" "0"; do
+ verify_modprobe_param_logging "p_level_num" "$lvl" \
+ "level_${lvl}_verb${verbose}"
+ done
+ done # verbose loop
+ ddcmd =_
+}
+
+# Built-in Feature Tests (Can run on any CONFIG_DYNAMIC_DEBUG kernel, modular or monolithic)
+builtin_tests=(
+ FT_grammar_ok
+ FT_grammar_errs
+ FT_basic_queries
+ #FT_path_module_queries
+ #FT_hyphen_underscore
+ #FT_comma_terminators
+)
+
+# Modular Feature Tests (Require CONFIG_MODULES=y and test_dynamic_debug*.ko available)
+modular_tests=(
+ #FT_test_classes
+ #FT_classmap_inheritance
+ #FT_modprobe_w_param
+)
+
+# ==============================================================================
+# GOLDEN_RECORDS (MD5 Fingerprint Verification Database)
+#
+# This database stores the expected invariant log content hashes for our tests.
+# Since the key has the line-number of the callsite, we dont yet
+# support looping over a test-call, maybe we'll need to address that
+# later.
+#
+# NB: records have lineno of the test in code above. table at bottom
+# means inserts dont shift test-lines.
+#
+# ==============================================================================
+function GOLDEN_RECORDS {
+ cat << 'EOF' | {
+#K= bb85d28739876b0fdf1eb426baae8ef7 FT_grammar_errs.1 dmesg
+#K= 9c405e06fcea50284df82d1ae9692403 FT_grammar_errs.2 dmesg
+#K= 0b0d9b0e6f4c45c7d75d699fd3b6aa93 FT_grammar_errs.3 dmesg
+#K= e008644e159c2a9d99a8d0806c06e71e FT_grammar_errs.4 dmesg
+#K= 11833e6cf8fe8b03869aa4f669b63398 FT_grammar_errs.5 dmesg
+#K= e85a4b665f5ef141e3f4d4505dec1da1 FT_grammar_errs.6 dmesg
+#K= 3e1eba65bd936e1276c7e1d76b399595 FT_grammar_errs.7 dmesg
+#K= e3ac86676e309d4e72ff3da287736c29 FT_grammar_errs.8 dmesg
+#K= 466d325c23519119f754fa7860bae7c6 FT_grammar_errs.9 dmesg
+#K= 51c489366cd86c16280858b95adf34ac FT_grammar_errs.10 dmesg
+#K= dfc632c6c0cd6206b6141dbc5fb989db FT_grammar_errs.11 dmesg
+#K= 69650fc26e97a78082bd851eb4a5c83b FT_grammar_errs.12 dmesg
+#K= 5cfe5665962a482f416836793067f2a2 FT_grammar_errs.13 dmesg
+#K= 661cc9a2d8d3c381bfeae41392432575 FT_grammar_errs.14 dmesg
+#K= 5dd0accf9be37ce2f4bf697e9bdebe30 FT_grammar_errs.15 dmesg
+#K= bd8d9cf661c9aca54dac93c74cfc1501 FT_grammar_errs.16 dmesg
+#K= e991391048f2fdffaaed99ba36b787ed FT_grammar_errs.17 dmesg
+#K= 82d99278f471ebfbf47eeb19898525cf FT_grammar_errs.18 dmesg
+#K= 3c8d268c94b0bf97e64999654cd840c4 FT_grammar_errs.19 dmesg
+#K= 1a448ff3f1df9155cc3acf1e124f24f5 FT_grammar_errs.20 dmesg
+#K= 76c8a33c585d414fcc3f1c8b5ea5b4b3 FT_grammar_errs.21 dmesg
+#K= 90cfc66e9d61f5c448353b97baa99145 FT_grammar_errs.22 dmesg
+#K= 37a51dfd6e84cb64b69d3a0e23e68ceb FT_grammar_errs.23 dmesg
+#K= 3aefa325b63742710b45e4531152c9d8 FT_grammar_errs.24 dmesg
+#K= bc8d2c23282cd6834879bc1f1b303d05 FT_grammar_errs.25 dmesg
+#K= 092ec5530d875be1dc96ad4202fc7dc3 FT_grammar_errs.26 dmesg
+#K= 98caa2677e9070953a852b27ab57861e FT_grammar_errs.27 dmesg
+#K= 8d755baa707c7b3b67491bf84708ef08 FT_grammar_errs.28 dmesg
+#K= 5dd0accf9be37ce2f4bf697e9bdebe30 FT_grammar_errs.29 dmesg
+#K= 2e37642dc8aee3d04b8a54a76b3b891c FT_grammar_errs.30 dmesg
+#K= 593c8b4c41932e3bde564a0008627ced FT_grammar_errs.31 dmesg
+#K= 85a2a0bee8d0cd10f883aa5477d62efb FT_grammar_errs.32 dmesg
+#K= 3c8d268c94b0bf97e64999654cd840c4 FT_grammar_errs.33 dmesg
+#K= 1a448ff3f1df9155cc3acf1e124f24f5 FT_grammar_errs.34 dmesg
+#K= d9f0cffb0898c54690735a71b0d4f3bf FT_grammar_errs.35 dmesg
+#K= 4cf728b6a32043ea5336a14d0c435312 FT_grammar_errs.36 dmesg
+#K= a11ae3f86f4e5f539fd6168d318f74b4 FT_grammar_errs.37 dmesg
+#K= c7d3830b0391f6a773205066b64e15f9 FT_grammar_errs.38 dmesg
+#K= 5c506ea2bfd050439b580b91543e9be7 FT_grammar_errs.39 dmesg
+#K= a257660007408f7bb0cb43c99b1c7bb8 FT_grammar_errs.40 dmesg
+#K= 0082939f100300a7b327b143cf40cb43 FT_grammar_errs.41 dmesg
+#K= 657ac4360f978687ad25a705b2bb89f1 FT_grammar_errs.42 dmesg
+#K= 9a7aaed40738f1c5203af2e421a20bc2 FT_basic_queries.1 "kernel/params.c"
+#K= 2a57a9e283a3912de3fa5e006fb330cc FT_basic_queries.2 "kernel/params.c"
+#K= fed3186684428b2f05bcf5df960c639c FT_basic_queries.3 "kernel/params.c"
+#K= 551f9801b6008553661453021ae6dfd3 FT_basic_queries.4 "kernel/params.c"
+#K= 33f7162ac85020894fab9752ef07b89c FT_basic_queries.5 "kernel/params.c"
+#K= 544d955c3bb9d3c78704f780d91396e0 FT_basic_queries.6 "kernel/params.c"
+
+
+EOF
+ # Read the K-recs and skip those for tests that can't run
+ while read -r line; do
+ # Filter built-in if needed
+ if [ "${LACK_DD_BUILTIN:-0}" -eq 1 ]; then
+ # Extract pattern (4th field) from #K= line
+ local pattern=$(echo "$line" | awk '{print $4}')
+ if [[ "$pattern" == *params* || "$pattern" == *main* ]]; then
+ continue
+ fi
+ fi
+ # Filter modular if needed
+ if [ "${LACK_TMOD:-0}" -eq 1 ]; then
+ # Extract label (3rd field) from #K= line
+ local label=$(echo "$line" | awk '{print $3}')
+ if [[ "$label" == FT_test_classes* \
+ || "$label" == FT_classmap_inheritance* \
+ || "$label" == FT_modprobe_w_param* ]]; then
+ continue
+ fi
+ fi
+ echo "$line"
+ done
+ }
+}
+
+# ==============================================================================
+# Run tests
+
+# Clear any stale seen/unregistered/drifted hashes from previous runs
+rm -f "$SEEN_HASHES_FILE" "$UNREG_HASHES_FILE" "$DRIFT_HASHES_FILE"
+
+ifrmmod test_dynamic_debug
+
+# Check if loadable module support or our test modules are missing/builtin
+LACK_TMOD=0
+if [ -d "/sys/module/test_dynamic_debug" ]; then
+ # If module is present but not in /proc/modules,
+ # it is a builtin module (cannot unload/reload)
+ if ! grep -q "^test_dynamic_debug " /proc/modules 2>/dev/null; then
+ LACK_TMOD=1
+ fi
+else
+ # Check if we can modprobe it from disk
+ modprobe -q -n test_dynamic_debug || LACK_TMOD=1
+fi
+
+# 1. Run all Built-in Feature Tests
+v_echo "${GREEN}# RUNNING BUILT-IN FEATURE TESTS ${NC}"
+for test_func in "${builtin_tests[@]}"; do
+ $test_func
+ v_echo ""
+done
+
+# 2. Run Modular Feature Tests only if test modules are available
+if [ $LACK_TMOD -eq 0 ]; then
+ v_echo "${GREEN}# RUNNING MODULAR FEATURE TESTS ${NC}"
+ for test_func in "${modular_tests[@]}"; do
+ $test_func
+ v_echo ""
+ done
+else
+ v_echo "${YELLOW}# SKIPPING MODULAR TESTS: test_dynamic_debug.ko not available ${NC}"
+fi
+
+if [ "$V" -ge 1 ]; then
+ echo -en "${GREEN}# Done on: "
+ date
+ echo -en "${NC}"
+fi
+
+audit_golden_records
+
+# Output consolidated blocks of unregistered and drifted fingerprints
+failed=0
+
+if [ -s "$UNREG_HASHES_FILE" ]; then
+ echo -e "${YELLOW}\n# --- Unregistered Baselines ---"
+ cat "$UNREG_HASHES_FILE"
+ echo -e "# ------------------------------${NC}"
+ rm -f "$UNREG_HASHES_FILE"
+ failed=1
+fi
+
+if [ -s "$DRIFT_HASHES_FILE" ]; then
+ echo -e "${RED}\n# --- Drifted Baselines ---"
+ cat "$DRIFT_HASHES_FILE"
+ echo -e "# -------------------------${NC}"
+ rm -f "$DRIFT_HASHES_FILE"
+ failed=1
+fi
+
+# Cleanup
+rm -f "$UNREG_HASHES_FILE" "$DRIFT_HASHES_FILE"
+
+if [ $failed -eq 1 ]; then
+ [ "$K" -eq 1 ] && echo "fake success" && exit $ksft_pass
+ exit $ksft_fail
+fi
+
+exit $ksft_pass
+
diff --git a/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh b/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh
new file mode 100644
index 000000000000..c6d60495d7ba
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh
@@ -0,0 +1,384 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic, zero-dependency syslog and file-slicing verification helper library.
+
+# Default APP to DYNDBG if not already set
+APP="${APP:-DYNDBG}"
+APP_LOWER=$(echo "$APP" | tr '[:upper:]' '[:lower:]')
+
+# Global files for tracking seen, unregistered, and drifted hashes
+SEEN_HASHES_FILE="/tmp/${APP_LOWER}_seen_hashes_$$"
+UNREG_HASHES_FILE="/tmp/${APP_LOWER}_unreg_hashes_$$"
+DRIFT_HASHES_FILE="/tmp/${APP_LOWER}_drift_hashes_$$"
+
+# Global variables for tracking active function transitions and sequence resets
+LAST_FT_FUNC=""
+TEST_SEQ_CTR=0
+ACTIVE_RESOLVED_LABEL=""
+
+# Global variables for bookending state transitions and local stimulus tracking
+IN_BOOKEND=0
+LOCAL_DDCMDS=""
+
+# Global variable to track the active dmesg block label
+ACTIVE_LOG_LABEL=""
+
+# Helper function to auto-resolve active FT_ test and sequence label
+function rdi_resolve_label {
+ local caller_fn=""
+ # Traverse the call stack to find the active Feature Test function (FT_*)
+ for fn in "${FUNCNAME[@]}"; do
+ if [[ "$fn" == FT_* ]]; then
+ caller_fn="$fn"
+ break
+ fi
+ done
+
+ # Fallback to the immediate caller if no FT_ is in the stack
+ if [ -z "$caller_fn" ]; then
+ caller_fn="${FUNCNAME[1]:-}"
+ fi
+
+ # Automatically reset sequence counter if the executing function has transitioned
+ if [ -n "$caller_fn" ] && [ "$caller_fn" != "$LAST_FT_FUNC" ]; then
+ TEST_SEQ_CTR=1
+ LAST_FT_FUNC="$caller_fn"
+ fi
+
+ if [ -n "$caller_fn" ]; then
+ ACTIVE_RESOLVED_LABEL="${caller_fn}.${TEST_SEQ_CTR}"
+ else
+ ACTIVE_RESOLVED_LABEL="${TEST_SEQ_CTR}"
+ fi
+}
+
+function log_start {
+ ((TEST_SEQ_CTR++))
+
+ rdi_resolve_label
+ ACTIVE_LOG_LABEL="$ACTIVE_RESOLVED_LABEL"
+
+ IN_BOOKEND=1
+
+ echo "${APP}_START_${ACTIVE_LOG_LABEL}_$$" > /dev/kmsg
+}
+
+function log_stop {
+ # Ends the dmesg capture block and verifies the slice
+ if [ -z "$ACTIVE_LOG_LABEL" ]; then
+ echo "Error: log_stop called without a matching log_start!" >&2
+ return 1
+ fi
+
+ echo "${APP}_END_${ACTIVE_LOG_LABEL}_$$" > /dev/kmsg
+
+ # Verify the dmesg slice
+ verify_dmesg_slice "$ACTIVE_LOG_LABEL"
+
+ # Reset active state, bookend flag, and command tracker at teardown
+ ACTIVE_LOG_LABEL=""
+ IN_BOOKEND=0
+ LOCAL_DDCMDS=""
+}
+
+function verify_fingerprint {
+ # Verifies a calculated fingerprint against the GOLDEN_RECORDS database
+ # $1 - unique test key (e.g. normal_513)
+ # $2 - optional extra args (e.g. "dmesg:1" or "control:pattern")
+ # $3 - the calculated fingerprint hash to verify
+ # $4 - description of what was captured (e.g. "Dmesg Log" or "File Slice")
+ # $5 - the raw captured text block (to display in case of mismatch)
+
+ local label="$1"
+ local extra_args="$2"
+ local fingerprint="$3"
+ local capture_desc="$4"
+ local raw_capture="$5"
+
+ # Require GOLDEN_RECORDS to be defined in the caller script
+ if ! declare -f GOLDEN_RECORDS >/dev/null; then
+ echo "Error: GOLDEN_RECORDS() is not defined in the caller script." >&2
+ return 1
+ fi
+
+ # Resolve the expected hash specifically for this label
+ local expected_hash_field
+ expected_hash_field=$(GOLDEN_RECORDS | \
+ grep -E "[[:space:]]${label}[[:space:]]" | head -n1 | awk '{print $2}')
+
+ local matched=0
+ local h
+ local OLD_IFS="$IFS"
+ IFS=","
+ for h in $expected_hash_field; do
+ if [ "$h" = "$fingerprint" ]; then
+ matched=1
+ break
+ fi
+ done
+ IFS="$OLD_IFS"
+
+ # Strictly verify that the computed fingerprint matches any
+ # expected hash for this label
+ if [ -n "$expected_hash_field" ] && [ $matched -eq 1 ]; then
+ local short_hash="${fingerprint:0:12}"
+ [ "$V" -ge 1 ] && echo -e "${GREEN}✔ Verified '${label}' " \
+ "(${short_hash}) [via: '${LOCAL_DDCMDS}']${NC}"
+
+ if [ "$V" -ge 2 ]; then
+ echo -e "${CYAN}--- Captured Invariant ${capture_desc} Output ($label) ---"
+ printf "#K= %-32s %-24s\n" "${fingerprint}" "${label}"
+ echo "$raw_capture"
+ echo -e "-----------------------------------${NC}"
+ fi
+ echo "$fingerprint" >> "$SEEN_HASHES_FILE"
+
+ # Telemetry
+ local T="${T:-0}"
+ if [ "$T" != "0" ] && [ "$T" != "n" ]; then
+ echo "TELEMETRY: $label $fingerprint VERIFIED $extra_args [via:\"$LOCAL_DDCMDS\"]"
+ fi
+ else
+ # Failure path: display mismatch and append to corrections
+ local status_str="UNREGISTERED"
+ local stimulus="${LOCAL_DDCMDS:-direct write to control}"
+ if [ -n "$expected_hash_field" ]; then
+ local short_expected="${expected_hash_field:0:12}"
+ local short_got="${fingerprint:0:12}"
+ echo -e "${RED}: DRIFT for '${label}'${NC}"
+ echo -e " Range: ${extra_args}"
+ echo -e " Stimulus: ${stimulus}"
+ echo -e " Expected: '${short_expected}' (${expected_hash_field})"
+ echo -e " Got: '${short_got}' (${fingerprint})${NC}"
+ status_str="DRIFTED"
+ else
+ echo -e "${YELLOW}: NO RECORD for '${label}'${NC}"
+ echo -e " Range: ${extra_args}"
+ echo -e " Stimulus: ${stimulus}${NC}"
+ fi
+
+ # Telemetry
+ local T="${T:-0}"
+ if [ "$T" != "0" ] && [ "$T" != "n" ]; then
+ echo "TELEMETRY: $label $fingerprint $status_str $extra_args " \
+ "[via:\"$CUMULATIVE_DDCMDS\"]"
+ fi
+
+ echo -e "\nAdd or replace this line in GOLDEN_RECORDS():"
+ printf "#K= %-32s %-24s %s\n" "${fingerprint}" "${label}" "${extra_args}"
+ echo -e "\n--- Captured Invariant ${capture_desc} Output ---"
+ if [ "$capture_desc" = "File Slice" ]; then
+ echo "$raw_capture" | \
+ sed -E "s/ =([_a-z]*[a-z][_a-z]*) / ${YELLOW}=\1${NC} /g"
+ else
+ echo "$raw_capture"
+ fi
+ echo -e "-----------------------------------${NC}"
+
+ if [ "$status_str" = "DRIFTED" ]; then
+ printf "#K= %-32s %-24s %s\n" \
+ "${fingerprint}" "${label}" "${extra_args}" \
+ >> "$DRIFT_HASHES_FILE"
+ else
+ printf "#K= %-32s %-24s %s\n" \
+ "${fingerprint}" "${label}" "${extra_args}" \
+ >> "$UNREG_HASHES_FILE"
+ fi
+ fi
+}
+
+function verify_dmesg_slice {
+ # Slices dmesg, computes its hash, and verifies it against the database.
+ # $1 - unique test key (e.g. normal_513)
+ # $2 - optional start marker (defaults to ${APP}_START_${label})
+ # $3 - optional end marker (defaults to ${APP}_END_${label})
+
+ local label="$1"
+ local app="${APP:-DYNDBG}"
+ local start_marker="${2:-${app}_START_${label}_$$}"
+ local end_marker="${3:-${app}_END_${label}_$$}"
+ local extra_args="dmesg"
+
+ # 1. Capture the log slice (exactly once!)
+ local log_slice=$(dmesg | sed -n "/$start_marker/,/$end_marker/p" | \
+ grep -E -v "$start_marker|$end_marker" | \
+ sed -e 's/^\[[^]]*\] //' )
+
+ # 2. Compute its fingerprint
+ local fingerprint=$(echo "$log_slice" | tr -d '\r' | md5sum | cut -d' ' -f1)
+
+ # 3. Verify
+ verify_fingerprint "$label" "$extra_args" "$fingerprint" "Dmesg Log" "$log_slice"
+}
+
+function slice_by_grep {
+ # Isolate lines matching a pattern from a file
+ # $1 - pattern to grep (returns entire file if empty or "*")
+ # $2 - file path (reads $CONTROL_FILE if not provided)
+ local pattern="$1"
+ local file_path="${2:-$CONTROL_FILE}"
+
+ if [ -z "$pattern" ] || [ "$pattern" = "*" ]; then
+ cat "$file_path"
+ else
+ grep "$pattern" "$file_path"
+ fi
+}
+
+function verify_file_slice {
+ # Captures a file slice by pattern, computes its hash,
+ # and verifies it against the database.
+ # $1 - pattern to slice
+ # $2 - optional file path (defaults to $CONTROL_FILE)
+ # $3 - optional extra args
+
+ local pattern="$1"
+ local file="${2:-$CONTROL_FILE}"
+ local extra_args="$3"
+
+ # Always auto-resolve label via call stack sequence resets!
+ ((TEST_SEQ_CTR++))
+ rdi_resolve_label
+ local label="$ACTIVE_RESOLVED_LABEL"
+
+ if [ -z "$extra_args" ]; then
+ extra_args="$pattern"
+ if [ "$pattern" != "*" ]; then
+ extra_args="\"$pattern\""
+ fi
+ fi
+
+ # 1. Capture the file slice (exactly once!)
+ local slice=$(slice_by_grep "$pattern" "$file")
+
+ # 2. Compute its fingerprint
+ local fingerprint=$(echo "$slice" | tr -d '\r' | md5sum | cut -d' ' -f1)
+
+ # 3. Verify
+ verify_fingerprint "$label" "$extra_args" "$fingerprint" "File Slice" "$slice"
+}
+
+# Global variables for bookending state transitions
+BEFORE_CAPTURE_SLICE=""
+BEFORE_CAPTURE_PATTERN=""
+BEFORE_CAPTURE_FILE=""
+
+function capture_before {
+ # Captures and stores the 'before' state for a file slice transition
+ # $1 - pattern to slice
+ # $2 - optional file path (defaults to $CONTROL_FILE)
+
+ BEFORE_CAPTURE_PATTERN="$1"
+ BEFORE_CAPTURE_FILE="${2:-$CONTROL_FILE}"
+ BEFORE_CAPTURE_SLICE=$(slice_by_grep "$BEFORE_CAPTURE_PATTERN" "$BEFORE_CAPTURE_FILE")
+
+ IN_BOOKEND=1
+}
+
+function verify_after_change {
+ # Verifies the transition between the stored 'before' state and the current state
+ # $1 - optional unique test key (resolved via stack if empty)
+
+ local label="$1"
+
+ if [ -z "$label" ]; then
+ ((TEST_SEQ_CTR++))
+ rdi_resolve_label
+ label="$ACTIVE_RESOLVED_LABEL"
+ fi
+ local extra_args="$BEFORE_CAPTURE_PATTERN"
+ if [ "$BEFORE_CAPTURE_PATTERN" != "*" ]; then
+ extra_args="\"$BEFORE_CAPTURE_PATTERN\""
+ fi
+
+ if [ -z "$BEFORE_CAPTURE_PATTERN" ]; then
+ echo "Error: verify_after_change called without a matching capture_before!" >&2
+ return 1
+ fi
+
+ # 1. Capture the 'after' state (exactly once!)
+ local after_slice=$(slice_by_grep "$BEFORE_CAPTURE_PATTERN" "$BEFORE_CAPTURE_FILE")
+
+ # 2. Generate the unified diff, stripped of volatile diff headers AND hunk line-numbers
+ local transition_diff=$(diff -u <(echo "$BEFORE_CAPTURE_SLICE") <(echo "$after_slice") | \
+ tail -n +3 | \
+ sed -E 's/^@@ -[0-9]+.* \+[0-9]+.* @@/@@/g')
+
+ # 3. Compute its fingerprint
+ local fingerprint=$(echo "$transition_diff" | tr -d '\r' | md5sum | cut -d' ' -f1)
+
+ # 4. Verify the diff as the captured text block
+ verify_fingerprint "$label" "$extra_args" "$fingerprint" "File Change Diff" "$transition_diff"
+
+ # Reset state, bookend flag, and command tracker at teardown
+ BEFORE_CAPTURE_SLICE=""
+ BEFORE_CAPTURE_PATTERN=""
+ BEFORE_CAPTURE_FILE=""
+ IN_BOOKEND=0
+ LOCAL_DDCMDS=""
+}
+
+function audit_golden_records {
+ local seen_file="$SEEN_HASHES_FILE"
+
+ if [ ! -f "$seen_file" ]; then
+ return
+ fi
+
+ # Require GOLDEN_RECORDS to be defined in the caller script
+ if ! declare -f GOLDEN_RECORDS >/dev/null; then
+ return
+ fi
+
+ [ "${V:-0}" -ge 1 ] && echo -e "${YELLOW}# --- GOLDEN_RECORDS Audit ---${NC}"
+ local stale_found=0
+ local total_records=$(GOLDEN_RECORDS | grep -c "^#K=")
+
+ # Read each active record line from GOLDEN_RECORDS
+ while read -r line; do
+ # Extract the hash/hashes (second word) from the #K= line
+ local hash_field=$(echo "$line" | awk '{print $2}')
+
+ # Check if at least one of the comma-separated hashes was seen
+ local hash_seen=0
+ local h
+ local OLD_IFS="$IFS"
+ IFS=","
+ for h in $hash_field; do
+ if grep -q "$h" "$seen_file" 2>/dev/null; then
+ hash_seen=1
+ break
+ fi
+ done
+ IFS="$OLD_IFS"
+
+ # Check if this hash field was seen during the run
+ if [ $hash_seen -eq 0 ]; then
+ if [ $stale_found -eq 0 ]; then
+ # On first failure, print header if not already printed
+ [ "${V:-0}" -eq 0 ] && \
+ echo -e "${YELLOW}# --- GOLDEN_RECORDS Audit ---${NC}"
+ echo -e "${YELLOW}# The following GOLDEN_RECORDS entries " \
+ "were never hit and may be stale:${NC}"
+ stale_found=1
+ fi
+ echo -e "${YELLOW}#K_STALE= $line${NC}"
+ fi
+ done < <(GOLDEN_RECORDS | grep "^#K=" | grep -v "<md5_hash>")
+
+ if [ $stale_found -eq 0 ] && [ "${V:-0}" -ge 1 ]; then
+ echo -e "${GREEN}# All $total_records GOLDEN_RECORDS entries " \
+ "were successfully hit!${NC}"
+ fi
+
+ # Detect duplicate labels in the database
+ local dupes=$(GOLDEN_RECORDS | grep "^#K=" | awk '{print $3}' | sort | uniq -d)
+ if [ -n "$dupes" ]; then
+ echo -e "\n${RED}# WARNING: Duplicate labels detected in GOLDEN_RECORDS():${NC}"
+ echo "$dupes" | sed 's/^/# /'
+ fi
+
+ # Clean up
+ rm -f "$seen_file"
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 03/29] drm: Fix incorrect ccflags-y spelling inside Makefile
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
2026-07-21 20:56 ` [PATCH v7 01/29] params: fix a pr_debug(" %p ") use - already in MM-* Jim Cromie
2026-07-21 20:56 ` [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 04/29] drm: fix config dependent unused variable warning Jim Cromie
` (25 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Fix a longstanding spelling error in the DRM Makefile by changing
'CFLAGS-y' to 'ccflags-y'. This fixes CONFIG_DRM_USE_DYNAMIC_DEBUG
dependent addition of -DDYNAMIC_DEBUG_MODULE, which is needed to enable
dyndbg-does-drm-debug when only DYNAMIC_DEBUG_CORE is enabled.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
drivers/gpu/drm/Makefile | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e97faabcd783..b0d0a85cbd4f 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -3,7 +3,8 @@
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
-CFLAGS-$(CONFIG_DRM_USE_DYNAMIC_DEBUG) += -DDYNAMIC_DEBUG_MODULE
+ccflags-$(CONFIG_DRM_USE_DYNAMIC_DEBUG) += -DDYNAMIC_DEBUG_MODULE
+subdir-ccflags-$(CONFIG_DRM_USE_DYNAMIC_DEBUG) += -DDYNAMIC_DEBUG_MODULE
# Unconditionally enable W=1 warnings locally
# --- begin copy-paste W=1 warnings from scripts/Makefile.warn
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 04/29] drm: fix config dependent unused variable warning.
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (2 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 03/29] drm: Fix incorrect ccflags-y spelling inside Makefile Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 05/29] drm: Mark CONFIG_DRM_USE_DYNAMIC_DEBUG as unBROKEN Jim Cromie
` (24 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
__drm_printfn_dbg() does
int category = p->category;
then does:
if (drm_debug_enabled(category))
When CONFIG_DRM_USE_DYNAMIC_DEBUG=y, that macro doesn't reference the
category arg, because its optimized away, as not needed when dyndbg's
static-key is under the callsite. Silence the warning by passing
p->category directly.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
drivers/gpu/drm/drm_print.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c
index ded9461df5f2..ac0288dabfc0 100644
--- a/drivers/gpu/drm/drm_print.c
+++ b/drivers/gpu/drm/drm_print.c
@@ -216,9 +216,8 @@ void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf)
{
const struct drm_device *drm = p->arg;
const struct device *dev = drm ? drm->dev : NULL;
- enum drm_debug_category category = p->category;
- if (!__drm_debug_enabled(category))
+ if (!__drm_debug_enabled(p->category))
return;
__drm_dev_vprintk(dev, KERN_DEBUG, p->origin, p->prefix, vaf);
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 05/29] drm: Mark CONFIG_DRM_USE_DYNAMIC_DEBUG as unBROKEN
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (3 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 04/29] drm: fix config dependent unused variable warning Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 06/29] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h Jim Cromie
` (23 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Remove BROKEN mark on CONFIG_DRM_USE_DYNAMIC_DEBUG, inside
drivers/gpu/drm/Kconfig.debug.
Un-breaking the var allows to enable the config, and build and test
dynamic-debug-enabled DRM drivers with full classmap query support.
Doing this early in the commit-set exposes the series to more
in-series enabled testing, which has more chance to expose bugs.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
drivers/gpu/drm/Kconfig.debug | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/Kconfig.debug b/drivers/gpu/drm/Kconfig.debug
index 1f4c98cbf090..2ae59a616e75 100644
--- a/drivers/gpu/drm/Kconfig.debug
+++ b/drivers/gpu/drm/Kconfig.debug
@@ -1,7 +1,6 @@
config DRM_USE_DYNAMIC_DEBUG
bool "use dynamic debug to implement drm.debug"
default n
- depends on BROKEN
depends on DRM
depends on DYNAMIC_DEBUG || DYNAMIC_DEBUG_CORE
depends on JUMP_LABEL
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 06/29] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (4 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 05/29] drm: Mark CONFIG_DRM_USE_DYNAMIC_DEBUG as unBROKEN Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 07/29] vmlinux.lds.h: drop unused HEADERED_SECTION* macros Jim Cromie
` (22 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Arnd Bergmann
Move BOUNDED_SECTION_* macros to a new helper file:
include/asm-generic/bounded_sections.lds.h and include it back into
vmlinux.lds.h. This allows its reuse later to fix a failure to keep
dyndbg sections in some circumstances.
NOTES:
These macros are only for use in vmlinux.lds.h, where the _start &
_end symbols are needed. Modules keep sections separate in ELF
sections, with their boundaries known, so the _start and _end are not
useful, and may confuse tools not expecting them.
This patch ignores a checkpatch warning, because new file is covered
by "GENERIC INCLUDE/ASM HEADER FILES" in MAINTAINERS
CC: Arnd Bergmann <arnd@arndb.de>
CC: linux-arch@vger.kernel.org
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v3: move include to top
---
include/asm-generic/bounded_sections.lds.h | 36 ++++++++++++++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 31 +------------------------
2 files changed, 37 insertions(+), 30 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
new file mode 100644
index 000000000000..8c29293ca7fb
--- /dev/null
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
+#define _ASM_GENERIC_BOUNDED_SECTIONS_H
+
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _BEGIN_##_label_ = .; \
+ KEEP(*(_sec_)) \
+ _END_##_label_ = .;
+
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _label_##_BEGIN_ = .; \
+ KEEP(*(_sec_)) \
+ _label_##_END_ = .;
+
+#define BOUNDED_SECTION_BY(_sec_, _label_) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+
+#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _HDR_##_label_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _label_##_HDR_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_BY(_sec_, _label_) \
+ HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
+
+#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 5659f4b5a125..f5ddf31b7f26 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -50,6 +50,7 @@
* [__nosave_begin, __nosave_end] for the nosave data
*/
+#include <asm-generic/bounded_sections.lds.h>
#include <asm-generic/codetag.lds.h>
#ifndef LOAD_OFFSET
@@ -211,36 +212,6 @@
# endif
#endif
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _BEGIN_##_label_ = .; \
- KEEP(*(_sec_)) \
- _END_##_label_ = .;
-
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _label_##_BEGIN_ = .; \
- KEEP(*(_sec_)) \
- _label_##_END_ = .;
-
-#define BOUNDED_SECTION_BY(_sec_, _label_) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
-
#ifdef CONFIG_TRACE_BRANCH_PROFILING
#define LIKELY_PROFILE() \
BOUNDED_SECTION_BY(_ftrace_annotated_branch, _annotated_branch_profile)
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 07/29] vmlinux.lds.h: drop unused HEADERED_SECTION* macros
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (5 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 06/29] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 08/29] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386 Jim Cromie
` (21 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
These macros are unused, no point in carrying them any more.
NB: these macros were just moved to bounded_sections.lds.h, from
vmlinux.lds.h, which is the known entity, and therefore more
meaningful in the 1-line summary, so thats what I used as the topic.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 8c29293ca7fb..268cdc34389b 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -18,19 +18,4 @@
#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
-
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 08/29] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (6 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 07/29] vmlinux.lds.h: drop unused HEADERED_SECTION* macros Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 09/29] vmlinux.lds.h: remove redundant ALIGN(8) directives Jim Cromie
` (20 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
Almost all uses of the BOUNDED_SECTION macros are ALIGN(8), either
explicitly, or by being below an aligned section containing x*8 byte
objects. The noteworthy exception is BOUNDED_SECTION(__dyndbg), which
immediately follows BOUNDED_SECTION(__dyndbg_classes).
On i386, struct _ddebug_classmap is 28 bytes, so without an explicit
ALIGN(8) in the macro, the following __dyndbg section gets misaligned,
causing a NULL ptr deref in dynamic_debug_init().
So fix this with an explicit ALIGN(8) in the existing BOUNDED_SECTION
macros, and introduce _ALIGNED variants to handle the cases with an
explicit . = ALIGN(x)
Also add explicit alignments for: EXCEPTION_TABLE, ORC_UNWIND_TABLE,
TRACEDATA, INIT_SETUP, and NOTES.
update BOUNDED_SECTION uses inside . = ALIGN(x) stanzas to use
_ALIGNED variants, but keep the outer ALIGNs so the symbols between
them are not "re-aligned".
In particular, scripts/sorttable.c does not tolerate sloppy padding.
At the top of ORC_UNWIND_TABLE, add . = ALIGN(4) to match the struct
orc_header __align() call in the code:
commit b9f174c811e3 ("x86/unwind/orc: Add ELF section with ORC version identifier")
Suggested-by: Louis Chauvet <louis.chauvet@bootlin.com> # _ALIGNED variants.
Link: https://lore.kernel.org/lkml/177402491426.6181.12855763650074831089.b4-review@b4/
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v3:
sashiko complained about NOTES and .BTF_ids.
gemini asserts that NOTES are natively 4-byte aligned, add comment repeating it.
.BTF_ids doesnt use BOUNDED_BY, since start/end isnt needed;
sashiko evidently got confused by immediately preceding usage.
v2:
sashiko picked up 2 cases, added to the explicit list above
https://sashiko.dev/#/patchset/20260515-asm-generic-1-v3-0-680b273666d4%40gmail.com
---
include/asm-generic/bounded_sections.lds.h | 17 ++++++++++++++---
include/asm-generic/vmlinux.lds.h | 18 ++++++++++--------
2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 268cdc34389b..8ff3e3420f60 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -3,19 +3,30 @@
#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
#define _ASM_GENERIC_BOUNDED_SECTIONS_H
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_BEGIN_##_label_ = .; \
KEEP(*(_sec_)) \
_END_##_label_ = .;
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
+#define BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_label_##_BEGIN_ = .; \
KEEP(*(_sec_)) \
_label_##_END_ = .;
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
#define BOUNDED_SECTION_BY(_sec_, _label_) \
BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+#define BOUNDED_SECTION_BY_ALIGNED(_sec_, _label_, _ALIGNED_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, __start, __stop, _ALIGNED_)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index f5ddf31b7f26..f29fc079e37e 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -640,7 +640,7 @@
#define EXCEPTION_TABLE(align) \
. = ALIGN(align); \
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(__ex_table, ___ex_table) \
+ BOUNDED_SECTION_BY_ALIGNED(__ex_table, ___ex_table, align) \
}
/*
@@ -650,7 +650,7 @@
#define BTF \
. = ALIGN(PAGE_SIZE); \
.BTF : AT(ADDR(.BTF) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.BTF, _BTF) \
+ BOUNDED_SECTION_BY_ALIGNED(.BTF, _BTF, PAGE_SIZE) \
} \
. = ALIGN(PAGE_SIZE); \
.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) { \
@@ -832,16 +832,17 @@
#ifdef CONFIG_UNWINDER_ORC
#define ORC_UNWIND_TABLE \
+ . = ALIGN(4); \
.orc_header : AT(ADDR(.orc_header) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_header, _orc_header) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_header, _orc_header, 4) \
} \
. = ALIGN(4); \
.orc_unwind_ip : AT(ADDR(.orc_unwind_ip) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind_ip, _orc_unwind_ip) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind_ip, _orc_unwind_ip, 4)\
} \
. = ALIGN(2); \
.orc_unwind : AT(ADDR(.orc_unwind) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind, _orc_unwind) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind, _orc_unwind, 2) \
} \
text_size = _etext - _stext; \
. = ALIGN(4); \
@@ -869,7 +870,7 @@
#define TRACEDATA \
. = ALIGN(4); \
.tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { \
- BOUNDED_SECTION_POST_LABEL(.tracedata, __tracedata, _start, _end) \
+ BOUNDED_SECTION_POST_LABEL_ALIGNED(.tracedata, __tracedata, _start, _end, 4) \
}
#else
#define TRACEDATA
@@ -898,13 +899,14 @@
*(.note.gnu.property) \
} \
.notes : AT(ADDR(.notes) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.note.*, _notes) \
+ /* *(.note.*) are natively 4-byte aligned */ \
+ BOUNDED_SECTION_BY_ALIGNED(.note.*, _notes, 4) \
} NOTES_HEADERS \
NOTES_HEADERS_RESTORE
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
- BOUNDED_SECTION_POST_LABEL(.init.setup, __setup, _start, _end)
+ BOUNDED_SECTION_POST_LABEL_ALIGNED(.init.setup, __setup, _start, _end, initsetup_align)
#define INIT_CALLS_LEVEL(level) \
__initcall##level##_start = .; \
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 09/29] vmlinux.lds.h: remove redundant ALIGN(8) directives
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (7 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 08/29] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386 Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:56 ` [PATCH v7 10/29] dyndbg.lds.S: fix lost dyndbg sections in modules Jim Cromie
` (19 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
The BOUNDED_SECTION_PRE_LABEL and BOUNDED_SECTION_POST_LABEL macros
were recently updated to inherently enforce an 8-byte alignment. This
makes the explicit '. = ALIGN(8);' statements preceding 'naked' macro
calls in vmlinux.lds.h redundant.
Remove these redundant alignment directives to clean up the file and
clarify that the macros handle their own alignment padding.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/vmlinux.lds.h | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index f29fc079e37e..3758a79d0430 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -228,7 +228,6 @@
#ifdef CONFIG_KPROBES
#define KPROBE_BLACKLIST() \
- . = ALIGN(8); \
BOUNDED_SECTION(_kprobe_blacklist)
#else
#define KPROBE_BLACKLIST()
@@ -244,7 +243,6 @@
#ifdef CONFIG_EVENT_TRACING
#define FTRACE_EVENTS() \
- . = ALIGN(8); \
BOUNDED_SECTION(_ftrace_events) \
BOUNDED_SECTION_BY(_ftrace_eval_map, _ftrace_eval_maps)
#else
@@ -261,7 +259,6 @@
#ifdef CONFIG_FTRACE_SYSCALLS
#define TRACE_SYSCALLS() \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__syscalls_metadata, _syscalls_metadata)
#else
#define TRACE_SYSCALLS()
@@ -276,7 +273,6 @@
#ifdef CONFIG_SERIAL_EARLYCON
#define EARLYCON_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__earlycon_table, __earlycon_table, , _end)
#else
#define EARLYCON_TABLE()
@@ -284,11 +280,9 @@
#ifdef CONFIG_SECURITY
#define LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.lsm_info.init, _lsm_info, __start, __end)
#define EARLY_LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.early_lsm_info.init, _early_lsm_info, __start, __end)
#else
#define LSM_TABLE()
@@ -314,7 +308,6 @@
#ifdef CONFIG_ACPI
#define ACPI_PROBE_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_acpi_probe_table, \
__##name##_acpi_probe_table,, _end)
#else
@@ -323,7 +316,6 @@
#ifdef CONFIG_THERMAL
#define THERMAL_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_thermal_table, \
__##name##_thermal_table,, _end)
#else
@@ -403,12 +395,10 @@
__end_init_stack = .;
#define JUMP_TABLE_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__jump_table, ___jump_table)
#ifdef CONFIG_HAVE_STATIC_CALL_INLINE
#define STATIC_CALL_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(.static_call_sites, _static_call_sites) \
BOUNDED_SECTION_BY(.static_call_tramp_key, _static_call_tramp_key)
#else
@@ -453,7 +443,6 @@
*(.rodata) *(.rodata.*) *(.data.rel.ro*) \
SCHED_DATA \
RO_AFTER_INIT_DATA /* Read only after init */ \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
} \
@@ -947,12 +936,10 @@
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end)
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_INIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \
__kunit_init_suites, _start, _end)
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 10/29] dyndbg.lds.S: fix lost dyndbg sections in modules
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (8 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 09/29] vmlinux.lds.h: remove redundant ALIGN(8) directives Jim Cromie
@ 2026-07-21 20:56 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change Jim Cromie
` (18 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:56 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Arnd Bergmann
With CONFIG_DRM_USE_DYNAMIC_DEBUG=y, several build configs had
problems with __dyndbg* sections getting lost in drm drivers. Fix
this by following the model demonstrated in codetag.lds.h.
Introduce include/asm-generic/dyndbg.lds.h, to bundle dynamic-debug's
multiple sections together, into 2 macros:
vmlinux.lds.h DATA_DATA: move the 2 BOUNDED_SECTION_BY(__dyndbg*)
calls into dyndbg.lds.h DYNDBG_SECTIONS(). vmlinux.lds.h now includes
the new file and calls the new macro.
MOD_DYNDBG_SECTIONS keeps the 2 sections by name, aligns them and sets
the output address to 0 when the sections are empty.
dyndbg.lds.h includes (reuses) bounded-section.lds.h
scripts/module.lds.S: now calls MOD_DYNDBG_SECTIONS right before the
CODETAG macro (consistent with their placements in vmlinux.lds.h), and
also includes dyndbg.lds.h
This isolates vmlinux.lds.h from further __dyndbg section additions.
CC: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
---
v3: move #includes to top, drop extra ALIGN(8) in DYNDBG_SECTIONS, add RvBy
v2: Address linker script review feedback for relocatable modules.
MOD_DYNDBG_SECTIONS() used the BOUNDED_SECTION_BY() macro, which
proved problematic for kernel modules for two reasons:
1. Unwanted Empty Sections:
BOUNDED_SECTION_BY() automatically generates `__start` and `__stop`
symbols. When applied to `MOD_DYNDBG_SECTIONS()`, the linker assumes
the sections are populated due to the symbol definitions, forcing an
empty `__dyndbg` and `__dyndbg_classes` output section in every
compiled module, even those without dynamic debug configuration.
Since the module loader uses `section_objs()` to locate data via
ELF headers instead of relying on `__start`/`__stop` symbols, these
assignments are completely unnecessary.
2. Non-zero Output Addresses:
During relocatable linking (e.g., `ld.bfd -r`), omitting an explicit
base address causes the section to inherit the current location
counter. This results in non-zero sh_addr values in `.ko` files,
which is confusing, degrades compressibility, and can cause issues
with external tools parsing the ELF.
Fix both issues by dropping `BOUNDED_SECTION_BY()` in favor of a simple
`KEEP(*(...))` constraint and explicitly defining the sections with a `0`
base address: `__dyndbg 0 : ALIGN(8) { ... }`.
fixup-inc-vml
---
MAINTAINERS | 1 +
include/asm-generic/dyndbg.lds.h | 18 ++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 6 ++----
scripts/module.lds.S | 2 ++
4 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index dd6e351d2a98..ac050552923e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9179,6 +9179,7 @@ DYNAMIC DEBUG
M: Jason Baron <jbaron@akamai.com>
M: Jim Cromie <jim.cromie@gmail.com>
S: Maintained
+F: include/asm-generic/dyndbg.lds.h
F: include/linux/dynamic_debug.h
F: lib/dynamic_debug.c
F: lib/test_dynamic_debug.c
diff --git a/include/asm-generic/dyndbg.lds.h b/include/asm-generic/dyndbg.lds.h
new file mode 100644
index 000000000000..9d8951bef688
--- /dev/null
+++ b/include/asm-generic/dyndbg.lds.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __ASM_GENERIC_DYNDBG_LDS_H
+#define __ASM_GENERIC_DYNDBG_LDS_H
+
+#include <asm-generic/bounded_sections.lds.h>
+#define DYNDBG_SECTIONS() \
+ BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes)
+
+#define MOD_DYNDBG_SECTIONS() \
+ __dyndbg 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg)) \
+ } \
+ __dyndbg_classes 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_classes)) \
+ }
+
+#endif /* __ASM_GENERIC_DYNDBG_LDS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3758a79d0430..bd60f278f762 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -52,6 +52,7 @@
#include <asm-generic/bounded_sections.lds.h>
#include <asm-generic/codetag.lds.h>
+#include <asm-generic/dyndbg.lds.h>
#ifndef LOAD_OFFSET
#define LOAD_OFFSET 0
@@ -344,10 +345,7 @@
*(.data..do_once) \
STRUCT_ALIGN(); \
*(__tracepoints) \
- /* implement dynamic printk debug */ \
- . = ALIGN(8); \
- BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes) \
- BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ DYNDBG_SECTIONS() \
CODETAG_SECTIONS() \
LIKELY_PROFILE() \
BRANCH_PROFILE() \
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index b62683061d79..2e62dc5bd5d4 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -10,6 +10,7 @@
#endif
#include <asm-generic/codetag.lds.h>
+#include <asm-generic/dyndbg.lds.h>
SECTIONS {
/DISCARD/ : {
@@ -61,6 +62,7 @@ SECTIONS {
*(.rodata..L*)
}
+ MOD_DYNDBG_SECTIONS()
MOD_SEPARATE_CODETAG_SECTIONS()
}
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (9 preceding siblings ...)
2026-07-21 20:56 ` [PATCH v7 10/29] dyndbg.lds.S: fix lost dyndbg sections in modules Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:05 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 12/29] dyndbg: add stub macro for DECLARE_DYNDBG_CLASSMAP Jim Cromie
` (17 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
ddebug_change() is a big (~100 lines) function with a nested for loop.
The outer loop walks the per-module ddebug_tables list, and does
module stuff: it filters on a query's "module FOO*" and "class BAR",
failures here skip the entire inner loop.
The inner loop (60 lines) scans a module's descriptors. It starts
with a long block of filters on function, line, format, and the
validated "BAR" class (or the legacy/_DPRINTK_CLASS_DFLT).
These filters "continue" past pr_debugs that don't match the query
criteria, before it falls through the code below that counts matches,
then adjusts the flags and static-keys. This is unnecessarily hard to
think about.
So move the per-descriptor filter-block into a boolean function:
ddebug_match_desc(desc), and change each "continue" to "return false".
This puts a clear interface in place, so any future changes are either
inside, outside, or across this interface.
also fix checkpatch complaints about spaces and braces.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v5: check for null format in callsite. shouldnt happen, but pr_debug() isnt illegal
---
lib/dynamic_debug.c | 87 +++++++++++++++++++++++++++++++----------------------
1 file changed, 51 insertions(+), 36 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 18a71a9108d3..577a07916072 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -172,6 +172,56 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
* callsites, normally the same as number of changes. If verbose,
* logs the changes. Takes ddebug_lock.
*/
+static bool ddebug_match_desc(const struct ddebug_query *query,
+ struct _ddebug *dp,
+ int valid_class)
+{
+ /* match site against query-class */
+ if (dp->class_id != valid_class)
+ return false;
+
+ /* match against the source filename */
+ if (query->filename &&
+ !match_wildcard(query->filename, dp->filename) &&
+ !match_wildcard(query->filename,
+ kbasename(dp->filename)) &&
+ !match_wildcard(query->filename,
+ trim_prefix(dp->filename)))
+ return false;
+
+ /* match against the function */
+ if (query->function &&
+ !match_wildcard(query->function, dp->function))
+ return false;
+
+ /* match against the format */
+ if (query->format) {
+ if (!dp->format) {
+ pr_info("encountered a NULL format\n");
+ return false;
+ }
+ if (*query->format == '^') {
+ char *p;
+ /* anchored search. match must be at beginning */
+ p = strstr(dp->format, query->format + 1);
+ if (p != dp->format)
+ return false;
+ } else if (!strstr(dp->format, query->format)) {
+ return false;
+ }
+ }
+
+ /* match against the line number range */
+ if (query->first_lineno &&
+ dp->lineno < query->first_lineno)
+ return false;
+ if (query->last_lineno &&
+ dp->lineno > query->last_lineno)
+ return false;
+
+ return true;
+}
+
static int ddebug_change(const struct ddebug_query *query,
struct flag_settings *modifiers)
{
@@ -204,42 +254,7 @@ static int ddebug_change(const struct ddebug_query *query,
for (i = 0; i < dt->num_ddebugs; i++) {
struct _ddebug *dp = &dt->ddebugs[i];
- /* match site against query-class */
- if (dp->class_id != valid_class)
- continue;
-
- /* match against the source filename */
- if (query->filename &&
- !match_wildcard(query->filename, dp->filename) &&
- !match_wildcard(query->filename,
- kbasename(dp->filename)) &&
- !match_wildcard(query->filename,
- trim_prefix(dp->filename)))
- continue;
-
- /* match against the function */
- if (query->function &&
- !match_wildcard(query->function, dp->function))
- continue;
-
- /* match against the format */
- if (query->format) {
- if (*query->format == '^') {
- char *p;
- /* anchored search. match must be at beginning */
- p = strstr(dp->format, query->format+1);
- if (p != dp->format)
- continue;
- } else if (!strstr(dp->format, query->format))
- continue;
- }
-
- /* match against the line number range */
- if (query->first_lineno &&
- dp->lineno < query->first_lineno)
- continue;
- if (query->last_lineno &&
- dp->lineno > query->last_lineno)
+ if (!ddebug_match_desc(query, dp, valid_class))
continue;
nfound++;
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 12/29] dyndbg: add stub macro for DECLARE_DYNDBG_CLASSMAP
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (10 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 13/29] dyndbg: reword "class unknown," to "class:_UNKNOWN_" Jim Cromie
` (16 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Add the stub macro for !DYNAMIC_DEBUG builds, after moving the
original macro-defn down under the big ifdef. Do it now so future
changes have a cleaner starting point.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/linux/dynamic_debug.h | 43 ++++++++++++++++++++++---------------------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 05743900a116..a10adac8e8f0 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -93,27 +93,6 @@ struct ddebug_class_map {
enum class_map_type map_type;
};
-/**
- * DECLARE_DYNDBG_CLASSMAP - declare classnames known by a module
- * @_var: a struct ddebug_class_map, passed to module_param_cb
- * @_type: enum class_map_type, chooses bits/verbose, numeric/symbolic
- * @_base: offset of 1st class-name. splits .class_id space
- * @classes: class-names used to control class'd prdbgs
- */
-#define DECLARE_DYNDBG_CLASSMAP(_var, _maptype, _base, ...) \
- static const char *_var##_classnames[] = { __VA_ARGS__ }; \
- static struct ddebug_class_map __aligned(8) __used \
- __section("__dyndbg_classes") _var = { \
- .mod = THIS_MODULE, \
- .mod_name = KBUILD_MODNAME, \
- .base = _base, \
- .map_type = _maptype, \
- .length = NUM_TYPE_ARGS(char*, __VA_ARGS__), \
- .class_names = _var##_classnames, \
- }
-#define NUM_TYPE_ARGS(eltype, ...) \
- (sizeof((eltype[]){__VA_ARGS__}) / sizeof(eltype))
-
/* encapsulate linker provided built-in (or module) dyndbg data */
struct _ddebug_info {
struct _ddebug *descs;
@@ -138,6 +117,27 @@ struct ddebug_class_param {
#if defined(CONFIG_DYNAMIC_DEBUG) || \
(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
+/**
+ * DECLARE_DYNDBG_CLASSMAP - declare classnames known by a module
+ * @_var: a struct ddebug_class_map, passed to module_param_cb
+ * @_type: enum class_map_type, chooses bits/verbose, numeric/symbolic
+ * @_base: offset of 1st class-name. splits .class_id space
+ * @classes: class-names used to control class'd prdbgs
+ */
+#define DECLARE_DYNDBG_CLASSMAP(_var, _maptype, _base, ...) \
+ static const char *_var##_classnames[] = { __VA_ARGS__ }; \
+ static struct ddebug_class_map __aligned(8) __used \
+ __section("__dyndbg_classes") _var = { \
+ .mod = THIS_MODULE, \
+ .mod_name = KBUILD_MODNAME, \
+ .base = _base, \
+ .map_type = _maptype, \
+ .length = NUM_TYPE_ARGS(char*, __VA_ARGS__), \
+ .class_names = _var##_classnames, \
+ }
+#define NUM_TYPE_ARGS(eltype, ...) \
+ (sizeof((eltype[]) {__VA_ARGS__}) / sizeof(eltype))
+
extern __printf(2, 3)
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
@@ -314,6 +314,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt)
#define DYNAMIC_DEBUG_BRANCH(descriptor) false
+#define DECLARE_DYNDBG_CLASSMAP(...)
#define dynamic_pr_debug(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 13/29] dyndbg: reword "class unknown," to "class:_UNKNOWN_"
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (11 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 12/29] dyndbg: add stub macro for DECLARE_DYNDBG_CLASSMAP Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 14/29] dyndbg-API: remove DD_CLASS_TYPE_(DISJOINT|LEVEL)_NAMES and code Jim Cromie
` (15 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
When a dyndbg classname is unknown to a kernel module, the callsite is
un-addressable via >control queries, and therefore uncontrollable.
The control-file displays this condition as "class unknown, _id:N"
currently. That spelling is sub-optimal/too-generic, so change it to
"class:_UNKNOWN_ _id:N" to loudly announce the erroneous situation,
and to make it uniquely greppable.
NB: while this might be seen as a user-visible change, this shouldn't
disqualify the change:
a- it reports a classmap coding error condition, which should be
detected in (or before) review.
b- SHOUTING the error makes it more visible, uniquely greppable.
c- the classmap feature is marked BROKEN for its only current user.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/dynamic_debug.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 577a07916072..a86e1d5845e6 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1170,7 +1170,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
if (class)
seq_printf(m, " class:%s", class);
else
- seq_printf(m, " class unknown, _id:%d", dp->class_id);
+ seq_printf(m, " class:_UNKNOWN_ _id:%d", dp->class_id);
}
seq_putc(m, '\n');
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 14/29] dyndbg-API: remove DD_CLASS_TYPE_(DISJOINT|LEVEL)_NAMES and code
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (12 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 13/29] dyndbg: reword "class unknown," to "class:_UNKNOWN_" Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 15/29] dyndbg: drop NUM_TYPE_ARGS Jim Cromie
` (14 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
Remove the DD_CLASS_TYPE_*_NAMES classmap types and code.
These 2 classmap types accept class names at the PARAM interface, for
example:
echo +DRM_UT_CORE,-DRM_UT_KMS > /sys/module/drm/parameters/debug_names
The code works, but its only used by test-dynamic-debug, and wasn't
asked for by anyone else, so reduce LOC & test-surface; simplify things.
Also rename enum class_map_type to enum ddebug_class_map_type.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v3:
fix name of enum in kdoc
also change name of struct (to future name)
v2:
move RvB after SoB
respect const instr in param_set_dyndbg_module_classes, return -EINVAL on classtype err.
---
include/linux/dynamic_debug.h | 28 ++++---------
lib/dynamic_debug.c | 98 +++----------------------------------------
lib/test_dynamic_debug.c | 26 ------------
3 files changed, 15 insertions(+), 137 deletions(-)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index a10adac8e8f0..9607121c3072 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -59,27 +59,17 @@ struct _ddebug {
#endif
} __attribute__((aligned(8)));
-enum class_map_type {
+enum ddebug_class_map_type {
DD_CLASS_TYPE_DISJOINT_BITS,
/**
- * DD_CLASS_TYPE_DISJOINT_BITS: classes are independent, one per bit.
- * expecting hex input. Built for drm.debug, basis for other types.
+ * DD_CLASS_TYPE_DISJOINT_BITS: classes are independent,
+ * mapped to bits[0..N]. Expects hex input. Built for
+ * drm.debug, basis for other types.
*/
DD_CLASS_TYPE_LEVEL_NUM,
/**
- * DD_CLASS_TYPE_LEVEL_NUM: input is numeric level, 0-N.
- * N turns on just bits N-1 .. 0, so N=0 turns all bits off.
- */
- DD_CLASS_TYPE_DISJOINT_NAMES,
- /**
- * DD_CLASS_TYPE_DISJOINT_NAMES: input is a CSV of [+-]CLASS_NAMES,
- * classes are independent, like _DISJOINT_BITS.
- */
- DD_CLASS_TYPE_LEVEL_NAMES,
- /**
- * DD_CLASS_TYPE_LEVEL_NAMES: input is a CSV of [+-]CLASS_NAMES,
- * intended for names like: INFO,DEBUG,TRACE, with a module prefix
- * avoid EMERG,ALERT,CRIT,ERR,WARNING: they're not debug
+ * DD_CLASS_TYPE_LEVEL_NUM: input is numeric level, 0..N.
+ * Input N turns on bits 0..N-1
*/
};
@@ -90,7 +80,7 @@ struct ddebug_class_map {
const char **class_names;
const int length;
const int base; /* index of 1st .class_id, allows split/shared space */
- enum class_map_type map_type;
+ enum ddebug_class_map_type map_type;
};
/* encapsulate linker provided built-in (or module) dyndbg data */
@@ -119,8 +109,8 @@ struct ddebug_class_param {
/**
* DECLARE_DYNDBG_CLASSMAP - declare classnames known by a module
- * @_var: a struct ddebug_class_map, passed to module_param_cb
- * @_type: enum class_map_type, chooses bits/verbose, numeric/symbolic
+ * @_var: a struct _ddebug_class_map, passed to module_param_cb
+ * @_maptype: enum ddebug_class_map_type, chooses bits/verbose
* @_base: offset of 1st class-name. splits .class_id space
* @classes: class-names used to control class'd prdbgs
*/
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index a86e1d5845e6..9bd6b87f5609 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -650,76 +650,6 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
#define CLASSMAP_BITMASK(width) ((1UL << (width)) - 1)
-/* accept comma-separated-list of [+-] classnames */
-static int param_set_dyndbg_classnames(const char *instr, const struct kernel_param *kp)
-{
- const struct ddebug_class_param *dcp = kp->arg;
- const struct ddebug_class_map *map = dcp->map;
- unsigned long curr_bits, old_bits;
- char *cl_str, *p, *tmp;
- int cls_id, totct = 0;
- bool wanted;
-
- cl_str = tmp = kstrdup_and_replace(instr, '\n', '\0', GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
-
- /* start with previously set state-bits, then modify */
- curr_bits = old_bits = *dcp->bits;
- vpr_info("\"%s\" > %s:0x%lx\n", cl_str, KP_NAME(kp), curr_bits);
-
- for (; cl_str; cl_str = p) {
- p = strchr(cl_str, ',');
- if (p)
- *p++ = '\0';
-
- if (*cl_str == '-') {
- wanted = false;
- cl_str++;
- } else {
- wanted = true;
- if (*cl_str == '+')
- cl_str++;
- }
- cls_id = match_string(map->class_names, map->length, cl_str);
- if (cls_id < 0) {
- pr_err("%s unknown to %s\n", cl_str, KP_NAME(kp));
- continue;
- }
-
- /* have one or more valid class_ids of one *_NAMES type */
- switch (map->map_type) {
- case DD_CLASS_TYPE_DISJOINT_NAMES:
- /* the +/- pertains to a single bit */
- if (test_bit(cls_id, &curr_bits) == wanted) {
- v3pr_info("no change on %s\n", cl_str);
- continue;
- }
- curr_bits ^= BIT(cls_id);
- totct += ddebug_apply_class_bitmap(dcp, &curr_bits, dcp->bits);
- *dcp->bits = curr_bits;
- v2pr_info("%s: changed bit %d:%s\n", KP_NAME(kp), cls_id,
- map->class_names[cls_id]);
- break;
- case DD_CLASS_TYPE_LEVEL_NAMES:
- /* cls_id = N in 0..max. wanted +/- determines N or N-1 */
- old_bits = CLASSMAP_BITMASK(*dcp->lvl);
- curr_bits = CLASSMAP_BITMASK(cls_id + (wanted ? 1 : 0 ));
-
- totct += ddebug_apply_class_bitmap(dcp, &curr_bits, &old_bits);
- *dcp->lvl = (cls_id + (wanted ? 1 : 0));
- v2pr_info("%s: changed bit-%d: \"%s\" %lx->%lx\n", KP_NAME(kp), cls_id,
- map->class_names[cls_id], old_bits, curr_bits);
- break;
- default:
- pr_err("illegal map-type value %d\n", map->map_type);
- }
- }
- kfree(tmp);
- vpr_info("total matches: %d\n", totct);
- return 0;
-}
-
/**
* param_set_dyndbg_classes - class FOO >control
* @instr: string echo>d to sysfs, input depends on map_type
@@ -738,28 +668,15 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
unsigned long inrep, new_bits, old_bits;
int rc, totct = 0;
- switch (map->map_type) {
-
- case DD_CLASS_TYPE_DISJOINT_NAMES:
- case DD_CLASS_TYPE_LEVEL_NAMES:
- /* handle [+-]classnames list separately, we are done here */
- return param_set_dyndbg_classnames(instr, kp);
+ rc = kstrtoul(instr, 0, &inrep);
+ if (rc) {
+ int len = strcspn(instr, "\n");
- case DD_CLASS_TYPE_DISJOINT_BITS:
- case DD_CLASS_TYPE_LEVEL_NUM:
- /* numeric input, accept and fall-thru */
- rc = kstrtoul(instr, 0, &inrep);
- if (rc) {
- pr_err("expecting numeric input: %s > %s\n", instr, KP_NAME(kp));
- return -EINVAL;
- }
- break;
- default:
- pr_err("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);
+ pr_err("expecting numeric input, not: %.*s > %s\n",
+ len, instr, KP_NAME(kp));
return -EINVAL;
}
- /* only _BITS,_NUM (numeric) map-types get here */
switch (map->map_type) {
case DD_CLASS_TYPE_DISJOINT_BITS:
/* expect bits. mask and warn if too many */
@@ -787,6 +704,7 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
break;
default:
pr_warn("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);
+ return -EINVAL;
}
vpr_info("%s: total matches: %d\n", KP_NAME(kp), totct);
return 0;
@@ -808,12 +726,8 @@ int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
const struct ddebug_class_map *map = dcp->map;
switch (map->map_type) {
-
- case DD_CLASS_TYPE_DISJOINT_NAMES:
case DD_CLASS_TYPE_DISJOINT_BITS:
return scnprintf(buffer, PAGE_SIZE, "0x%lx\n", *dcp->bits);
-
- case DD_CLASS_TYPE_LEVEL_NAMES:
case DD_CLASS_TYPE_LEVEL_NUM:
return scnprintf(buffer, PAGE_SIZE, "%d\n", *dcp->lvl);
default:
diff --git a/lib/test_dynamic_debug.c b/lib/test_dynamic_debug.c
index 77c2a669b6af..74d183ebf3e0 100644
--- a/lib/test_dynamic_debug.c
+++ b/lib/test_dynamic_debug.c
@@ -74,13 +74,6 @@ DECLARE_DYNDBG_CLASSMAP(map_disjoint_bits, DD_CLASS_TYPE_DISJOINT_BITS, 0,
DD_SYS_WRAP(disjoint_bits, p);
DD_SYS_WRAP(disjoint_bits, T);
-/* symbolic input, independent bits */
-enum cat_disjoint_names { LOW = 11, MID, HI };
-DECLARE_DYNDBG_CLASSMAP(map_disjoint_names, DD_CLASS_TYPE_DISJOINT_NAMES, 10,
- "LOW", "MID", "HI");
-DD_SYS_WRAP(disjoint_names, p);
-DD_SYS_WRAP(disjoint_names, T);
-
/* numeric verbosity, V2 > V1 related */
enum cat_level_num { V0 = 14, V1, V2, V3, V4, V5, V6, V7 };
DECLARE_DYNDBG_CLASSMAP(map_level_num, DD_CLASS_TYPE_LEVEL_NUM, 14,
@@ -88,13 +81,6 @@ DECLARE_DYNDBG_CLASSMAP(map_level_num, DD_CLASS_TYPE_LEVEL_NUM, 14,
DD_SYS_WRAP(level_num, p);
DD_SYS_WRAP(level_num, T);
-/* symbolic verbosity */
-enum cat_level_names { L0 = 22, L1, L2, L3, L4, L5, L6, L7 };
-DECLARE_DYNDBG_CLASSMAP(map_level_names, DD_CLASS_TYPE_LEVEL_NAMES, 22,
- "L0", "L1", "L2", "L3", "L4", "L5", "L6", "L7");
-DD_SYS_WRAP(level_names, p);
-DD_SYS_WRAP(level_names, T);
-
/* stand-in for all pr_debug etc */
#define prdbg(SYM) __pr_debug_cls(SYM, #SYM " msg\n")
@@ -102,10 +88,6 @@ static void do_cats(void)
{
pr_debug("doing categories\n");
- prdbg(LOW);
- prdbg(MID);
- prdbg(HI);
-
prdbg(D2_CORE);
prdbg(D2_DRIVER);
prdbg(D2_KMS);
@@ -129,14 +111,6 @@ static void do_levels(void)
prdbg(V5);
prdbg(V6);
prdbg(V7);
-
- prdbg(L1);
- prdbg(L2);
- prdbg(L3);
- prdbg(L4);
- prdbg(L5);
- prdbg(L6);
- prdbg(L7);
}
static void do_prints(void)
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 15/29] dyndbg: drop NUM_TYPE_ARGS
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (13 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 14/29] dyndbg-API: remove DD_CLASS_TYPE_(DISJOINT|LEVEL)_NAMES and code Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 16/29] dyndbg: bump num-tokens in a query-cmd from 9 to 15 Jim Cromie
` (13 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
ARRAY_SIZE almost works here, since array decl is complete.
But define it locally, named __DDEBUG_ARRAY_SIZE, to avoid
include conflicts with boot/<something> on some arch.
no functional change
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v7: actually use macro
v5: drop include, it causes redefined probs in /boot/* for some arch.
v2: include linux/array_size.h, correct commit subject, review after sob
---
include/linux/dynamic_debug.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 9607121c3072..baf5c0853f45 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -8,6 +8,8 @@
#include <linux/build_bug.h>
+#define __DDEBUG_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
/*
* An instance of this structure is created in a special
* ELF section at every dynamic debug callsite. At runtime,
@@ -122,11 +124,9 @@ struct ddebug_class_param {
.mod_name = KBUILD_MODNAME, \
.base = _base, \
.map_type = _maptype, \
- .length = NUM_TYPE_ARGS(char*, __VA_ARGS__), \
.class_names = _var##_classnames, \
+ .length = __DDEBUG_ARRAY_SIZE(_var##_classnames), \
}
-#define NUM_TYPE_ARGS(eltype, ...) \
- (sizeof((eltype[]) {__VA_ARGS__}) / sizeof(eltype))
extern __printf(2, 3)
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 16/29] dyndbg: bump num-tokens in a query-cmd from 9 to 15
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (14 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 15/29] dyndbg: drop NUM_TYPE_ARGS Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 17/29] dyndbg: reduce verbose/debug clutter Jim Cromie
` (12 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Current MAXWORDS in ddebug_exec_query() is too small to accept a legal
query-command using all 6 keywords. We *need* 13, but this adds a few
extra to allow certain errors to fail on subsequent, more meaningful
grammar checks.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
lib/dynamic_debug.c | 2 +-
tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh | 14 ++++++--------
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 9bd6b87f5609..2fa034177268 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -554,7 +554,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)
{
struct flag_settings modifiers = {};
struct ddebug_query query = {};
-#define MAXWORDS 9
+#define MAXWORDS 15
int nwords, nfound;
char *words[MAXWORDS];
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 1dafeaabc976..446455683df4 100755
--- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -649,8 +649,8 @@ function GOLDEN_RECORDS {
#K= 9c405e06fcea50284df82d1ae9692403 FT_grammar_errs.2 dmesg
#K= 0b0d9b0e6f4c45c7d75d699fd3b6aa93 FT_grammar_errs.3 dmesg
#K= e008644e159c2a9d99a8d0806c06e71e FT_grammar_errs.4 dmesg
-#K= 11833e6cf8fe8b03869aa4f669b63398 FT_grammar_errs.5 dmesg
-#K= e85a4b665f5ef141e3f4d4505dec1da1 FT_grammar_errs.6 dmesg
+#K= 5750ed178633f9a68623f928c093d16e FT_grammar_errs.5 dmesg
+#K= 2cd51e5e3c3e2501544524bed0fab620 FT_grammar_errs.6 dmesg
#K= 3e1eba65bd936e1276c7e1d76b399595 FT_grammar_errs.7 dmesg
#K= e3ac86676e309d4e72ff3da287736c29 FT_grammar_errs.8 dmesg
#K= 466d325c23519119f754fa7860bae7c6 FT_grammar_errs.9 dmesg
@@ -663,8 +663,8 @@ function GOLDEN_RECORDS {
#K= bd8d9cf661c9aca54dac93c74cfc1501 FT_grammar_errs.16 dmesg
#K= e991391048f2fdffaaed99ba36b787ed FT_grammar_errs.17 dmesg
#K= 82d99278f471ebfbf47eeb19898525cf FT_grammar_errs.18 dmesg
-#K= 3c8d268c94b0bf97e64999654cd840c4 FT_grammar_errs.19 dmesg
-#K= 1a448ff3f1df9155cc3acf1e124f24f5 FT_grammar_errs.20 dmesg
+#K= f3f088fa7d276981bf2898c048087f76 FT_grammar_errs.19 dmesg
+#K= 984ecc198e0fa9fc91bc31caf7da2a5d FT_grammar_errs.20 dmesg
#K= 76c8a33c585d414fcc3f1c8b5ea5b4b3 FT_grammar_errs.21 dmesg
#K= 90cfc66e9d61f5c448353b97baa99145 FT_grammar_errs.22 dmesg
#K= 37a51dfd6e84cb64b69d3a0e23e68ceb FT_grammar_errs.23 dmesg
@@ -677,8 +677,8 @@ function GOLDEN_RECORDS {
#K= 2e37642dc8aee3d04b8a54a76b3b891c FT_grammar_errs.30 dmesg
#K= 593c8b4c41932e3bde564a0008627ced FT_grammar_errs.31 dmesg
#K= 85a2a0bee8d0cd10f883aa5477d62efb FT_grammar_errs.32 dmesg
-#K= 3c8d268c94b0bf97e64999654cd840c4 FT_grammar_errs.33 dmesg
-#K= 1a448ff3f1df9155cc3acf1e124f24f5 FT_grammar_errs.34 dmesg
+#K= f3f088fa7d276981bf2898c048087f76 FT_grammar_errs.33 dmesg
+#K= 629f6a7e379e02f38c543cdb48e3b84f FT_grammar_errs.34 dmesg
#K= d9f0cffb0898c54690735a71b0d4f3bf FT_grammar_errs.35 dmesg
#K= 4cf728b6a32043ea5336a14d0c435312 FT_grammar_errs.36 dmesg
#K= a11ae3f86f4e5f539fd6168d318f74b4 FT_grammar_errs.37 dmesg
@@ -693,8 +693,6 @@ function GOLDEN_RECORDS {
#K= 551f9801b6008553661453021ae6dfd3 FT_basic_queries.4 "kernel/params.c"
#K= 33f7162ac85020894fab9752ef07b89c FT_basic_queries.5 "kernel/params.c"
#K= 544d955c3bb9d3c78704f780d91396e0 FT_basic_queries.6 "kernel/params.c"
-
-
EOF
# Read the K-recs and skip those for tests that can't run
while read -r line; do
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 17/29] dyndbg: reduce verbose/debug clutter
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (15 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 16/29] dyndbg: bump num-tokens in a query-cmd from 9 to 15 Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 18/29] lib/parser: add match_wildcard_hyphen() for agnostic matching Jim Cromie
` (11 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
currently, for verbose=3, these are logged (blank lines for clarity):
dyndbg: query 0: "class DRM_UT_CORE +p" mod:*
dyndbg: split into words: "class" "DRM_UT_CORE" "+p"
dyndbg: op='+'
dyndbg: flags=0x1
dyndbg: *flagsp=0x1 *maskp=0xffffffff
dyndbg: parsed: func="" file="" module="" format="" lineno=0-0 class=...
dyndbg: no matches for query
dyndbg: no-match: func="" file="" module="" format="" lineno=0-0 class=...
dyndbg: processed 1 queries, with 0 matches, 0 errs
That is excessive, so this patch:
- shrinks 3 lines of 2nd stanza to single line
- drops 1st 2 lines of 3rd stanza
3rd line is like 1st, with result, not procedure.
2nd line is just status, retold in 4th, with more info.
New output:
dyndbg: query 0: "class DRM_UT_CORE +p"
dyndbg: split into words: "class" "DRM_UT_CORE" "+p"
dyndbg: op='+' flags=0x1 maskp=0xffffffff
dyndbg: processed 1 queries, with 0 matches, 0 errs
Also drop several verbose=3 messages in ddebug_add_module. When
modprobing a module, dyndbg currently logs/says "add-module", and then
"skipping" if the module has no prdbgs. Instead just check 1st and
return quietly.
Unmatched query diagnostics are intentionally restricted to verbose
level 3 (v3pr_info_dq) to reduce dmesg output clutter on standard
verbose levels (verbose=1 and verbose=2), aligning with the overall
de-cluttering of dynamic debug logging.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v7: update fingerprints of grammar-errs changed here
v4: rename vpr_dq_info to v3pr_dq_info to tell its active logging level
adjust some vX levels per doc'd intentions
v2: RvB after SoB
trivial change to verbose-debug output line to output the actual
"module" keyword rather than "mod:", and do so only when the module is
constrained by the callchain (ie as part of a modprobe).
was: query X: "(keyword value)* [+-=]flags" mod:*
now: query X: "(keyword value)* [+-=]flags"
or query X: module FOO "keyword value)* [+-=]flags"
IOW, adjust output to reflect the input grammar more closely.
---
lib/dynamic_debug.c | 24 +++---
.../selftests/dynamic_debug/dyndbg_selftest.sh | 86 +++++++++++-----------
2 files changed, 54 insertions(+), 56 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 2fa034177268..251df88cb04e 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -128,7 +128,7 @@ do { \
#define v3pr_info(fmt, ...) vnpr_info(3, fmt, ##__VA_ARGS__)
#define v4pr_info(fmt, ...) vnpr_info(4, fmt, ##__VA_ARGS__)
-static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
+static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
{
/* trim any trailing newlines */
int fmtlen = 0;
@@ -280,9 +280,6 @@ static int ddebug_change(const struct ddebug_query *query,
}
mutex_unlock(&ddebug_lock);
- if (!nfound && verbose)
- pr_info("no matches for query\n");
-
return nfound;
}
@@ -491,7 +488,6 @@ static int ddebug_parse_query(char *words[], int nwords,
*/
query->module = modname;
- vpr_info_dq(query, "parsed");
return 0;
}
@@ -515,7 +511,6 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
pr_err("bad flag-op %c, at start of %s\n", *str, str);
return -EINVAL;
}
- v3pr_info("op='%c'\n", op);
for (; *str ; ++str) {
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
@@ -529,7 +524,6 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
return -EINVAL;
}
}
- v3pr_info("flags=0x%x\n", modifiers->flags);
/* calculate final flags, mask based upon op */
switch (op) {
@@ -545,7 +539,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
modifiers->flags = 0;
break;
}
- v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask);
+ v3pr_info("op='%c' flags=0x%x maskp=0x%x\n", op, modifiers->flags, modifiers->mask);
return 0;
}
@@ -574,7 +568,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)
}
/* actually go and implement the change */
nfound = ddebug_change(&query, &modifiers);
- vpr_info_dq(&query, nfound ? "applied" : "no-match");
+ v3pr_info_dq(&query, nfound ? "applied" : "no-match");
return nfound;
}
@@ -597,7 +591,10 @@ static int ddebug_exec_queries(char *query, const char *modname)
if (!query || !*query || *query == '#')
continue;
- vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*");
+ if (modname)
+ v2pr_info("query %d: module %s \"%s\"\n", i, modname, query);
+ else
+ v2pr_info("query %d: \"%s\"\n", i, query);
rc = ddebug_exec_query(query, modname);
if (rc < 0) {
@@ -1164,11 +1161,10 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
{
struct ddebug_table *dt;
- v3pr_info("add-module: %s.%d sites\n", modname, di->num_descs);
- if (!di->num_descs) {
- v3pr_info(" skip %s\n", modname);
+ if (!di->num_descs)
return 0;
- }
+
+ v3pr_info("add-module: %s %d sites\n", modname, di->num_descs);
dt = kzalloc_obj(*dt);
if (dt == NULL) {
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 446455683df4..6e977486ea90 100755
--- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -645,54 +645,56 @@ modular_tests=(
# ==============================================================================
function GOLDEN_RECORDS {
cat << 'EOF' | {
-#K= bb85d28739876b0fdf1eb426baae8ef7 FT_grammar_errs.1 dmesg
-#K= 9c405e06fcea50284df82d1ae9692403 FT_grammar_errs.2 dmesg
-#K= 0b0d9b0e6f4c45c7d75d699fd3b6aa93 FT_grammar_errs.3 dmesg
-#K= e008644e159c2a9d99a8d0806c06e71e FT_grammar_errs.4 dmesg
-#K= 5750ed178633f9a68623f928c093d16e FT_grammar_errs.5 dmesg
-#K= 2cd51e5e3c3e2501544524bed0fab620 FT_grammar_errs.6 dmesg
-#K= 3e1eba65bd936e1276c7e1d76b399595 FT_grammar_errs.7 dmesg
-#K= e3ac86676e309d4e72ff3da287736c29 FT_grammar_errs.8 dmesg
-#K= 466d325c23519119f754fa7860bae7c6 FT_grammar_errs.9 dmesg
-#K= 51c489366cd86c16280858b95adf34ac FT_grammar_errs.10 dmesg
-#K= dfc632c6c0cd6206b6141dbc5fb989db FT_grammar_errs.11 dmesg
-#K= 69650fc26e97a78082bd851eb4a5c83b FT_grammar_errs.12 dmesg
-#K= 5cfe5665962a482f416836793067f2a2 FT_grammar_errs.13 dmesg
-#K= 661cc9a2d8d3c381bfeae41392432575 FT_grammar_errs.14 dmesg
-#K= 5dd0accf9be37ce2f4bf697e9bdebe30 FT_grammar_errs.15 dmesg
-#K= bd8d9cf661c9aca54dac93c74cfc1501 FT_grammar_errs.16 dmesg
-#K= e991391048f2fdffaaed99ba36b787ed FT_grammar_errs.17 dmesg
-#K= 82d99278f471ebfbf47eeb19898525cf FT_grammar_errs.18 dmesg
-#K= f3f088fa7d276981bf2898c048087f76 FT_grammar_errs.19 dmesg
-#K= 984ecc198e0fa9fc91bc31caf7da2a5d FT_grammar_errs.20 dmesg
-#K= 76c8a33c585d414fcc3f1c8b5ea5b4b3 FT_grammar_errs.21 dmesg
-#K= 90cfc66e9d61f5c448353b97baa99145 FT_grammar_errs.22 dmesg
-#K= 37a51dfd6e84cb64b69d3a0e23e68ceb FT_grammar_errs.23 dmesg
-#K= 3aefa325b63742710b45e4531152c9d8 FT_grammar_errs.24 dmesg
-#K= bc8d2c23282cd6834879bc1f1b303d05 FT_grammar_errs.25 dmesg
-#K= 092ec5530d875be1dc96ad4202fc7dc3 FT_grammar_errs.26 dmesg
-#K= 98caa2677e9070953a852b27ab57861e FT_grammar_errs.27 dmesg
-#K= 8d755baa707c7b3b67491bf84708ef08 FT_grammar_errs.28 dmesg
-#K= 5dd0accf9be37ce2f4bf697e9bdebe30 FT_grammar_errs.29 dmesg
-#K= 2e37642dc8aee3d04b8a54a76b3b891c FT_grammar_errs.30 dmesg
-#K= 593c8b4c41932e3bde564a0008627ced FT_grammar_errs.31 dmesg
-#K= 85a2a0bee8d0cd10f883aa5477d62efb FT_grammar_errs.32 dmesg
-#K= f3f088fa7d276981bf2898c048087f76 FT_grammar_errs.33 dmesg
-#K= 629f6a7e379e02f38c543cdb48e3b84f FT_grammar_errs.34 dmesg
-#K= d9f0cffb0898c54690735a71b0d4f3bf FT_grammar_errs.35 dmesg
-#K= 4cf728b6a32043ea5336a14d0c435312 FT_grammar_errs.36 dmesg
-#K= a11ae3f86f4e5f539fd6168d318f74b4 FT_grammar_errs.37 dmesg
-#K= c7d3830b0391f6a773205066b64e15f9 FT_grammar_errs.38 dmesg
-#K= 5c506ea2bfd050439b580b91543e9be7 FT_grammar_errs.39 dmesg
-#K= a257660007408f7bb0cb43c99b1c7bb8 FT_grammar_errs.40 dmesg
-#K= 0082939f100300a7b327b143cf40cb43 FT_grammar_errs.41 dmesg
-#K= 657ac4360f978687ad25a705b2bb89f1 FT_grammar_errs.42 dmesg
+# --- Unregistered Baselines ---
+#K= f3dbd5afb9aa1750f93275b634499e22 FT_grammar_errs.1 dmesg
+#K= 200c01632c52a63f6d186da1c6460740 FT_grammar_errs.2 dmesg
+#K= 7d7141900ce6e32f15c99202309c63a4 FT_grammar_errs.3 dmesg
+#K= 1bb798a5831d0119789d424ef6cb55c4 FT_grammar_errs.4 dmesg
+#K= 5edd66e308b2792d5694df86c07a3eaf FT_grammar_errs.5 dmesg
+#K= 6f87d92ffe0812550f43287127c6f2b9 FT_grammar_errs.6 dmesg
+#K= c0eb05b58a008c722e091e1ae74440ec FT_grammar_errs.7 dmesg
+#K= 911929ec0e2ffc1f13822b479dec6805 FT_grammar_errs.8 dmesg
+#K= c1407512376369d2e591a4b25a4b607a FT_grammar_errs.9 dmesg
+#K= 2046abda72725ea06fe339d5f364f1c9 FT_grammar_errs.10 dmesg
+#K= b72f7fccf76f8a5bee47a05d7bb545fb FT_grammar_errs.11 dmesg
+#K= 98e2bd3e4f3da58536496a38ec3e6238 FT_grammar_errs.12 dmesg
+#K= b371c6ba52503d037dbc43da788af8be FT_grammar_errs.13 dmesg
+#K= cb8288d607b0c5282125852f3ab05107 FT_grammar_errs.14 dmesg
+#K= 9346a310c4ad57cc3746afbace702c3e FT_grammar_errs.15 dmesg
+#K= 533d27af85eed3c0fd2eaec961982a36 FT_grammar_errs.16 dmesg
+#K= 114e0632585e205a3347c82bac7d79f2 FT_grammar_errs.17 dmesg
+#K= 73f5c173bafdfb9674b5ecce77db3354 FT_grammar_errs.18 dmesg
+#K= 0fc110d078f60eacdd389e5975ba18d9 FT_grammar_errs.19 dmesg
+#K= 815a1c52f365510c644450bb80c07e72 FT_grammar_errs.20 dmesg
+#K= 621e3cd81b553973cb40a935bb9298f1 FT_grammar_errs.21 dmesg
+#K= 581222901232344ade18bbda58302c48 FT_grammar_errs.22 dmesg
+#K= ea0aae3e01b3bb22eb8ad7acd327b371 FT_grammar_errs.23 dmesg
+#K= 8f28189bff62a3d5ed16f537d41a725a FT_grammar_errs.24 dmesg
+#K= 19c425e5d3a645b5dc5e23758ba0f4a1 FT_grammar_errs.25 dmesg
+#K= 75415542333f2250f0e060a54dae50f8 FT_grammar_errs.26 dmesg
+#K= 0955815c0e595ab2206e25aa31fe1ef2 FT_grammar_errs.27 dmesg
+#K= 3ff4c0b60db33e44c3cd6f0e14f81e3e FT_grammar_errs.28 dmesg
+#K= 9346a310c4ad57cc3746afbace702c3e FT_grammar_errs.29 dmesg
+#K= 7aaf0a16c287e66b62e11298ee160b34 FT_grammar_errs.30 dmesg
+#K= 06350c62105b537cdd0c67736b29727d FT_grammar_errs.31 dmesg
+#K= 6ef0ec01805c8719d828553098f95377 FT_grammar_errs.32 dmesg
+#K= 0fc110d078f60eacdd389e5975ba18d9 FT_grammar_errs.33 dmesg
+#K= 72203b2d88d0617cd5c659d3b80e26f9 FT_grammar_errs.34 dmesg
+#K= 6ecb03736d5cddb7ab2aaff49d561be9 FT_grammar_errs.35 dmesg
+#K= eaa989336cb7c96c13ef4a3964fc6898 FT_grammar_errs.36 dmesg
+#K= 5b624d9c133d7bb4f5370c3ce06929ed FT_grammar_errs.37 dmesg
+#K= 127275739b1fe04c84eedad28ec154f6 FT_grammar_errs.38 dmesg
+#K= 3e2fd15a7e8c0583bc5066524bc50508 FT_grammar_errs.39 dmesg
+#K= 781995971d28f732a792522f3c56cdd3 FT_grammar_errs.40 dmesg
+#K= 6614a677d9f9ac09d9825b4e989d2c42 FT_grammar_errs.41 dmesg
+#K= 70de9afed457a6be9f9c3c81cbd6d4d5 FT_grammar_errs.42 dmesg
#K= 9a7aaed40738f1c5203af2e421a20bc2 FT_basic_queries.1 "kernel/params.c"
#K= 2a57a9e283a3912de3fa5e006fb330cc FT_basic_queries.2 "kernel/params.c"
#K= fed3186684428b2f05bcf5df960c639c FT_basic_queries.3 "kernel/params.c"
#K= 551f9801b6008553661453021ae6dfd3 FT_basic_queries.4 "kernel/params.c"
#K= 33f7162ac85020894fab9752ef07b89c FT_basic_queries.5 "kernel/params.c"
#K= 544d955c3bb9d3c78704f780d91396e0 FT_basic_queries.6 "kernel/params.c"
+# ------------------------------
EOF
# Read the K-recs and skip those for tests that can't run
while read -r line; do
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 18/29] lib/parser: add match_wildcard_hyphen() for agnostic matching
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (16 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 17/29] dyndbg: reduce verbose/debug clutter Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 19/29] dyndbg: use KBUILD_MODFILE for unique builtin module names Jim Cromie
` (10 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
This commit introduces match_wildcard_hyphen() as a variant of the
existing match_wildcard() function. It treats hyphens and underscores
as identical characters during the matching process.
This is necessary for subsystems like dynamic_debug that need to match
module names provided by users (who often use underscores) against
names stored in the kernel (which may use hyphens, especially when
using KBUILD_MODFILE for built-ins).
To avoid code duplication, the core logic is refactored into a private
__match_wildcard() function marked as __always_inline. This allows the
compiler to generate optimized versions for both the strict and agnostic
callsites with zero runtime overhead.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v5: move ahead of array-slice patch to silence sashiko complaint about it
v4: initial version
---
include/linux/parser.h | 1 +
lib/parser.c | 58 +++++++++++++++++++++++++++++++++++++-------------
2 files changed, 44 insertions(+), 15 deletions(-)
diff --git a/include/linux/parser.h b/include/linux/parser.h
index dd79f45a37b8..a3cc7bc5fb93 100644
--- a/include/linux/parser.h
+++ b/include/linux/parser.h
@@ -34,6 +34,7 @@ int match_u64(substring_t *, u64 *result);
int match_octal(substring_t *, int *result);
int match_hex(substring_t *, int *result);
bool match_wildcard(const char *pattern, const char *str);
+bool match_wildcard_hyphen(const char *pattern, const char *str);
size_t match_strlcpy(char *, const substring_t *, size_t);
char *match_strdup(const substring_t *);
diff --git a/lib/parser.c b/lib/parser.c
index 62da0ac0d438..d5be01fa9adf 100644
--- a/lib/parser.c
+++ b/lib/parser.c
@@ -268,20 +268,13 @@ int match_hex(substring_t *s, int *result)
}
EXPORT_SYMBOL(match_hex);
-/**
- * match_wildcard - parse if a string matches given wildcard pattern
- * @pattern: wildcard pattern
- * @str: the string to be parsed
- *
- * Description: Parse the string @str to check if matches wildcard
- * pattern @pattern. The pattern may contain two types of wildcards:
- *
- * * '*' - matches zero or more characters
- * * '?' - matches one character
- *
- * Return: If the @str matches the @pattern, return true, else return false.
- */
-bool match_wildcard(const char *pattern, const char *str)
+static inline char dash2underscore(char c)
+{
+ return (c == '-') ? '_' : c;
+}
+
+static __always_inline bool __match_wildcard(const char *pattern, const char *str,
+ bool hyphen_agnostic)
{
const char *s = str;
const char *p = pattern;
@@ -301,7 +294,9 @@ bool match_wildcard(const char *pattern, const char *str)
pattern = p;
break;
default:
- if (*s == *p) {
+ if (hyphen_agnostic ?
+ (dash2underscore(*s) == dash2underscore(*p)) :
+ (*s == *p)) {
s++;
p++;
} else {
@@ -319,8 +314,41 @@ bool match_wildcard(const char *pattern, const char *str)
++p;
return !*p;
}
+
+/**
+ * match_wildcard - parse if a string matches given wildcard pattern
+ * @pattern: wildcard pattern
+ * @str: the string to be parsed
+ *
+ * Description: Parse the string @str to check if matches wildcard
+ * pattern @pattern. The pattern may contain two types of wildcards:
+ *
+ * * '*' - matches zero or more characters
+ * * '?' - matches one character
+ *
+ * Return: If the @str matches the @pattern, return true, else return false.
+ */
+bool match_wildcard(const char *pattern, const char *str)
+{
+ return __match_wildcard(pattern, str, false);
+}
EXPORT_SYMBOL(match_wildcard);
+/**
+ * match_wildcard_hyphen - parse if a string matches given wildcard pattern
+ * @pattern: wildcard pattern
+ * @str: the string to be parsed
+ *
+ * Description: Same as match_wildcard, but treats '-' and '_' as identical.
+ *
+ * Return: If the @str matches the @pattern, return true, else return false.
+ */
+bool match_wildcard_hyphen(const char *pattern, const char *str)
+{
+ return __match_wildcard(pattern, str, true);
+}
+EXPORT_SYMBOL(match_wildcard_hyphen);
+
/**
* match_strlcpy - Copy the characters from a substring_t to a sized buffer
* @dest: where to copy to
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 19/29] dyndbg: use KBUILD_MODFILE for unique builtin module names
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (17 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 18/29] lib/parser: add match_wildcard_hyphen() for agnostic matching Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below Jim Cromie
` (9 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Historically dynamic-debug gets its module names from KBUILD_MODNAME.
This works well for loadable modules, as the module loader has always
required them to have unique names, but for builtins it is basically
kbasename(srcfile), which sadly gives us many modules named "main".
IOW, it makes this ambiguous:
bash-5.3# echo module main +m > /proc/dynamic_debug/control
since it would affect all 4 independent modules named main:
bash-5.3# ddgrep =m
init/main.c:1265 [main]initcall_blacklist =m "blacklisting initcall %s\n"
kernel/power/main.c:49 [main]pm_restore_gfp_mask =m "GFP mask restored\n"
kernel/module/main.c:2862 [main]move_module =m "\t0x%lx 0x%.8lx %s\n"
drivers/base/power/main.c:149 [main]device_pm_add =m "Adding info for %s:%s\n"
We can improve this by adding DDEBUG_MODNAME, which is KBUILD_MODFILE
for dyndbg's builtins (which is unique), and KBUILD_MODNAME for
loadables (which is already required/guaranteed to be unique by
module-loader):
The above control-file entries then become:
init/main.c:1265 [init/main]initcall_blacklist ...
kernel/power/main.c:49 [kernel/power/main]pm_restore_gfp_mask ...
kernel/module/main.c:2862 [kernel/module/main]move_module ...
drivers/base/power/main.c:149 [drivers/base/power/main]device_pm_add ...
While this is a user visible change; [params] becomes [kernel/params]
etc, it is not a behavior change; we now match the query-module
against the subsystem/module name or its kbasename (the
simple-modname), which as before, matches all 4 modules.
This allows queries to be specific when desired: "module init/main",
while preserving the existing meaning of "module main"
The deeper reason for this change is not obvious. If any builtin
"main" module were to add a classmap, it would attach to all "main"
modules. If 2 "main" modules defined separate classmaps, both modules
would inadvertently share both classmaps. Since classmaps map
classnames to 0..62, and independently defined classmaps are most
likely to start at 0 (unless author is planning to share the 0..62
range with other classmaps), we have a setup for later reserved range
conflicts. Having unique names prevents future conflicts.
This solution isn't perfect:
1. it changes displayed [params] to [kernel/params] etc
2. its mostly redundant with "filename */main.*"
3. Ideally, queries like "module power", "module module", "module
base/power" might be better but would break old queries.
Adding classmaps to any of the builtins named "[main]" is unlikely, so
this change isn't absolutely necessary, but it seemed proper to at
least address the latent problem.
Summary:
1. DDEBUG_MODNAME is either KBUILD_MODFILE or KBUILD_MODNAME.
2. Update basic-tests working against init/main.c pr_debugs to
validate changes to "[init/main]" as the state-of-interest (where the
relevant state is in the control file).
3. update GOLDEN_RECORDS to for changed state-of-interest in
dynamic_debug/control, where [main] became [*/main]
4. Adjust Documentation with "simple modname" and "subsystem modname".
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v7: checksum updates - needed due to [module] display changes
v5: move ahead of array-slice patch to silence sashiko complaint about it
v4: call match_wildcard_hyphen() to allow dash vs underscore modname equivalence
v3: use KBUILD_MODFILE to give unique modnames for builtins
Note: Updated expected FT_basic_queries hashes to reflect the new unique 'kernel/params' module name format introduced by KBUILD_MODFILE.
---
Documentation/admin-guide/dynamic-debug-howto.rst | 42 ++++++++++++----------
include/linux/dynamic_debug.h | 15 ++++++--
lib/dynamic_debug.c | 3 +-
.../selftests/dynamic_debug/dyndbg_selftest.sh | 26 ++++++--------
4 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst
index 9c2f096ed1d8..99bbae37d34e 100644
--- a/Documentation/admin-guide/dynamic-debug-howto.rst
+++ b/Documentation/admin-guide/dynamic-debug-howto.rst
@@ -38,12 +38,12 @@ You can view the currently configured behaviour in the *prdbg* catalog::
:#> head -n7 /proc/dynamic_debug/control
# filename:lineno [module]function flags format
- init/main.c:1179 [main]initcall_blacklist =_ "blacklisting initcall %s\n"
- init/main.c:1218 [main]initcall_blacklisted =_ "initcall %s blacklisted\n"
- init/main.c:1424 [main]run_init_process =_ " with arguments:\n"
- init/main.c:1426 [main]run_init_process =_ " %s\n"
- init/main.c:1427 [main]run_init_process =_ " with environment:\n"
- init/main.c:1429 [main]run_init_process =_ " %s\n"
+ init/main.c:1179 [init/main]initcall_blacklist =_ "blacklisting initcall %s\n"
+ init/main.c:1218 [init/main]initcall_blacklisted =_ "initcall %s blacklisted\n"
+ init/main.c:1424 [init/main]run_init_process =_ " with arguments:\n"
+ init/main.c:1426 [init/main]run_init_process =_ " %s\n"
+ init/main.c:1427 [init/main]run_init_process =_ " with environment:\n"
+ init/main.c:1429 [init/main]run_init_process =_ " %s\n"
The 3rd space-delimited column shows the current flags, preceded by
a ``=`` for easy use with grep/cut. ``=p`` shows enabled callsites.
@@ -59,10 +59,10 @@ query/commands to the control file. Example::
:#> ddcmd '-p; module main func run* +p'
:#> grep =p /proc/dynamic_debug/control
- init/main.c:1424 [main]run_init_process =p " with arguments:\n"
- init/main.c:1426 [main]run_init_process =p " %s\n"
- init/main.c:1427 [main]run_init_process =p " with environment:\n"
- init/main.c:1429 [main]run_init_process =p " %s\n"
+ init/main.c:1424 [init/main]run_init_process =p " with arguments:\n"
+ init/main.c:1426 [init/main]run_init_process =p " %s\n"
+ init/main.c:1427 [init/main]run_init_process =p " with environment:\n"
+ init/main.c:1429 [init/main]run_init_process =p " %s\n"
Error messages go to console/syslog::
@@ -161,17 +161,21 @@ file
file kernel/freezer.c # ie column 1 of control file
file drivers/usb/* # all callsites under it
file inode.c:start_* # parse :tail as a func (above)
- file inode.c:1-100 # parse :tail as a line-range (above)
+ file inode.c:1-100 # parse :tail as a line-range (below)
module
- The given string is compared against the module name
- of each callsite. The module name is the string as
- seen in ``lsmod``, i.e. without the directory or the ``.ko``
- suffix and with ``-`` changed to ``_``. Examples::
-
- module sunrpc
- module nfsd
- module drm* # both drm, drm_kms_helper
+ The query string is compared against the subsystem module name of
+ each callsite, as shown in the control file, or its simple name.
+ The simple module name is the string as seen in ``lsmod``,
+ i.e. without the directory or the ``.ko`` suffix and with ``-``
+ changed to ``_``.
+ Examples::
+
+ module nfsd # simple modname (as from lsmod)
+ module init/main # subsystem modname (as in control file)
+ module */main # any subsystem ending in main
+ module main # simple modname, selects same as above
+ module drm* # both drm, drm_kms_helper
format
The given string is searched for in the dynamic debug format
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index baf5c0853f45..17cc1ae7baef 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -10,6 +10,17 @@
#define __DDEBUG_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+/*
+ * Pick the best name for the module:
+ * KBUILD_MODFILE includes the path (e.g., drivers/usb/core/usbcore) for built-ins.
+ * Fall back to KBUILD_MODNAME for modules (loader requires unique names).
+ */
+#ifdef KBUILD_MODFILE
+# define DDEBUG_MODNAME KBUILD_MODFILE
+#else
+# define DDEBUG_MODNAME KBUILD_MODNAME
+#endif
+
/*
* An instance of this structure is created in a special
* ELF section at every dynamic debug callsite. At runtime,
@@ -121,7 +132,7 @@ struct ddebug_class_param {
static struct ddebug_class_map __aligned(8) __used \
__section("__dyndbg_classes") _var = { \
.mod = THIS_MODULE, \
- .mod_name = KBUILD_MODNAME, \
+ .mod_name = DDEBUG_MODNAME, \
.base = _base, \
.map_type = _maptype, \
.class_names = _var##_classnames, \
@@ -160,7 +171,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
#define DEFINE_DYNAMIC_DEBUG_METADATA_CLS(name, cls, fmt) \
static struct _ddebug __aligned(8) \
__section("__dyndbg") name = { \
- .modname = KBUILD_MODNAME, \
+ .modname = DDEBUG_MODNAME, \
.function = __func__, \
.filename = __FILE__, \
.format = (fmt), \
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 251df88cb04e..f66e5373e61e 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -239,7 +239,8 @@ static int ddebug_change(const struct ddebug_query *query,
/* match against the module name */
if (query->module &&
- !match_wildcard(query->module, dt->mod_name))
+ !match_wildcard_hyphen(query->module, dt->mod_name) &&
+ !match_wildcard_hyphen(query->module, kbasename(dt->mod_name)))
continue;
if (query->class_string) {
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 6e977486ea90..947f23dcb4ce 100755
--- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -461,7 +461,8 @@ function FT_hyphen_underscore {
ddcmd =_
}
-# test parsing on spaces, commas. testing agains builtin [kernel/params]
+# test parsing on spaces, commas. testing against builtin [kernel/params]
+# disabled pending feature patch
function FT_comma_terminators {
v_echo "${GREEN}# COMMA_TERMINATOR_TESTS ${NC}"
if [ $LACK_DD_BUILTIN -eq 1 ]; then
@@ -470,15 +471,9 @@ function FT_comma_terminators {
fi
ddcmd "module params =_"
- # 1. Verify transition after commas-as-spaces query and splitting on @
- # Use non-printing decorator flags (+mf) to avoid print-enabling (+p) and
- # prevent syslog pollution
- #ddcmd "module,params,=_ @ module,params,+mf" 'kernel/params.c'
-
- # 2. Verify transition after ignored-commas query
+ ddcmd "module,params,=_ @ module,params,+mf" 'kernel/params.c'
+ # ignore empty tokens
ddcmd ",module ,, , params, -p" 'kernel/params.c'
-
- # 3. Verify transition after quoted-commas query
ddcmd " , module ,,, , params, -m" 'kernel/params.c'
ddcmd =_
@@ -688,13 +683,12 @@ function GOLDEN_RECORDS {
#K= 781995971d28f732a792522f3c56cdd3 FT_grammar_errs.40 dmesg
#K= 6614a677d9f9ac09d9825b4e989d2c42 FT_grammar_errs.41 dmesg
#K= 70de9afed457a6be9f9c3c81cbd6d4d5 FT_grammar_errs.42 dmesg
-#K= 9a7aaed40738f1c5203af2e421a20bc2 FT_basic_queries.1 "kernel/params.c"
-#K= 2a57a9e283a3912de3fa5e006fb330cc FT_basic_queries.2 "kernel/params.c"
-#K= fed3186684428b2f05bcf5df960c639c FT_basic_queries.3 "kernel/params.c"
-#K= 551f9801b6008553661453021ae6dfd3 FT_basic_queries.4 "kernel/params.c"
-#K= 33f7162ac85020894fab9752ef07b89c FT_basic_queries.5 "kernel/params.c"
-#K= 544d955c3bb9d3c78704f780d91396e0 FT_basic_queries.6 "kernel/params.c"
-# ------------------------------
+#K= 24d85e3b86f3d5f7640995922d91e08c FT_basic_queries.1 "kernel/params.c"
+#K= 958898bcd9736a94e1f8b293b230a96a FT_basic_queries.2 "kernel/params.c"
+#K= 130118da5a296e4039865d167e06cacd FT_basic_queries.3 "kernel/params.c"
+#K= da6bd1c6a299290150668186f8263b82 FT_basic_queries.4 "kernel/params.c"
+#K= 82572e8d20c4b567afac783006d1a935 FT_basic_queries.5 "kernel/params.c"
+#K= baea1247680e8151c121539f4b90a6d8 FT_basic_queries.6 "kernel/params.c"
EOF
# Read the K-recs and skip those for tests that can't run
while read -r line; do
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (18 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 19/29] dyndbg: use KBUILD_MODFILE for unique builtin module names Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:11 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 21/29] dyndbg: tighten fn-sig of ddebug_apply_class_bitmap Jim Cromie
` (8 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
Refactor the callchain below param_set_dyndbg_classes(1) to allow
mod-name specific settings. Split (1) into upper/lower fns, adding
modname param to lower, and passing NULL in from upper. Below that,
add the same param to ddebug_apply_class_bitmap(), and pass it thru to
_ddebug_queries(), replacing NULL with the param.
This allows the callchain to update the classmap in just one module,
vs just all as currently done. While the sysfs param is unlikely to
ever update just one module, the callchain will be used for modprobe
handling, which should update only that just-probed module.
In ddebug_apply_class_bitmap(), also check for actual changes to the
bits before announcing them, to declutter logs.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v2: move RvB after SoB
---
lib/dynamic_debug.c | 51 +++++++++++++++++++++++++++++++++++++++------------
1 file changed, 39 insertions(+), 12 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index f66e5373e61e..a619cefd801f 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -615,9 +615,10 @@ static int ddebug_exec_queries(char *query, const char *modname)
return nfound;
}
-/* apply a new bitmap to the sys-knob's current bit-state */
+/* apply a new class-param setting */
static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
- unsigned long *new_bits, unsigned long *old_bits)
+ unsigned long *new_bits, unsigned long *old_bits,
+ const char *query_modname)
{
#define QUERY_SIZE 128
char query[QUERY_SIZE];
@@ -625,7 +626,9 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
int matches = 0;
int bi, ct;
- v2pr_info("apply: 0x%lx to: 0x%lx\n", *new_bits, *old_bits);
+ if (*new_bits != *old_bits)
+ v2pr_info("apply bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
+ *old_bits, query_modname ?: "'*'");
for (bi = 0; bi < map->length; bi++) {
if (test_bit(bi, new_bits) == test_bit(bi, old_bits))
@@ -634,12 +637,16 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
test_bit(bi, new_bits) ? '+' : '-', dcp->flags);
- ct = ddebug_exec_queries(query, NULL);
+ ct = ddebug_exec_queries(query, query_modname);
matches += ct;
v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,
ct, map->class_names[bi], *new_bits);
}
+ if (*new_bits != *old_bits)
+ v2pr_info("applied bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
+ *old_bits, query_modname ?: "'*'");
+
return matches;
}
@@ -652,6 +659,7 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
* param_set_dyndbg_classes - class FOO >control
* @instr: string echo>d to sysfs, input depends on map_type
* @kp: kp->arg has state: bits/lvl, map, map_type
+ * @mod_name: module name or null for all modules with the classes
*
* Enable/disable prdbgs by their class, as given in the arguments to
* DECLARE_DYNDBG_CLASSMAP. For LEVEL map-types, enforce relative
@@ -659,7 +667,9 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
*
* Returns: 0 or <0 if error.
*/
-int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
+static int param_set_dyndbg_module_classes(const char *instr,
+ const struct kernel_param *kp,
+ const char *mod_name)
{
const struct ddebug_class_param *dcp = kp->arg;
const struct ddebug_class_map *map = dcp->map;
@@ -683,8 +693,8 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
KP_NAME(kp), inrep, CLASSMAP_BITMASK(map->length));
inrep &= CLASSMAP_BITMASK(map->length);
}
- v2pr_info("bits:%lx > %s\n", inrep, KP_NAME(kp));
- totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits);
+ v2pr_info("bits:0x%lx > %s.%s\n", inrep, mod_name ?: "*", KP_NAME(kp));
+ totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits, mod_name);
*dcp->bits = inrep;
break;
case DD_CLASS_TYPE_LEVEL_NUM:
@@ -697,7 +707,7 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
old_bits = CLASSMAP_BITMASK(*dcp->lvl);
new_bits = CLASSMAP_BITMASK(inrep);
v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));
- totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits);
+ totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits, mod_name);
*dcp->lvl = inrep;
break;
default:
@@ -707,16 +717,33 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
vpr_info("%s: total matches: %d\n", KP_NAME(kp), totct);
return 0;
}
+
+/**
+ * param_set_dyndbg_classes - classmap kparam setter
+ * @instr: string echo>d to sysfs, input depends on map_type
+ * @kp: kp->arg has state: bits/lvl, map, map_type
+ *
+ * enable/disable all class'd pr_debugs in the classmap. For LEVEL
+ * map-types, enforce * relative levels by bitpos.
+ *
+ * Returns: 0 or <0 if error.
+ */
+int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
+{
+ return param_set_dyndbg_module_classes(instr, kp, NULL);
+}
EXPORT_SYMBOL(param_set_dyndbg_classes);
/**
- * param_get_dyndbg_classes - classes reader
+ * param_get_dyndbg_classes - classmap kparam getter
* @buffer: string description of controlled bits -> classes
* @kp: kp->arg has state: bits, map
*
- * Reads last written state, underlying prdbg state may have been
- * altered by direct >control. Displays 0x for DISJOINT, 0-N for
- * LEVEL Returns: #chars written or <0 on error
+ * Reads last written state, underlying pr_debug states may have been
+ * altered by direct >control. Displays 0x for DISJOINT classmap
+ * types, 0-N for LEVEL types.
+ *
+ * Returns: ct of chars written or <0 on error
*/
int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 21/29] dyndbg: tighten fn-sig of ddebug_apply_class_bitmap
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (19 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 22/29] dyndbg: replace classmap list with an array-slice Jim Cromie
` (7 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
old_bits arg is currently a pointer to the input bits, but this could
allow inadvertent changes to the input by the fn. Disallow this.
And constify new_bits while here.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v2: move RvB after SoB
---
lib/dynamic_debug.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index a619cefd801f..364a55fc9447 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -617,7 +617,8 @@ static int ddebug_exec_queries(char *query, const char *modname)
/* apply a new class-param setting */
static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
- unsigned long *new_bits, unsigned long *old_bits,
+ const unsigned long *new_bits,
+ const unsigned long old_bits,
const char *query_modname)
{
#define QUERY_SIZE 128
@@ -626,12 +627,12 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
int matches = 0;
int bi, ct;
- if (*new_bits != *old_bits)
+ if (*new_bits != old_bits)
v2pr_info("apply bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
- *old_bits, query_modname ?: "'*'");
+ old_bits, query_modname ?: "'*'");
for (bi = 0; bi < map->length; bi++) {
- if (test_bit(bi, new_bits) == test_bit(bi, old_bits))
+ if (test_bit(bi, new_bits) == test_bit(bi, &old_bits))
continue;
snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
@@ -643,9 +644,9 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,
ct, map->class_names[bi], *new_bits);
}
- if (*new_bits != *old_bits)
+ if (*new_bits != old_bits)
v2pr_info("applied bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
- *old_bits, query_modname ?: "'*'");
+ old_bits, query_modname ?: "'*'");
return matches;
}
@@ -694,7 +695,7 @@ static int param_set_dyndbg_module_classes(const char *instr,
inrep &= CLASSMAP_BITMASK(map->length);
}
v2pr_info("bits:0x%lx > %s.%s\n", inrep, mod_name ?: "*", KP_NAME(kp));
- totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits, mod_name);
+ totct += ddebug_apply_class_bitmap(dcp, &inrep, *dcp->bits, mod_name);
*dcp->bits = inrep;
break;
case DD_CLASS_TYPE_LEVEL_NUM:
@@ -707,7 +708,7 @@ static int param_set_dyndbg_module_classes(const char *instr,
old_bits = CLASSMAP_BITMASK(*dcp->lvl);
new_bits = CLASSMAP_BITMASK(inrep);
v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));
- totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits, mod_name);
+ totct += ddebug_apply_class_bitmap(dcp, &new_bits, old_bits, mod_name);
*dcp->lvl = inrep;
break;
default:
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 22/29] dyndbg: replace classmap list with an array-slice
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (20 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 21/29] dyndbg: tighten fn-sig of ddebug_apply_class_bitmap Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 23/29] dyndbg: macrofy a 2-index for-loop pattern Jim Cromie
` (6 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
Classmaps are stored in an elf section/array, but currently are
individually list-linked onto dyndbg's per-module ddebug_table for
operation. This is unnecessary.
Just like dyndbg's descriptors, classmaps are packed in compile order;
so even with many builtin modules employing multiple classmaps, each
modules' maps are packed contiguously, and can be treated as a
array-start-address & array-length.
So this drops the whole list building operation done in
ddebug_attach_module_classes(), and removes the list-head members of
the classmap structs. The "select-by-modname" condition is reused to
find the start,end of the subrange of classmaps belonging to the module.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v2: RvB after SoB
---
include/linux/dynamic_debug.h | 1 -
lib/dynamic_debug.c | 65 +++++++++++++++++++++++--------------------
2 files changed, 35 insertions(+), 31 deletions(-)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 17cc1ae7baef..5e484862f814 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -87,7 +87,6 @@ enum ddebug_class_map_type {
};
struct ddebug_class_map {
- struct list_head link;
struct module *mod;
const char *mod_name; /* needed for builtins */
const char **class_names;
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 364a55fc9447..5abf6ff6be52 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -45,10 +45,11 @@ extern struct ddebug_class_map __start___dyndbg_classes[];
extern struct ddebug_class_map __stop___dyndbg_classes[];
struct ddebug_table {
- struct list_head link, maps;
+ struct list_head link;
const char *mod_name;
- unsigned int num_ddebugs;
struct _ddebug *ddebugs;
+ struct ddebug_class_map *classes;
+ unsigned int num_ddebugs, num_classes;
};
struct ddebug_query {
@@ -149,12 +150,13 @@ static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
}
static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
- const char *class_string, int *class_id)
+ const char *class_string,
+ int *class_id)
{
struct ddebug_class_map *map;
- int idx;
+ int i, idx;
- list_for_each_entry(map, &dt->maps, link) {
+ for (map = dt->classes, i = 0; i < dt->num_classes; i++, map++) {
idx = match_string(map->class_names, map->length, class_string);
if (idx >= 0) {
*class_id = idx + map->base;
@@ -165,7 +167,6 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
return NULL;
}
-#define __outvar /* filled by callee */
/*
* Search the tables for _ddebug's which match the given `query' and
* apply the `flags' and `mask' to them. Returns number of matching
@@ -231,7 +232,7 @@ static int ddebug_change(const struct ddebug_query *query,
unsigned int nfound = 0;
struct flagsbuf fbuf, nbuf;
struct ddebug_class_map *map = NULL;
- int __outvar valid_class;
+ int valid_class;
/* search for matching ddebugs */
mutex_lock(&ddebug_lock);
@@ -1070,9 +1071,10 @@ static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)
static const char *ddebug_class_name(struct ddebug_iter *iter, struct _ddebug *dp)
{
- struct ddebug_class_map *map;
+ struct ddebug_class_map *map = iter->table->classes;
+ int i, nc = iter->table->num_classes;
- list_for_each_entry(map, &iter->table->maps, link)
+ for (i = 0; i < nc; i++, map++)
if (class_in_range(dp->class_id, map))
return map->class_names[dp->class_id - map->base];
@@ -1156,30 +1158,34 @@ static const struct proc_ops proc_fops = {
.proc_write = ddebug_proc_write
};
-static void ddebug_attach_module_classes(struct ddebug_table *dt,
- struct ddebug_class_map *classes,
- int num_classes)
+static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug_info *di)
{
struct ddebug_class_map *cm;
- int i, j, ct = 0;
+ int i, nc = 0;
- for (cm = classes, i = 0; i < num_classes; i++, cm++) {
+ /*
+ * Find this module's classmaps in a subrange/wholerange of
+ * the builtin/modular classmap vector/section. Save the start
+ * and length of the subrange at its edges.
+ */
+ for (cm = di->classes, i = 0; i < di->num_classes; i++, cm++) {
if (!strcmp(cm->mod_name, dt->mod_name)) {
-
- v2pr_info("class[%d]: module:%s base:%d len:%d ty:%d\n", i,
- cm->mod_name, cm->base, cm->length, cm->map_type);
-
- for (j = 0; j < cm->length; j++)
- v3pr_info(" %d: %d %s\n", j + cm->base, j,
- cm->class_names[j]);
-
- list_add(&cm->link, &dt->maps);
- ct++;
+ if (!nc) {
+ v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
+ i, cm->mod_name, cm->base, cm->length, cm->map_type);
+ dt->classes = cm;
+ }
+ nc++;
+ } else if (nc) {
+ /* end of matching classmaps */
+ break;
}
}
- if (ct)
- vpr_info("module:%s attached %d classes\n", dt->mod_name, ct);
+ if (nc) {
+ dt->num_classes = nc;
+ vpr_info("module:%s attached %d classes\n", dt->mod_name, nc);
+ }
}
/*
@@ -1211,10 +1217,9 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
dt->num_ddebugs = di->num_descs;
INIT_LIST_HEAD(&dt->link);
- INIT_LIST_HEAD(&dt->maps);
if (di->classes && di->num_classes)
- ddebug_attach_module_classes(dt, di->classes, di->num_classes);
+ ddebug_attach_module_classes(dt, di);
mutex_lock(&ddebug_lock);
list_add_tail(&dt->link, &ddebug_tables);
@@ -1327,8 +1332,8 @@ static void ddebug_remove_all_tables(void)
mutex_lock(&ddebug_lock);
while (!list_empty(&ddebug_tables)) {
struct ddebug_table *dt = list_entry(ddebug_tables.next,
- struct ddebug_table,
- link);
+ struct ddebug_table,
+ link);
ddebug_table_free(dt);
}
mutex_unlock(&ddebug_lock);
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 23/29] dyndbg: macrofy a 2-index for-loop pattern
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (21 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 22/29] dyndbg: replace classmap list with an array-slice Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 24/29] dyndbg: pin class param storage to u32 Jim Cromie
` (5 subsequent siblings)
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
dynamic-debug currently has 2 __sections (__dyndbg, __dyndb_classes),
struct _ddebug_info keeps track of them both, with 2 members each:
_vec and _vec#_len.
We need to loop over these sections, with index and record pointer,
making ref to both _vec and _vec_len. This is already fiddly and
error-prone, and will get worse as we add a 3rd section.
Lets instead embed/abstract the fiddly-ness in the `for_subvec()`
macro, and avoid repeating it going forward.
This is a for-loop macro expander, so it syntactically expects to
precede either a single statement or a { block } of them, and the
usual typeof or do-while-0 tricks are unavailable to fix the
multiple-expansion warning.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v2: move RvB after SoB
---
lib/dynamic_debug.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 5abf6ff6be52..59e3c0eccbed 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -149,6 +149,20 @@ static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
query->first_lineno, query->last_lineno, query->class_string);
}
+/*
+ * simplify a repeated for-loop pattern walking N steps in a T _vec
+ * member inside a struct _box. It expects int i and T *_sp to be
+ * declared in the caller.
+ * @_i: caller provided counter.
+ * @_sp: cursor into _vec, to examine each item.
+ * @_box: ptr to a struct containing @_vec member
+ * @_vec: name of a member in @_box
+ */
+#define for_subvec(_i, _sp, _box, _vec) \
+ for ((_i) = 0, (_sp) = (_box)->_vec; \
+ (_i) < (_box)->num_##_vec; \
+ (_i)++, (_sp)++) /* { block } */
+
static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
const char *class_string,
int *class_id)
@@ -156,7 +170,7 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
struct ddebug_class_map *map;
int i, idx;
- for (map = dt->classes, i = 0; i < dt->num_classes; i++, map++) {
+ for_subvec(i, map, dt, classes) {
idx = match_string(map->class_names, map->length, class_string);
if (idx >= 0) {
*class_id = idx + map->base;
@@ -1168,8 +1182,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
* the builtin/modular classmap vector/section. Save the start
* and length of the subrange at its edges.
*/
- for (cm = di->classes, i = 0; i < di->num_classes; i++, cm++) {
-
+ for_subvec(i, cm, di, classes) {
if (!strcmp(cm->mod_name, dt->mod_name)) {
if (!nc) {
v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 24/29] dyndbg: pin class param storage to u32
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (22 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 23/29] dyndbg: macrofy a 2-index for-loop pattern Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:10 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info Jim Cromie
` (4 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
Currently, `struct ddebug_class_param` uses pointers to `unsigned
long` values which store the state of `bits` and `lvl`, so it changes
sizes depending upon the architecture. Make it always u32 for
consistency.
The bits field references __drm_debug, which was unsigned int, before
commit f158936b60a7 ("drm: POC drm on dyndbg - use in core, 2 helpers, 3 drivers.")
changed it to unsigned long. This patch changes it back.
That enlargement was a thinko; although modules can have up to 63
classes, and *could* have all those classes in a single classmap, the
real reason is to support multiple classmaps (with non-overlapping
class-id ranges).
32 bits is a practical limit for a class-param's usability since all
classes are set together with a single write of a hex value; 16 would
be a realistic limit, drm.debug has ~12 classes.
#> echo 0x0fff > /sys/module/drm/parameters/debug
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v5: u32 for all arches
v4: undo change struct ddebug_class_param to _ddebug_class_param
v3:
fix undefd behavior when classmaps is all 64 bits.
change module_param_named( type-arg from ulong to ullong)
change struct ddebug_class_param to _ddebug_class_param
in drivers/gpu/drm/drm_print.{c,h}
api change later
v2:
patch was "make bits & lvl same size"
but that size was unsigned long, only 32 bits on i386 etc
use u64 for all bits, and %llu %llx
u64-fix
u64-drm-dbg
---
drivers/gpu/drm/drm_print.c | 4 ++--
include/drm/drm_print.h | 2 +-
include/linux/dynamic_debug.h | 4 ++--
lib/dynamic_debug.c | 35 +++++++++++++++++++----------------
lib/test_dynamic_debug.c | 2 +-
5 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c
index ac0288dabfc0..0a4091807722 100644
--- a/drivers/gpu/drm/drm_print.c
+++ b/drivers/gpu/drm/drm_print.c
@@ -40,7 +40,7 @@
* __drm_debug: Enable debug output.
* Bitmask of DRM_UT_x. See include/drm/drm_print.h for details.
*/
-unsigned long __drm_debug;
+u32 __drm_debug;
EXPORT_SYMBOL(__drm_debug);
MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
@@ -54,7 +54,7 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat
"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
#if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG)
-module_param_named(debug, __drm_debug, ulong, 0600);
+module_param_named(debug, __drm_debug, uint, 0600);
#else
/* classnames must match vals of enum drm_debug_category */
DECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0,
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
index ab017b05e175..ed7ce7d7b74c 100644
--- a/include/drm/drm_print.h
+++ b/include/drm/drm_print.h
@@ -39,7 +39,7 @@ struct drm_device;
struct seq_file;
/* Do *not* use outside of drm_print.[ch]! */
-extern unsigned long __drm_debug;
+extern u32 __drm_debug;
/**
* DOC: print
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 5e484862f814..7aff85bd145e 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -105,8 +105,8 @@ struct _ddebug_info {
struct ddebug_class_param {
union {
- unsigned long *bits;
- unsigned int *lvl;
+ u32 *bits;
+ u32 *lvl;
};
char flags[8];
const struct ddebug_class_map *map;
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 59e3c0eccbed..60b67face46e 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -582,6 +582,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)
pr_err("query parse failed\n");
return -EINVAL;
}
+
/* actually go and implement the change */
nfound = ddebug_change(&query, &modifiers);
v3pr_info_dq(&query, nfound ? "applied" : "no-match");
@@ -632,8 +633,7 @@ static int ddebug_exec_queries(char *query, const char *modname)
/* apply a new class-param setting */
static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
- const unsigned long *new_bits,
- const unsigned long old_bits,
+ const u32 *new_bits, const u32 old_bits,
const char *query_modname)
{
#define QUERY_SIZE 128
@@ -643,24 +643,27 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
int bi, ct;
if (*new_bits != old_bits)
- v2pr_info("apply bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
+ v2pr_info("apply bitmap: 0x%x to: 0x%x for %s\n", *new_bits,
old_bits, query_modname ?: "'*'");
for (bi = 0; bi < map->length; bi++) {
- if (test_bit(bi, new_bits) == test_bit(bi, &old_bits))
+ bool new_b = !!(*new_bits & BIT(bi));
+ bool old_b = !!(old_bits & BIT(bi));
+
+ if (new_b == old_b)
continue;
snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
- test_bit(bi, new_bits) ? '+' : '-', dcp->flags);
+ new_b ? '+' : '-', dcp->flags);
ct = ddebug_exec_queries(query, query_modname);
matches += ct;
- v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,
+ v2pr_info("bit_%d: %d matches on class: %s -> 0x%x\n", bi,
ct, map->class_names[bi], *new_bits);
}
if (*new_bits != old_bits)
- v2pr_info("applied bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
+ v2pr_info("applied bitmap: 0x%x to: 0x%x for %s\n", *new_bits,
old_bits, query_modname ?: "'*'");
return matches;
@@ -669,7 +672,7 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
/* stub to later conditionally add "$module." prefix where not already done */
#define KP_NAME(kp) kp->name
-#define CLASSMAP_BITMASK(width) ((1UL << (width)) - 1)
+#define CLASSMAP_BITMASK(width) ((width) >= 32 ? ~0U : (1U << (width)) - 1)
/**
* param_set_dyndbg_classes - class FOO >control
@@ -689,10 +692,10 @@ static int param_set_dyndbg_module_classes(const char *instr,
{
const struct ddebug_class_param *dcp = kp->arg;
const struct ddebug_class_map *map = dcp->map;
- unsigned long inrep, new_bits, old_bits;
+ u32 inrep, new_bits, old_bits;
int rc, totct = 0;
- rc = kstrtoul(instr, 0, &inrep);
+ rc = kstrtou32(instr, 0, &inrep);
if (rc) {
int len = strcspn(instr, "\n");
@@ -705,24 +708,24 @@ static int param_set_dyndbg_module_classes(const char *instr,
case DD_CLASS_TYPE_DISJOINT_BITS:
/* expect bits. mask and warn if too many */
if (inrep & ~CLASSMAP_BITMASK(map->length)) {
- pr_warn("%s: input: 0x%lx exceeds mask: 0x%lx, masking\n",
+ pr_warn("%s: input: 0x%x exceeds mask: 0x%x, masking\n",
KP_NAME(kp), inrep, CLASSMAP_BITMASK(map->length));
inrep &= CLASSMAP_BITMASK(map->length);
}
- v2pr_info("bits:0x%lx > %s.%s\n", inrep, mod_name ?: "*", KP_NAME(kp));
+ v2pr_info("bits:0x%x > %s.%s\n", inrep, mod_name ?: "*", KP_NAME(kp));
totct += ddebug_apply_class_bitmap(dcp, &inrep, *dcp->bits, mod_name);
*dcp->bits = inrep;
break;
case DD_CLASS_TYPE_LEVEL_NUM:
/* input is bitpos, of highest verbosity to be enabled */
if (inrep > map->length) {
- pr_warn("%s: level:%ld exceeds max:%d, clamping\n",
+ pr_warn("%s: level:%u exceeds max:%d, clamping\n",
KP_NAME(kp), inrep, map->length);
inrep = map->length;
}
old_bits = CLASSMAP_BITMASK(*dcp->lvl);
new_bits = CLASSMAP_BITMASK(inrep);
- v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));
+ v2pr_info("lvl:%u bits:0x%x > %s\n", inrep, new_bits, KP_NAME(kp));
totct += ddebug_apply_class_bitmap(dcp, &new_bits, old_bits, mod_name);
*dcp->lvl = inrep;
break;
@@ -768,9 +771,9 @@ int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
switch (map->map_type) {
case DD_CLASS_TYPE_DISJOINT_BITS:
- return scnprintf(buffer, PAGE_SIZE, "0x%lx\n", *dcp->bits);
+ return scnprintf(buffer, PAGE_SIZE, "0x%x\n", *dcp->bits);
case DD_CLASS_TYPE_LEVEL_NUM:
- return scnprintf(buffer, PAGE_SIZE, "%d\n", *dcp->lvl);
+ return scnprintf(buffer, PAGE_SIZE, "%u\n", *dcp->lvl);
default:
return -1;
}
diff --git a/lib/test_dynamic_debug.c b/lib/test_dynamic_debug.c
index 74d183ebf3e0..9e8e028461ad 100644
--- a/lib/test_dynamic_debug.c
+++ b/lib/test_dynamic_debug.c
@@ -40,7 +40,7 @@ module_param_cb(do_prints, ¶m_ops_do_prints, NULL, 0600);
* - tie together sysname, mapname, bitsname, flagsname
*/
#define DD_SYS_WRAP(_model, _flags) \
- static unsigned long bits_##_model; \
+ static u32 bits_##_model; \
static struct ddebug_class_param _flags##_model = { \
.bits = &bits_##_model, \
.flags = #_flags, \
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (23 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 24/29] dyndbg: pin class param storage to u32 Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:09 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info Jim Cromie
` (3 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
recompose struct _ddebug_info, inserting proper sub-structs.
The struct _ddebug_info has 2 pairs of _vec, num_##_vec fields, for
descs and classes respectively. for_subvec() makes walking these
vectors less cumbersome, now lets move those field pairs into their
own "vec" structs: _ddebug_descs & _ddebug_class_maps, and re-compose
struct _ddebug_info to contain them cleanly. This also lets us get
rid of for_subvec()'s num_##_vec paste-up.
Also recompose struct ddebug_table to contain a _ddebug_info. This
reinforces _ddebug_info's use as a cursor into relevant data for a
builtin module, and access to the full _ddebug state for modules.
NOTES:
rename section:__dyndbg_classes to _class_maps, to better align with
struct _ddebug_class_maps.
names together, for more obvious name pairing.
Invariant: These vectors ref a contiguous subrange of __section memory
in builtin/DATA or in loadable modules via mod->dyndbg_info; with
guaranteed life-time for us.
struct module contains a _ddebug_info field and module/main.c sets it
up, so that gets adjusted rather obviously.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v3: squash in section name changes.
v2:
Move RvB after SoB
In structs _ddebug_descs & _ddebug_class_maps, change int length to unsigned int
No use of <0 vals is contemplated.
dyndbg: improve section names
change __dyndbg to __dyndbg_descs
change __dyndbg_classes to __dyndbg_class_maps
this sets up for adding __dyndbg_class_users
fixup-drmprint
---
include/asm-generic/dyndbg.lds.h | 14 +++---
include/linux/dynamic_debug.h | 34 +++++++++-----
kernel/module/main.c | 12 ++---
lib/dynamic_debug.c | 98 ++++++++++++++++++++--------------------
4 files changed, 85 insertions(+), 73 deletions(-)
diff --git a/include/asm-generic/dyndbg.lds.h b/include/asm-generic/dyndbg.lds.h
index 9d8951bef688..ec661f9f3793 100644
--- a/include/asm-generic/dyndbg.lds.h
+++ b/include/asm-generic/dyndbg.lds.h
@@ -3,16 +3,16 @@
#define __ASM_GENERIC_DYNDBG_LDS_H
#include <asm-generic/bounded_sections.lds.h>
-#define DYNDBG_SECTIONS() \
- BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
- BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes)
+#define DYNDBG_SECTIONS() \
+ BOUNDED_SECTION_BY(__dyndbg_descs, ___dyndbg_descs) \
+ BOUNDED_SECTION_BY(__dyndbg_class_maps, ___dyndbg_class_maps)
#define MOD_DYNDBG_SECTIONS() \
- __dyndbg 0 : ALIGN(8) { \
- KEEP(*(__dyndbg)) \
+ __dyndbg_descs 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_descs)) \
} \
- __dyndbg_classes 0 : ALIGN(8) { \
- KEEP(*(__dyndbg_classes)) \
+ __dyndbg_class_maps 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_class_maps)) \
}
#endif /* __ASM_GENERIC_DYNDBG_LDS_H */
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 7aff85bd145e..00cfc8d9f239 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -87,7 +87,7 @@ enum ddebug_class_map_type {
};
struct ddebug_class_map {
- struct module *mod;
+ struct module *mod; /* NULL for builtins */
const char *mod_name; /* needed for builtins */
const char **class_names;
const int length;
@@ -95,12 +95,24 @@ struct ddebug_class_map {
enum ddebug_class_map_type map_type;
};
-/* encapsulate linker provided built-in (or module) dyndbg data */
+/*
+ * @_ddebug_info: gathers module/builtin dyndbg_* __sections together.
+ * For builtins, it is used as a cursor, with the inner structs
+ * marking sub-vectors of the builtin __sections in DATA.
+ */
+struct _ddebug_descs {
+ struct _ddebug *start;
+ unsigned int len;
+};
+
+struct _ddebug_class_maps {
+ struct ddebug_class_map *start;
+ unsigned int len;
+};
+
struct _ddebug_info {
- struct _ddebug *descs;
- struct ddebug_class_map *classes;
- unsigned int num_descs;
- unsigned int num_classes;
+ struct _ddebug_descs descs;
+ struct _ddebug_class_maps maps;
};
struct ddebug_class_param {
@@ -121,7 +133,7 @@ struct ddebug_class_param {
/**
* DECLARE_DYNDBG_CLASSMAP - declare classnames known by a module
- * @_var: a struct _ddebug_class_map, passed to module_param_cb
+ * @_var: a struct ddebug_class_map, passed to module_param_cb
* @_maptype: enum ddebug_class_map_type, chooses bits/verbose
* @_base: offset of 1st class-name. splits .class_id space
* @classes: class-names used to control class'd prdbgs
@@ -129,7 +141,7 @@ struct ddebug_class_param {
#define DECLARE_DYNDBG_CLASSMAP(_var, _maptype, _base, ...) \
static const char *_var##_classnames[] = { __VA_ARGS__ }; \
static struct ddebug_class_map __aligned(8) __used \
- __section("__dyndbg_classes") _var = { \
+ __section("__dyndbg_class_maps") _var = { \
.mod = THIS_MODULE, \
.mod_name = DDEBUG_MODNAME, \
.base = _base, \
@@ -169,7 +181,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
#define DEFINE_DYNAMIC_DEBUG_METADATA_CLS(name, cls, fmt) \
static struct _ddebug __aligned(8) \
- __section("__dyndbg") name = { \
+ __section("__dyndbg_descs") name = { \
.modname = DDEBUG_MODNAME, \
.function = __func__, \
.filename = __FILE__, \
@@ -256,7 +268,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
* macro.
*/
#define _dynamic_func_call_cls(cls, fmt, func, ...) \
- __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
+ __dynamic_func_call_cls(__UNIQUE_ID(_ddebug), cls, fmt, func, ##__VA_ARGS__)
#define _dynamic_func_call(fmt, func, ...) \
_dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
@@ -266,7 +278,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
* with precisely the macro's varargs.
*/
#define _dynamic_func_call_cls_no_desc(cls, fmt, func, ...) \
- __dynamic_func_call_cls_no_desc(__UNIQUE_ID(ddebug), cls, fmt, \
+ __dynamic_func_call_cls_no_desc(__UNIQUE_ID(_ddebug), cls, fmt, \
func, ##__VA_ARGS__)
#define _dynamic_func_call_no_desc(fmt, func, ...) \
_dynamic_func_call_cls_no_desc(_DPRINTK_CLASS_DFLT, fmt, \
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..bd7899a91755 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2774,12 +2774,12 @@ static int find_module_sections(struct module *mod, struct load_info *info)
pr_warn("%s: Ignoring obsolete parameters\n", mod->name);
#ifdef CONFIG_DYNAMIC_DEBUG_CORE
- mod->dyndbg_info.descs = section_objs(info, "__dyndbg",
- sizeof(*mod->dyndbg_info.descs),
- &mod->dyndbg_info.num_descs);
- mod->dyndbg_info.classes = section_objs(info, "__dyndbg_classes",
- sizeof(*mod->dyndbg_info.classes),
- &mod->dyndbg_info.num_classes);
+ mod->dyndbg_info.descs.start = section_objs(info, "__dyndbg_descs",
+ sizeof(*mod->dyndbg_info.descs.start),
+ &mod->dyndbg_info.descs.len);
+ mod->dyndbg_info.maps.start = section_objs(info, "__dyndbg_class_maps",
+ sizeof(*mod->dyndbg_info.maps.start),
+ &mod->dyndbg_info.maps.len);
#endif
return 0;
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 60b67face46e..e7742fcca50b 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -39,17 +39,15 @@
#include <rdma/ib_verbs.h>
-extern struct _ddebug __start___dyndbg[];
-extern struct _ddebug __stop___dyndbg[];
-extern struct ddebug_class_map __start___dyndbg_classes[];
-extern struct ddebug_class_map __stop___dyndbg_classes[];
+extern struct _ddebug __start___dyndbg_descs[];
+extern struct _ddebug __stop___dyndbg_descs[];
+extern struct ddebug_class_map __start___dyndbg_class_maps[];
+extern struct ddebug_class_map __stop___dyndbg_class_maps[];
struct ddebug_table {
struct list_head link;
const char *mod_name;
- struct _ddebug *ddebugs;
- struct ddebug_class_map *classes;
- unsigned int num_ddebugs, num_classes;
+ struct _ddebug_info info;
};
struct ddebug_query {
@@ -159,18 +157,18 @@ static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
* @_vec: name of a member in @_box
*/
#define for_subvec(_i, _sp, _box, _vec) \
- for ((_i) = 0, (_sp) = (_box)->_vec; \
- (_i) < (_box)->num_##_vec; \
+ for ((_i) = 0, (_sp) = (_box)->_vec.start; \
+ (_i) < (_box)->_vec.len; \
(_i)++, (_sp)++) /* { block } */
static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
- const char *class_string,
- int *class_id)
+ const char *class_string,
+ int *class_id)
{
struct ddebug_class_map *map;
int i, idx;
- for_subvec(i, map, dt, classes) {
+ for_subvec(i, map, &dt->info, maps) {
idx = match_string(map->class_names, map->length, class_string);
if (idx >= 0) {
*class_id = idx + map->base;
@@ -267,8 +265,8 @@ static int ddebug_change(const struct ddebug_query *query,
valid_class = _DPRINTK_CLASS_DFLT;
}
- for (i = 0; i < dt->num_ddebugs; i++) {
- struct _ddebug *dp = &dt->ddebugs[i];
+ for (i = 0; i < dt->info.descs.len; i++) {
+ struct _ddebug *dp = &dt->info.descs.start[i];
if (!ddebug_match_desc(query, dp, valid_class))
continue;
@@ -1014,8 +1012,8 @@ static struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter)
}
iter->table = list_entry(ddebug_tables.next,
struct ddebug_table, link);
- iter->idx = iter->table->num_ddebugs;
- return &iter->table->ddebugs[--iter->idx];
+ iter->idx = iter->table->info.descs.len;
+ return &iter->table->info.descs.start[--iter->idx];
}
/*
@@ -1036,10 +1034,10 @@ static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
}
iter->table = list_entry(iter->table->link.next,
struct ddebug_table, link);
- iter->idx = iter->table->num_ddebugs;
+ iter->idx = iter->table->info.descs.len;
--iter->idx;
}
- return &iter->table->ddebugs[iter->idx];
+ return &iter->table->info.descs.start[iter->idx];
}
/*
@@ -1083,16 +1081,19 @@ static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)
return dp;
}
-#define class_in_range(class_id, map) \
- (class_id >= map->base && class_id < map->base + map->length)
+static bool ddebug_class_in_range(const int class_id, const struct ddebug_class_map *map)
+{
+ return (class_id >= map->base &&
+ class_id < map->base + map->length);
+}
-static const char *ddebug_class_name(struct ddebug_iter *iter, struct _ddebug *dp)
+static const char *ddebug_class_name(struct ddebug_table *dt, struct _ddebug *dp)
{
- struct ddebug_class_map *map = iter->table->classes;
- int i, nc = iter->table->num_classes;
+ struct ddebug_class_map *map;
+ int i;
- for (i = 0; i < nc; i++, map++)
- if (class_in_range(dp->class_id, map))
+ for_subvec(i, map, &dt->info, maps)
+ if (ddebug_class_in_range(dp->class_id, map))
return map->class_names[dp->class_id - map->base];
return NULL;
@@ -1125,7 +1126,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
seq_putc(m, '"');
if (dp->class_id != _DPRINTK_CLASS_DFLT) {
- class = ddebug_class_name(iter, dp);
+ class = ddebug_class_name(iter->table, dp);
if (class)
seq_printf(m, " class:%s", class);
else
@@ -1185,12 +1186,12 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
* the builtin/modular classmap vector/section. Save the start
* and length of the subrange at its edges.
*/
- for_subvec(i, cm, di, classes) {
+ for_subvec(i, cm, di, maps) {
if (!strcmp(cm->mod_name, dt->mod_name)) {
if (!nc) {
v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
i, cm->mod_name, cm->base, cm->length, cm->map_type);
- dt->classes = cm;
+ dt->info.maps.start = cm;
}
nc++;
} else if (nc) {
@@ -1199,7 +1200,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
}
}
if (nc) {
- dt->num_classes = nc;
+ dt->info.maps.len = nc;
vpr_info("module:%s attached %d classes\n", dt->mod_name, nc);
}
}
@@ -1212,10 +1213,10 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
{
struct ddebug_table *dt;
- if (!di->num_descs)
+ if (!di->descs.len)
return 0;
- v3pr_info("add-module: %s %d sites\n", modname, di->num_descs);
+ v3pr_info("add-module: %s %d sites\n", modname, di->descs.len);
dt = kzalloc_obj(*dt);
if (dt == NULL) {
@@ -1229,19 +1230,18 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
* this struct ddebug_table.
*/
dt->mod_name = modname;
- dt->ddebugs = di->descs;
- dt->num_ddebugs = di->num_descs;
+ dt->info = *di;
INIT_LIST_HEAD(&dt->link);
- if (di->classes && di->num_classes)
+ if (di->maps.len)
ddebug_attach_module_classes(dt, di);
mutex_lock(&ddebug_lock);
list_add_tail(&dt->link, &ddebug_tables);
mutex_unlock(&ddebug_lock);
- vpr_info("%3u debug prints in module %s\n", di->num_descs, modname);
+ vpr_info("%3u debug prints in module %s\n", di->descs.len, modname);
return 0;
}
@@ -1388,10 +1388,10 @@ static int __init dynamic_debug_init(void)
char *cmdline;
struct _ddebug_info di = {
- .descs = __start___dyndbg,
- .classes = __start___dyndbg_classes,
- .num_descs = __stop___dyndbg - __start___dyndbg,
- .num_classes = __stop___dyndbg_classes - __start___dyndbg_classes,
+ .descs.start = __start___dyndbg_descs,
+ .maps.start = __start___dyndbg_class_maps,
+ .descs.len = __stop___dyndbg_descs - __start___dyndbg_descs,
+ .maps.len = __stop___dyndbg_class_maps - __start___dyndbg_class_maps,
};
#ifdef CONFIG_MODULES
@@ -1402,7 +1402,7 @@ static int __init dynamic_debug_init(void)
}
#endif /* CONFIG_MODULES */
- if (&__start___dyndbg == &__stop___dyndbg) {
+ if (&__start___dyndbg_descs == &__stop___dyndbg_descs) {
if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) {
pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n");
return 1;
@@ -1412,16 +1412,16 @@ static int __init dynamic_debug_init(void)
return 0;
}
- iter = iter_mod_start = __start___dyndbg;
+ iter = iter_mod_start = __start___dyndbg_descs;
modname = iter->modname;
i = mod_sites = mod_ct = 0;
- for (; iter < __stop___dyndbg; iter++, i++, mod_sites++) {
+ for (; iter < __stop___dyndbg_descs; iter++, i++, mod_sites++) {
if (strcmp(modname, iter->modname)) {
mod_ct++;
- di.num_descs = mod_sites;
- di.descs = iter_mod_start;
+ di.descs.len = mod_sites;
+ di.descs.start = iter_mod_start;
ret = ddebug_add_module(&di, modname);
if (ret)
goto out_err;
@@ -1431,19 +1431,19 @@ static int __init dynamic_debug_init(void)
iter_mod_start = iter;
}
}
- di.num_descs = mod_sites;
- di.descs = iter_mod_start;
+ di.descs.len = mod_sites;
+ di.descs.start = iter_mod_start;
ret = ddebug_add_module(&di, modname);
if (ret)
goto out_err;
ddebug_init_success = 1;
- vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n",
+ vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg_descs section\n",
i, mod_ct, (int)((mod_ct * sizeof(struct ddebug_table)) >> 10),
(int)((i * sizeof(struct _ddebug)) >> 10));
- if (di.num_classes)
- v2pr_info(" %d builtin ddebug class-maps\n", di.num_classes);
+ if (di.maps.len)
+ v2pr_info(" %d builtin ddebug class-maps\n", di.maps.len);
/* now that ddebug tables are loaded, process all boot args
* again to find and activate queries given in dyndbg params.
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (24 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:13 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 27/29] dyndbg: hoist classmap-filter-by-modname up to ddebug_add_module Jim Cromie
` (2 subsequent siblings)
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
struct _ddebug_info already has most of dyndbg's info for a module;
push debug_table.mod_name down into it, finishing the encapsulation.
This allows refactoring several callchains, passing &_ddebug_info
instead of &ddebug_table, and hoisting the "&dt->info" deref up
instead of repeating it thru the callchans
ddebug_table contains a _ddebug_info member, so code with a ptr to a
ddebug_table still have access to mod_name, just now with "->info."
added in.
In static ddebug_add_module(&di), reinforce the cursor-model by
dropping the modname arg, and setting di->mod_name at each caller.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v2: move RvB after SoB
old-v12
. moved up 1 position in series, ahead of hoist...
---
include/linux/dynamic_debug.h | 1 +
lib/dynamic_debug.c | 52 ++++++++++++++++++++++---------------------
2 files changed, 28 insertions(+), 25 deletions(-)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 00cfc8d9f239..35e03b0eba6a 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -111,6 +111,7 @@ struct _ddebug_class_maps {
};
struct _ddebug_info {
+ const char *mod_name;
struct _ddebug_descs descs;
struct _ddebug_class_maps maps;
};
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index e7742fcca50b..905277fa495b 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -46,7 +46,6 @@ extern struct ddebug_class_map __stop___dyndbg_class_maps[];
struct ddebug_table {
struct list_head link;
- const char *mod_name;
struct _ddebug_info info;
};
@@ -249,11 +248,12 @@ static int ddebug_change(const struct ddebug_query *query,
/* search for matching ddebugs */
mutex_lock(&ddebug_lock);
list_for_each_entry(dt, &ddebug_tables, link) {
+ struct _ddebug_info *di = &dt->info;
/* match against the module name */
if (query->module &&
- !match_wildcard_hyphen(query->module, dt->mod_name) &&
- !match_wildcard_hyphen(query->module, kbasename(dt->mod_name)))
+ !match_wildcard_hyphen(query->module, di->mod_name) &&
+ !match_wildcard_hyphen(query->module, kbasename(di->mod_name)))
continue;
if (query->class_string) {
@@ -265,8 +265,8 @@ static int ddebug_change(const struct ddebug_query *query,
valid_class = _DPRINTK_CLASS_DFLT;
}
- for (i = 0; i < dt->info.descs.len; i++) {
- struct _ddebug *dp = &dt->info.descs.start[i];
+ for (i = 0; i < di->descs.len; i++) {
+ struct _ddebug *dp = &di->descs.start[i];
if (!ddebug_match_desc(query, dp, valid_class))
continue;
@@ -286,7 +286,7 @@ static int ddebug_change(const struct ddebug_query *query,
#endif
v4pr_info("changed %s:%d [%s]%s %s => %s\n",
trim_prefix(dp->filename), dp->lineno,
- dt->mod_name, dp->function,
+ di->mod_name, dp->function,
ddebug_describe_flags(dp->flags, &fbuf),
ddebug_describe_flags(newflags, &nbuf));
dp->flags = newflags;
@@ -1087,12 +1087,12 @@ static bool ddebug_class_in_range(const int class_id, const struct ddebug_class_
class_id < map->base + map->length);
}
-static const char *ddebug_class_name(struct ddebug_table *dt, struct _ddebug *dp)
+static const char *ddebug_class_name(struct _ddebug_info *di, struct _ddebug *dp)
{
struct ddebug_class_map *map;
int i;
- for_subvec(i, map, &dt->info, maps)
+ for_subvec(i, map, di, maps)
if (ddebug_class_in_range(dp->class_id, map))
return map->class_names[dp->class_id - map->base];
@@ -1120,13 +1120,13 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
seq_printf(m, "%s:%u [%s]%s =%s \"",
trim_prefix(dp->filename), dp->lineno,
- iter->table->mod_name, dp->function,
+ iter->table->info.mod_name, dp->function,
ddebug_describe_flags(dp->flags, &flags));
seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\"");
seq_putc(m, '"');
if (dp->class_id != _DPRINTK_CLASS_DFLT) {
- class = ddebug_class_name(iter->table, dp);
+ class = ddebug_class_name(&iter->table->info, dp);
if (class)
seq_printf(m, " class:%s", class);
else
@@ -1187,7 +1187,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
* and length of the subrange at its edges.
*/
for_subvec(i, cm, di, maps) {
- if (!strcmp(cm->mod_name, dt->mod_name)) {
+ if (!strcmp(cm->mod_name, dt->info.mod_name)) {
if (!nc) {
v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
i, cm->mod_name, cm->base, cm->length, cm->map_type);
@@ -1201,7 +1201,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
}
if (nc) {
dt->info.maps.len = nc;
- vpr_info("module:%s attached %d classes\n", dt->mod_name, nc);
+ vpr_info("module:%s attached %d classes\n", dt->info.mod_name, nc);
}
}
@@ -1209,27 +1209,26 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
* Allocate a new ddebug_table for the given module
* and add it to the global list.
*/
-static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
+static int ddebug_add_module(struct _ddebug_info *di)
{
struct ddebug_table *dt;
if (!di->descs.len)
return 0;
- v3pr_info("add-module: %s %d sites\n", modname, di->descs.len);
+ v3pr_info("add-module: %s %d sites\n", di->mod_name, di->descs.len);
dt = kzalloc_obj(*dt);
if (dt == NULL) {
- pr_err("error adding module: %s\n", modname);
+ pr_err("error adding module: %s\n", di->mod_name);
return -ENOMEM;
}
/*
- * For built-in modules, name lives in .rodata and is
- * immortal. For loaded modules, name points at the name[]
- * member of struct module, which lives at least as long as
- * this struct ddebug_table.
+ * For built-in modules, name (as supplied in di by its
+ * callers) lives in .rodata and is immortal. For loaded
+ * modules, name points at the name[] member of struct module,
+ * which lives at least as long as this struct ddebug_table.
*/
- dt->mod_name = modname;
dt->info = *di;
INIT_LIST_HEAD(&dt->link);
@@ -1241,7 +1240,7 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
list_add_tail(&dt->link, &ddebug_tables);
mutex_unlock(&ddebug_lock);
- vpr_info("%3u debug prints in module %s\n", di->descs.len, modname);
+ vpr_info("%3u debug prints in module %s\n", di->descs.len, di->mod_name);
return 0;
}
@@ -1304,7 +1303,7 @@ static int ddebug_remove_module(const char *mod_name)
mutex_lock(&ddebug_lock);
list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) {
- if (dt->mod_name == mod_name) {
+ if (dt->info.mod_name == mod_name) {
ddebug_table_free(dt);
ret = 0;
break;
@@ -1324,7 +1323,8 @@ static int ddebug_module_notify(struct notifier_block *self, unsigned long val,
switch (val) {
case MODULE_STATE_COMING:
- ret = ddebug_add_module(&mod->dyndbg_info, mod->name);
+ mod->dyndbg_info.mod_name = mod->name;
+ ret = ddebug_add_module(&mod->dyndbg_info);
if (ret)
WARN(1, "Failed to allocate memory: dyndbg may not work properly.\n");
break;
@@ -1422,7 +1422,8 @@ static int __init dynamic_debug_init(void)
mod_ct++;
di.descs.len = mod_sites;
di.descs.start = iter_mod_start;
- ret = ddebug_add_module(&di, modname);
+ di.mod_name = modname;
+ ret = ddebug_add_module(&di);
if (ret)
goto out_err;
@@ -1433,7 +1434,8 @@ static int __init dynamic_debug_init(void)
}
di.descs.len = mod_sites;
di.descs.start = iter_mod_start;
- ret = ddebug_add_module(&di, modname);
+ di.mod_name = modname;
+ ret = ddebug_add_module(&di);
if (ret)
goto out_err;
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 27/29] dyndbg: hoist classmap-filter-by-modname up to ddebug_add_module
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (25 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 20:57 ` [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP Jim Cromie
2026-07-21 20:57 ` [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance Jim Cromie
28 siblings, 0 replies; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
The body of ddebug_attach_module_classes() is just a code-block that
finds the contiguous subrange of classmaps matching on modname, and
saves it into the ddebug_table's info record.
Implement this block in a macro to accommodate different component
vectors in the "box" (as named in the for_subvec macro). We will
reuse this macro shortly.
And hoist its invocation out of ddebug_attach_module_classes() up into
ddebug_add_module(). This moves the filtering step up closer to
dynamic_debug_init(), which already segments the builtin pr_debug
descriptors on their mod_name boundaries.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v3: expand block-comment in ddebug_add_module
v2: move RvB after SoB
finish hoist - drop old fn - ddebug_attach_module_classes
the v1 rev left the old ddebug_attach_module_classes in place, but it
is completely redundant now, since it already lost the list-linking
job it was doing.
It was being cut out later in the patchset (in the unsent API
adaptation phase), but for cleaner review, lets excise it now.
OLD all-in-1-series (pre split into reviewable chunks)
v10?- reordered params to match kdoc
v12- refactor/rename: s/dd_mark_vector_subrange/dd_set_module_subrange/
1. Renamed the macro from dd_mark_vector_subrange to
dd_set_module_subrange to better reflect its purpose of narrowing a
vector to a module-specific subrange.
2. Simplified the arguments by removing the redundant _dst, as the _di
pointer already provides access to the target _ddebug_info struct.
3. Refactored for Clarity: Instead of overwriting the struct's start
pointer while the for_subvec loop is using it to iterate, I
introduced a temporary __start variable. This avoids the "subtle"
side effect and makes the logic easier to follow.
4. Updated Documentation: Improved the comment block to explicitly
state that the macro scans for the first match and counts
contiguous elements.
fiuxp
---
lib/dynamic_debug.c | 80 ++++++++++++++++++++++++++++-------------------------
1 file changed, 43 insertions(+), 37 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 905277fa495b..b5bc2cc8c400 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1176,34 +1176,34 @@ static const struct proc_ops proc_fops = {
.proc_write = ddebug_proc_write
};
-static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug_info *di)
-{
- struct ddebug_class_map *cm;
- int i, nc = 0;
-
- /*
- * Find this module's classmaps in a subrange/wholerange of
- * the builtin/modular classmap vector/section. Save the start
- * and length of the subrange at its edges.
- */
- for_subvec(i, cm, di, maps) {
- if (!strcmp(cm->mod_name, dt->info.mod_name)) {
- if (!nc) {
- v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
- i, cm->mod_name, cm->base, cm->length, cm->map_type);
- dt->info.maps.start = cm;
- }
- nc++;
- } else if (nc) {
- /* end of matching classmaps */
- break;
- }
- }
- if (nc) {
- dt->info.maps.len = nc;
- vpr_info("module:%s attached %d classes\n", dt->info.mod_name, nc);
- }
-}
+/*
+ * dd_set_module_subrange - find matching subrange of classmaps
+ * @_i: caller-provided index var
+ * @_sp: cursor into @_vec
+ * @_di: pointer to the struct _ddebug_info to be narrowed
+ * @_vec: name of the vector member (must have .start and .len)
+ *
+ * Narrow a _ddebug_info's vector (@_vec) of classmaps to the
+ * contiguous subrange of elements where ->mod_name matches
+ * @__di->mod_name. This is primarily for builtins, loadable modules
+ * have only their classmaps, and dont need this sub-selection.
+ */
+#define dd_set_module_subrange(_i, _sp, _di, _vec) ({ \
+ struct _ddebug_info *__di = (_di); \
+ typeof(__di->_vec.start) __start = NULL; \
+ int __nc = 0; \
+ for_subvec(_i, _sp, __di, _vec) { \
+ if (!strcmp((_sp)->mod_name, __di->mod_name)) { \
+ if (!__nc++) \
+ __start = (_sp); \
+ } else if (__nc) { \
+ break; /* end of consecutive matches */ \
+ } \
+ } \
+ if (__nc) \
+ __di->_vec.start = __start; \
+ __di->_vec.len = __nc; \
+})
/*
* Allocate a new ddebug_table for the given module
@@ -1212,6 +1212,8 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
static int ddebug_add_module(struct _ddebug_info *di)
{
struct ddebug_table *dt;
+ struct ddebug_class_map *cm;
+ int i;
if (!di->descs.len)
return 0;
@@ -1224,17 +1226,21 @@ static int ddebug_add_module(struct _ddebug_info *di)
return -ENOMEM;
}
/*
- * For built-in modules, name (as supplied in di by its
- * callers) lives in .rodata and is immortal. For loaded
- * modules, name points at the name[] member of struct module,
- * which lives at least as long as this struct ddebug_table.
+ * For built-in modules, di is a partial cursor into the
+ * builtin dyndbg data; the descriptors are the subrange
+ * matching the modname, but the classmaps are the full set.
+ * We find and set the relevant subrange of classmaps here.
+ *
+ * The modname string is in .rodata, the descriptors and
+ * classmaps are in writable .data. All are immortal.
+ *
+ * For loaded modules, mod_name points at the name[] member
+ * of struct module, and the descriptors and classmaps point
+ * at the module's ELF sections; all have lifetimes matching
+ * the module's presence.
*/
dt->info = *di;
-
- INIT_LIST_HEAD(&dt->link);
-
- if (di->maps.len)
- ddebug_attach_module_classes(dt, di);
+ dd_set_module_subrange(i, cm, &dt->info, maps);
mutex_lock(&ddebug_lock);
list_add_tail(&dt->link, &ddebug_tables);
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (26 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 27/29] dyndbg: hoist classmap-filter-by-modname up to ddebug_add_module Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:18 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance Jim Cromie
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie, Louis Chauvet
commit aad0214f3026 ("dyndbg: add DECLARE_DYNDBG_CLASSMAP macro")
DECLARE_DYNDBG_CLASSMAP() has a design error; its usage fails a
basic K&R rule: "define once, refer many times".
When CONFIG_DRM_USE_DYNAMIC_DEBUG=y, it is used across DRM core &
drivers; each invocation allocates/inits the classmap understood by
that module. They *all* must match for the DRM modules to respond
consistently when drm.debug categories are enabled. This is at least
a maintenance hassle.
Worse, its the root cause of the CONFIG_DRM_USE_DYNAMIC_DEBUG=Y
regression; its use in both core & drivers obfuscates the 2 roles,
muddling the design, yielding an incomplete initialization when
modprobing drivers:
1st drm.ko loads, and dyndbg initializes its drm.debug callsites, then
a drm-driver loads, but too late for the drm.debug enablement.
And that led to:
commit bb2ff6c27bc9 ("drm: Disable dynamic debug as broken")
So retire it, replace with 2 macros:
DYNAMIC_DEBUG_CLASSMAP_DEFINE - invoked once from core - drm.ko
DYNAMIC_DEBUG_CLASSMAP_USE* - from all drm drivers and helpers.
NB: name-space de-noise
DYNAMIC_DEBUG_CLASSMAP_DEFINE: this reworks DECLARE_DYNDBG_CLASSMAP,
basically by dropping the static qualifier on the classmap, and
exporting it instead.
DYNAMIC_DEBUG_CLASSMAP_USE: then refers to the exported var by name:
used from drivers, helper-mods
lets us drop the repetitive "classname" declarations
fixes 2nd-defn problem
creates a ddebug_class_user record in new __dyndbg_class_users section
new section is scanned similarly to old
DECLARE_DYNDBG_CLASSMAP is preserved temporarily, to decouple DRM
adaptation work and avoid compile errs before its done.
The DEFINE,USE distinction, and the separate classmap-use record,
allows dyndbg to initialize the driver's & helper's drm.debug
callsites separately after each is modprobed. Basically, the classmap
initial scan is repeated for classmap-users.
Data Structure and Header Changes:
- Introduce 'struct ddebug_class_user' & __dyndbg_class_users section.
Contains the user-module-name and a named ref to the classmap export.
It records a drm-driver's use of the classmap in a new section,
allowing runtime lookup.
- add ptr to new section in 'struct ddebug_info'.
'class_users' and 'num_class_users'.
- These are initialized by
dynamic_debug_init() for built-ins, and by load_info() in
kernel/module/main.c for loadable modules.
- Update 'vmlinux.lds.h':
Add a new BOUNDED_SECTION for '__dyndbg_class_users' to define
__start and __stop C symbols for the section.
- Rename the '__dyndbg_classes' section to '__dyndbg_class_maps'.
Execution Engine Changes:
- ddebug_add_module():
Refactor and split ddebug_attach_module_classes() into
debug_apply_class_maps() and ddebug_apply_class_users(), both of
which call ddebug_apply_params().
- ddebug_apply_params():
Scans a module's or built-in's kernel-parameters, calling
ddebug_match_apply_kparam() for each to locate parameters wired
to a classmap.
- ddebug_match_apply_kparam():
Verifies that the kernel-parameter ops belong to dyndbg, ensuring
the target parameter is valid.
Fixes: aad0214f3026 ("dyndbg: add DECLARE_DYNDBG_CLASSMAP macro")
cc: linux-doc@vger.kernel.org
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
v5:
old, overwrought commit-msg:
dyndbg's existing __dyndbg_classes[] section does:
. catalogs the module's classmaps
. tells dyndbg about them, allowing >control
. DYNAMIC_DEBUG_CLASSMAP_DEFINE creates section records.
. we rename it to: __dyndbg_class_maps[]
this patch adds __dyndbg_class_users[] section:
. catalogs users of classmap definitions from elsewhere
. authorizes dyndbg to >control user's class'd prdbgs
. DYNAMIC_DEBUG_CLASSMAP_USE() creates section records.
Now ddebug_add_module(etal) can handle classmap-uses similar to (and
after) classmaps; when a dependent module is loaded, if it has
classmap-uses (to a classmap-def in another module), that module's
kernel params are scanned to find if it has a kparam that is wired to
dyndbg's param-ops, and whose classmap is the one being ref'd.
To support this, there are a few data/header changes:
new struct ddebug_class_user
contains: user-module-name, &classmap-defn
it records drm-driver's use of a classmap in the section, allowing lookup
struct ddebug_info gets 2 new fields for the new sections:
class_users, num_class_users.
set by dynamic_debug_init() for builtins.
or by kernel/module/main:load_info() for loadable modules.
vmlinux.lds.h: Add a new BOUNDED_SECTION for __dyndbg_class_users.
this creates start,stop C symbol-names for the section.
Callchain Details:
dynamic_debug.c: 2 changes from ddebug_add_module() & ddebug_change():
ddebug_add_module():
ddebug_attach_module_classes() is reworked/renamed/split into
debug_apply_class_maps(), ddebug_apply_class_users(), which both call
ddebug_apply_params().
ddebug_apply_params(new fn):
It scans module's/builtin kernel-params, calls ddebug_match_apply_kparam
for each to find any params/sysfs-nodes which may be wired to a classmap.
ddebug_match_apply_kparam(new fn):
1st, it tests the kernel-param.ops is dyndbg's; this guarantees that
the attached arg is a struct ddebug_class_param, which has a ref to
the param's state, and to the classmap defining the param's handling.
2nd, it requires that the classmap ref'd by the kparam is the one
we've been called for; modules can use many separate classmaps (as
test_dynamic_debug does).
Then apply the "parent" kparam's setting to the dependent module,
using ddebug_apply_class_bitmap().
ddebug_change(and callees) also gets adjustments:
ddebug_find_valid_class(): This does a search over the module's
classmaps, looking for the class FOO echo'd to >control. So now it
searches over __dyndbg_class_users[] after __dyndbg_classes[].
ddebug_class_name(): return class-names for defined OR used classes.
test_dynamic_debug.c, test_dynamic_debug_submod.c:
This demonstrates the 2 types of classmaps & sysfs-params, following
the 4-part recipe:
0. define an enum for the classmap's class_ids
drm.debug gives us DRM_UT_<*> (aka <T>)
multiple classmaps in a module(s) must share 0-62 classid space.
1. DYNAMIC_DEBUG_CLASSMAP_DEFINE(classmap_name, .. "<T>")
names the classes, maps them to consecutive class-ids.
convention here is stringified ENUM_SYMBOLS
these become API/ABI if 2 is done.
2. DYNAMIC_DEBUG_CLASSMAP_PARAM* (classmap_name)
adds a controlling kparam to the class
3. DYNAMIC_DEBUG_CLASSMAP_USE(classmap_name)
for subsystem/group/drivers to use extern created by 1.
Move all the enum declarations together, to better explain how they
share the 0..62 class-id space available to a module (non-overlapping
subranges).
reorg macros 2,3 by name. This gives a tabular format, making it easy
to see the pattern of repetition, and the points of change.
And extend the test to replicate the 2-module (parent & dependent)
scenario which caused the CONFIG_DRM_USE_DYNAMIC_DEBUG=y regression
seen in drm & drivers.
The _submod.c is a 2-line file: #define _SUBMOD, #include parent.
This gives identical complements of prdbgs in parent & _submod, and
thus identical print behavior when all of: >control, >params, and
parent->_submod propagation are working correctly.
It also puts all the parent/_submod declarations together in the same
source; the new ifdef _SUBMOD block invokes DYNAMIC_DEBUG_CLASSMAP_USE
for the 2 test-interfaces. I think this is clearer.
These 2 modules are both tristate, allowing 3 super/sub combos: Y/Y,
Y/M, M/M (not N/Y, since this is disallowed by dependence).
Y/Y, Y/M testing once exposed a missing __align(8) in the _METADATA
macro, which M/M didn't see, probably because the module-loader memory
placement constrained it from misalignment.
---
v2: RvB after SoB
old-v?
replace di with &dt->info, since di becomes stale
fix dd_mark_vector_subrange macro param ordering to match kdoc
s/base/offset/ in _ddebug_class_user, to reduce later churn
-v12 - squash in _USE_ and refinements.
A: dyndbg: add DYNAMIC_DEBUG_CLASSMAP_USE_(dd_class_name, offset)
Allow a module to use 2 classmaps together that would otherwise have a
class_id range conflict.
Suppose a drm-driver does:
DYNAMIC_DEBUG_CLASSMAP_USE(drm_debug_classes);
DYNAMIC_DEBUG_CLASSMAP_USE(drm_accel_xfer_debug);
If (for some reason) drm-accel cannot define their constants to avoid
DRM's drm_debug_category 0..10 reservations, we would have a conflict
with reserved-ids.
In this case a driver needing to use both would _USE_ one of them with
an offset to avoid the conflict. This will handle most forseeable
cases; perhaps a 3-X-3 of classmap-defns X classmap-users would get
too awkward and fiddly.
B: dyndbg: refine DYNAMIC_DEBUG_CLASSMAP_USE_ macro
The struct _ddebug_class_user _varname construct is needlessly
permissive; it has a static qualifier, and a unique name. Together,
these allow a module to have 2 or more _USE(foo)s, which is contrary
to its purpose, and therefore potentially confusing.
So drop the unique name, and the static qualifier, and replace it with
an extern pre-declaration. Construct the name by pasting together the
_var (which is the name of the exported ddebug_class_map), and
__KBUILD_MODNAME (which is the user module name). This allows only a
single USE() reference to the exported record, which is all that is
required.
---
include/asm-generic/dyndbg.lds.h | 6 +-
include/linux/dynamic_debug.h | 163 +++++++++++++++++++++++++++++++++++----
kernel/module/main.c | 3 +
lib/Kconfig.debug | 24 ++++--
lib/Makefile | 3 +
lib/dynamic_debug.c | 140 ++++++++++++++++++++++++++++++---
lib/test_dynamic_debug.c | 119 +++++++++++++++++++++-------
lib/test_dynamic_debug_submod.c | 14 ++++
8 files changed, 414 insertions(+), 58 deletions(-)
diff --git a/include/asm-generic/dyndbg.lds.h b/include/asm-generic/dyndbg.lds.h
index ec661f9f3793..0ffc9cde4377 100644
--- a/include/asm-generic/dyndbg.lds.h
+++ b/include/asm-generic/dyndbg.lds.h
@@ -5,7 +5,8 @@
#include <asm-generic/bounded_sections.lds.h>
#define DYNDBG_SECTIONS() \
BOUNDED_SECTION_BY(__dyndbg_descs, ___dyndbg_descs) \
- BOUNDED_SECTION_BY(__dyndbg_class_maps, ___dyndbg_class_maps)
+ BOUNDED_SECTION_BY(__dyndbg_class_maps, ___dyndbg_class_maps) \
+ BOUNDED_SECTION_BY(__dyndbg_class_users, ___dyndbg_class_users)
#define MOD_DYNDBG_SECTIONS() \
__dyndbg_descs 0 : ALIGN(8) { \
@@ -13,6 +14,9 @@
} \
__dyndbg_class_maps 0 : ALIGN(8) { \
KEEP(*(__dyndbg_class_maps)) \
+ } \
+ __dyndbg_class_users 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_class_users)) \
}
#endif /* __ASM_GENERIC_DYNDBG_LDS_H */
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 35e03b0eba6a..8c2055e4c7f8 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -86,19 +86,30 @@ enum ddebug_class_map_type {
*/
};
+/*
+ * map @class_names 0..N to consecutive constants starting at @base.
+ */
struct ddebug_class_map {
- struct module *mod; /* NULL for builtins */
- const char *mod_name; /* needed for builtins */
+ const struct module *mod; /* NULL for builtins */
+ const char *mod_name; /* needed for builtins */
const char **class_names;
const int length;
const int base; /* index of 1st .class_id, allows split/shared space */
enum ddebug_class_map_type map_type;
-};
+} __aligned(8);
+
+struct ddebug_class_user {
+ char *mod_name;
+ struct ddebug_class_map *map;
+ const int offset; /* offset from map->base */
+} __aligned(8);
/*
- * @_ddebug_info: gathers module/builtin dyndbg_* __sections together.
+ * @_ddebug_info: gathers module/builtin __dyndbg_<T> __sections
+ * together, each is a vec_<T>: a struct { struct T start[], int len }.
+ *
* For builtins, it is used as a cursor, with the inner structs
- * marking sub-vectors of the builtin __sections in DATA.
+ * marking sub-vectors of the builtin __sections in DATA_DATA
*/
struct _ddebug_descs {
struct _ddebug *start;
@@ -110,10 +121,16 @@ struct _ddebug_class_maps {
unsigned int len;
};
+struct _ddebug_class_users {
+ struct ddebug_class_user *start;
+ int len;
+};
+
struct _ddebug_info {
const char *mod_name;
struct _ddebug_descs descs;
struct _ddebug_class_maps maps;
+ struct _ddebug_class_users users;
};
struct ddebug_class_param {
@@ -132,17 +149,86 @@ struct ddebug_class_param {
#if defined(CONFIG_DYNAMIC_DEBUG) || \
(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
+/*
+ * dyndbg classmaps is modelled closely upon drm.debug:
+ *
+ * 1. run-time control via sysfs node (api/abi)
+ * 2. each bit 0..N controls a single "category"
+ * 3. a pr_debug can have only 1 category, not several.
+ * 4. "kind" is a compile-time constant: 0..N or BIT() thereof
+ * 5. macro impls - give compile-time resolution or fail.
+ *
+ * dyndbg classmaps design axioms/constraints:
+ *
+ * . optimizing compilers use 1-5 above, so preserve them.
+ * . classmaps.class_id *is* the category.
+ * . classmap definers/users are modules.
+ * . every user wants 0..N
+ * . 0..N exposes as ABI
+ * . no 1 use-case wants N > 32, 16 is more usable
+ * . N <= 64 in *all* cases
+ * . modules/subsystems make category/classmap decisions
+ * . ie an enum: DRM has DRM_UT_CORE..DRM_UT_DRMRES
+ * . some categories are exposed to user: ABI
+ * . making modules change their numbering is bogus, avoid if possible
+ *
+ * We can solve for all these at once:
+ * A: map class-names to a .class_id range at compile-time
+ * B: allow only "class NAME" changes to class'd callsites at run-time
+ * C: users/modules must manage 0..62 hardcoded .class_id range limit.
+ * D: existing pr_debugs get CLASS_DFLT=63
+ *
+ * By mapping class-names at >control to class-ids underneath, and
+ * responding only to class-names DEFINEd or USEd by the module, we
+ * can private-ize the class-id, and adjust class'd pr_debugs only by
+ * their names.
+ *
+ * This give us:
+ * E: class_ids without classnames are unreachable
+ * F: user modules opt-in by DEFINEing a classmap and/or USEing another
+ *
+ * Multi-classmap modules/groups are supported, if the classmaps share
+ * the class_id space [0..62] without overlap/conflict.
+ *
+ * NOTE: Due to the integer class_id, this api cannot disallow these:
+ * __pr_debug_cls(0, "fake CORE msg"); works only if a classmap maps 0.
+ * __pr_debug_cls(22, "no such class"); compiles but is not reachable
+ */
+
/**
- * DECLARE_DYNDBG_CLASSMAP - declare classnames known by a module
- * @_var: a struct ddebug_class_map, passed to module_param_cb
- * @_maptype: enum ddebug_class_map_type, chooses bits/verbose
- * @_base: offset of 1st class-name. splits .class_id space
- * @classes: class-names used to control class'd prdbgs
+ * DYNAMIC_DEBUG_CLASSMAP_DEFINE - define debug classes used by a module.
+ * @_var: name of the classmap, exported for other modules coordinated use.
+ * @_mapty: enum ddebug_class_map_type: 0:DISJOINT - independent, 1:LEVEL - v2>v1
+ * @_base: reserve N classids starting at _base, to split 0..62 classid space
+ * @classes: names of the N classes.
+ *
+ * This tells dyndbg what class_ids the module is using: _base..+N, by
+ * mapping names onto them. This qualifies "class NAME" >controls on
+ * the defining module, ignoring unknown names.
+ */
+#define DYNAMIC_DEBUG_CLASSMAP_DEFINE(_var, _mapty, _base, ...) \
+ static const char *_var##_classnames[] = { __VA_ARGS__ }; \
+ extern struct ddebug_class_map _var; \
+ struct ddebug_class_map __aligned(8) __used \
+ __section("__dyndbg_class_maps") _var = { \
+ .mod = THIS_MODULE, \
+ .mod_name = DDEBUG_MODNAME, \
+ .base = (_base), \
+ .map_type = (_mapty), \
+ .length = ARRAY_SIZE(_var##_classnames), \
+ .class_names = _var##_classnames, \
+ }; \
+ EXPORT_SYMBOL(_var)
+
+/*
+ * XXX: keep this until DRM adapts to use the DEFINE/USE api, it
+ * differs from DYNAMIC_DEBUG_CLASSMAP_DEFINE by the lack of the
+ * extern/EXPORT on the struct init, and cascading thinkos.
*/
#define DECLARE_DYNDBG_CLASSMAP(_var, _maptype, _base, ...) \
static const char *_var##_classnames[] = { __VA_ARGS__ }; \
static struct ddebug_class_map __aligned(8) __used \
- __section("__dyndbg_class_maps") _var = { \
+ __section("__dyndbg_class_maps") _var = { \
.mod = THIS_MODULE, \
.mod_name = DDEBUG_MODNAME, \
.base = _base, \
@@ -151,6 +237,44 @@ struct ddebug_class_param {
.length = __DDEBUG_ARRAY_SIZE(_var##_classnames), \
}
+/**
+ * DYNAMIC_DEBUG_CLASSMAP_USE - refer to a classmap, DEFINEd elsewhere.
+ * @_var: name of the exported classmap var
+ *
+ * This tells dyndbg that the module has prdbgs with classids defined
+ * in the named classmap. This qualifies "class NAME" >controls on
+ * the user module, and ignores unknown names. This is a wrapper for
+ * DYNAMIC_DEBUG_CLASSMAP_USE_() with a base offset of 0.
+ */
+#define DYNAMIC_DEBUG_CLASSMAP_USE(_var) \
+ DYNAMIC_DEBUG_CLASSMAP_USE_(_var, 0)
+
+/**
+ * DYNAMIC_DEBUG_CLASSMAP_USE_ - refer to a classmap with a manual offset.
+ * @_var: name of the exported classmap var to use.
+ * @_offset: an integer offset to add to the class IDs of the used map.
+ *
+ * This is an extended version of DYNAMIC_DEBUG_CLASSMAP_USE(). It should
+ * only be used to resolve class ID conflicts when a module uses multiple
+ * classmaps that have overlapping ID ranges.
+ *
+ * The final class IDs for the used map will be calculated as:
+ * original_map_base + class_index + @_offset.
+ */
+#define DYNAMIC_DEBUG_CLASSMAP_USE_(_var, _offset) \
+ extern struct ddebug_class_map _var; \
+ static_assert((_offset) >= 0 && (_offset) < _DPRINTK_CLASS_DFLT, \
+ "classmap use offset must be in 0..62"); \
+ extern struct ddebug_class_user __aligned(8) \
+ __PASTE(_var ## _, __KBUILD_MODNAME); \
+ struct ddebug_class_user __aligned(8) __used \
+ __section("__dyndbg_class_users") \
+ __PASTE(_var ## _, __KBUILD_MODNAME) = { \
+ .mod_name = DDEBUG_MODNAME, \
+ .map = &(_var), \
+ .offset = _offset \
+ }
+
extern __printf(2, 3)
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
@@ -312,12 +436,18 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
KERN_DEBUG, prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii)
-/* for test only, generally expect drm.debug style macro wrappers */
-#define __pr_debug_cls(cls, fmt, ...) do { \
+/*
+ * This is the "model" class variant of pr_debug. It is not really
+ * intended for direct use; I'd encourage DRM-style drm_dbg_<T>
+ * macros for the interface, along with an enum for the <T>
+ *
+ * __printf(2, 3) would apply.
+ */
+#define __pr_debug_cls(cls, fmt, ...) ({ \
BUILD_BUG_ON_MSG(!__builtin_constant_p(cls), \
"expecting constant class int/enum"); \
dynamic_pr_debug_cls(cls, fmt, ##__VA_ARGS__); \
- } while (0)
+})
#else /* !(CONFIG_DYNAMIC_DEBUG || (CONFIG_DYNAMIC_DEBUG_CORE && DYNAMIC_DEBUG_MODULE)) */
@@ -325,6 +455,8 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
#include <linux/errno.h>
#include <linux/printk.h>
+#define DYNAMIC_DEBUG_CLASSMAP_DEFINE(_var, _mapty, _base, ...)
+#define DYNAMIC_DEBUG_CLASSMAP_USE(_var)
#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt)
#define DYNAMIC_DEBUG_BRANCH(descriptor) false
#define DECLARE_DYNDBG_CLASSMAP(...)
@@ -371,8 +503,7 @@ static inline int param_set_dyndbg_classes(const char *instr, const struct kerne
static inline int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
{ return 0; }
-#endif
-
+#endif /* !CONFIG_DYNAMIC_DEBUG_CORE */
extern const struct kernel_param_ops param_ops_dyndbg_classes;
diff --git a/kernel/module/main.c b/kernel/module/main.c
index bd7899a91755..6414608b5c3c 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2780,6 +2780,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
mod->dyndbg_info.maps.start = section_objs(info, "__dyndbg_class_maps",
sizeof(*mod->dyndbg_info.maps.start),
&mod->dyndbg_info.maps.len);
+ mod->dyndbg_info.users.start = section_objs(info, "__dyndbg_class_users",
+ sizeof(*mod->dyndbg_info.users.start),
+ &mod->dyndbg_info.users.len);
#endif
return 0;
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1244dcac2294..1bcce12cd875 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3152,12 +3152,26 @@ config TEST_STATIC_KEYS
If unsure, say N.
config TEST_DYNAMIC_DEBUG
- tristate "Test DYNAMIC_DEBUG"
- depends on DYNAMIC_DEBUG
+ tristate "Build test-dynamic-debug module"
+ depends on DYNAMIC_DEBUG || DYNAMIC_DEBUG_CORE
help
- This module registers a tracer callback to count enabled
- pr_debugs in a 'do_debugging' function, then alters their
- enablements, calls the function, and compares counts.
+ This module exercises/demonstrates dyndbg's classmap API, by
+ creating 2 classes: a DISJOINT classmap (supporting DRM.debug)
+ and a LEVELS/VERBOSE classmap (like verbose2 > verbose1).
+
+ If unsure, say N.
+
+config TEST_DYNAMIC_DEBUG_SUBMOD
+ tristate "Build test-dynamic-debug submodule"
+ default m
+ depends on DYNAMIC_DEBUG || DYNAMIC_DEBUG_CORE
+ depends on TEST_DYNAMIC_DEBUG
+ help
+ This sub-module uses a classmap defined and exported by the
+ parent module, recapitulating drm & driver's shared use of
+ drm.debug to control enabled debug-categories.
+ It is tristate, independent of parent, to allow testing all
+ proper combinations of parent=y/m submod=y/m.
If unsure, say N.
diff --git a/lib/Makefile b/lib/Makefile
index 7f75cc6edf94..75d4c5e596e9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
+obj-$(CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD) += test_dynamic_debug_submod.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy)
@@ -206,6 +207,8 @@ obj-$(CONFIG_ARCH_NEED_CMPXCHG_1_EMU) += cmpxchg-emu.o
obj-$(CONFIG_DYNAMIC_DEBUG_CORE) += dynamic_debug.o
#ensure exported functions have prototypes
CFLAGS_dynamic_debug.o := -DDYNAMIC_DEBUG_MODULE
+CFLAGS_test_dynamic_debug.o := -DDYNAMIC_DEBUG_MODULE
+CFLAGS_test_dynamic_debug_submod.o := -DDYNAMIC_DEBUG_MODULE
obj-$(CONFIG_SYMBOLIC_ERRNAME) += errname.o
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index b5bc2cc8c400..c35d722fe850 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -29,6 +29,7 @@
#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <linux/dynamic_debug.h>
+
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/jump_label.h>
@@ -43,6 +44,8 @@ extern struct _ddebug __start___dyndbg_descs[];
extern struct _ddebug __stop___dyndbg_descs[];
extern struct ddebug_class_map __start___dyndbg_class_maps[];
extern struct ddebug_class_map __stop___dyndbg_class_maps[];
+extern struct ddebug_class_user __start___dyndbg_class_users[];
+extern struct ddebug_class_user __stop___dyndbg_class_users[];
struct ddebug_table {
struct list_head link;
@@ -160,20 +163,39 @@ static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
(_i) < (_box)->_vec.len; \
(_i)++, (_sp)++) /* { block } */
-static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
- const char *class_string,
+#define v2pr_di_info(di_p, msg_p, ...) \
+({ \
+ struct _ddebug_info const *_di = di_p; \
+ v2pr_info(msg_p "module:%s nd:%d nc:%d nu:%d\n", ##__VA_ARGS__, \
+ _di->mod_name, _di->descs.len, _di->maps.len, \
+ _di->users.len); \
+})
+
+static struct ddebug_class_map *ddebug_find_valid_class(struct _ddebug_info const *di,
+ const char *query_class,
int *class_id)
{
struct ddebug_class_map *map;
+ struct ddebug_class_user *cli;
int i, idx;
- for_subvec(i, map, &dt->info, maps) {
- idx = match_string(map->class_names, map->length, class_string);
+ for_subvec(i, map, di, maps) {
+ idx = match_string(map->class_names, map->length, query_class);
if (idx >= 0) {
+ v2pr_di_info(di, "good-class: %s.%s ", map->mod_name, query_class);
*class_id = idx + map->base;
return map;
}
}
+ for_subvec(i, cli, di, users) {
+ idx = match_string(cli->map->class_names, cli->map->length, query_class);
+ if (idx >= 0) {
+ v2pr_di_info(di, "class-ref: %s -> %s.%s ",
+ cli->mod_name, cli->map->mod_name, query_class);
+ *class_id = idx + cli->map->base - cli->offset;
+ return cli->map;
+ }
+ }
*class_id = -ENOENT;
return NULL;
}
@@ -234,8 +256,7 @@ static bool ddebug_match_desc(const struct ddebug_query *query,
return true;
}
-static int ddebug_change(const struct ddebug_query *query,
- struct flag_settings *modifiers)
+static int ddebug_change(const struct ddebug_query *query, struct flag_settings *modifiers)
{
int i;
struct ddebug_table *dt;
@@ -257,7 +278,8 @@ static int ddebug_change(const struct ddebug_query *query,
continue;
if (query->class_string) {
- map = ddebug_find_valid_class(dt, query->class_string, &valid_class);
+ map = ddebug_find_valid_class(&dt->info, query->class_string,
+ &valid_class);
if (!map)
continue;
} else {
@@ -590,7 +612,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)
/* handle multiple queries in query string, continue on error, return
last error or number of matching callsites. Module name is either
- in param (for boot arg) or perhaps in query string.
+ in the modname arg (for boot args) or perhaps in query string.
*/
static int ddebug_exec_queries(char *query, const char *modname)
{
@@ -738,7 +760,7 @@ static int param_set_dyndbg_module_classes(const char *instr,
/**
* param_set_dyndbg_classes - classmap kparam setter
* @instr: string echo>d to sysfs, input depends on map_type
- * @kp: kp->arg has state: bits/lvl, map, map_type
+ * @kp: kp->arg has state: bits/lvl, classmap, map_type
*
* enable/disable all class'd pr_debugs in the classmap. For LEVEL
* map-types, enforce * relative levels by bitpos.
@@ -775,6 +797,7 @@ int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
default:
return -1;
}
+ return 0;
}
EXPORT_SYMBOL(param_get_dyndbg_classes);
@@ -1090,12 +1113,17 @@ static bool ddebug_class_in_range(const int class_id, const struct ddebug_class_
static const char *ddebug_class_name(struct _ddebug_info *di, struct _ddebug *dp)
{
struct ddebug_class_map *map;
+ struct ddebug_class_user *cli;
int i;
for_subvec(i, map, di, maps)
if (ddebug_class_in_range(dp->class_id, map))
return map->class_names[dp->class_id - map->base];
+ for_subvec(i, cli, di, users)
+ if (ddebug_class_in_range(dp->class_id, cli->map))
+ return cli->map->class_names[dp->class_id - cli->map->base - cli->offset];
+
return NULL;
}
@@ -1176,6 +1204,87 @@ static const struct proc_ops proc_fops = {
.proc_write = ddebug_proc_write
};
+#define vpr_cm_info(cm_p, msg_fmt, ...) ({ \
+ struct ddebug_class_map const *_cm = cm_p; \
+ v2pr_info(msg_fmt "%s [%d..%d] %s..%s\n", ##__VA_ARGS__, \
+ _cm->mod_name, _cm->base, _cm->base + _cm->length, \
+ _cm->class_names[0], _cm->class_names[_cm->length - 1]); \
+ })
+
+static void ddebug_sync_classbits(const struct kernel_param *kp, const char *modname)
+{
+ const struct ddebug_class_param *dcp = kp->arg;
+
+ /* clamp initial bitvec, mask off hi-bits */
+ if (*dcp->bits & ~CLASSMAP_BITMASK(dcp->map->length)) {
+ *dcp->bits &= CLASSMAP_BITMASK(dcp->map->length);
+ v2pr_info("preset classbits: %x\n", *dcp->bits);
+ }
+ /* force class'd prdbgs (in USEr module) to match (DEFINEr module) class-param */
+ ddebug_apply_class_bitmap(dcp, dcp->bits, ~0, modname);
+ ddebug_apply_class_bitmap(dcp, dcp->bits, 0, modname);
+}
+
+static void ddebug_match_apply_kparam(const struct kernel_param *kp,
+ const struct ddebug_class_map *map,
+ const char *mod_name)
+{
+ struct ddebug_class_param *dcp;
+
+ if (kp->ops != ¶m_ops_dyndbg_classes)
+ return;
+
+ dcp = (struct ddebug_class_param *)kp->arg;
+
+ if (dcp) {
+ v2pr_info(" kp:%s.%s =0x%x", mod_name, kp->name, *dcp->bits);
+ vpr_cm_info(map, " %s maps ", mod_name);
+ ddebug_sync_classbits(kp, mod_name);
+ }
+}
+
+static void ddebug_apply_params(const struct ddebug_class_map *cm, const char *mod_name)
+{
+ const struct kernel_param *kp;
+#if IS_ENABLED(CONFIG_MODULES)
+ int i;
+
+ if (cm->mod) {
+ vpr_cm_info(cm, "loaded classmap: %s ", mod_name);
+ /* ifdef protects the cm->mod->kp deref */
+ for (i = 0, kp = cm->mod->kp; i < cm->mod->num_kp; i++, kp++)
+ ddebug_match_apply_kparam(kp, cm, mod_name);
+ }
+#endif
+ if (!cm->mod) {
+ vpr_cm_info(cm, "builtin classmap: %s ", mod_name);
+ for (kp = __start___param; kp < __stop___param; kp++)
+ ddebug_match_apply_kparam(kp, cm, mod_name);
+ }
+}
+
+static void ddebug_apply_class_maps(const struct _ddebug_info *di)
+{
+ struct ddebug_class_map *cm;
+ int i;
+
+ for_subvec(i, cm, di, maps)
+ ddebug_apply_params(cm, cm->mod_name);
+
+ v2pr_di_info(di, "attached %d class-maps to ", i);
+}
+
+static void ddebug_apply_class_users(const struct _ddebug_info *di)
+{
+ struct ddebug_class_user *cli;
+ int i;
+
+ for_subvec(i, cli, di, users)
+ ddebug_apply_params(cli->map, cli->mod_name);
+
+ v2pr_di_info(di, "attached %d class-users to ", i);
+}
+
/*
* dd_set_module_subrange - find matching subrange of classmaps
* @_i: caller-provided index var
@@ -1213,6 +1322,7 @@ static int ddebug_add_module(struct _ddebug_info *di)
{
struct ddebug_table *dt;
struct ddebug_class_map *cm;
+ struct ddebug_class_user *cli;
int i;
if (!di->descs.len)
@@ -1225,6 +1335,7 @@ static int ddebug_add_module(struct _ddebug_info *di)
pr_err("error adding module: %s\n", di->mod_name);
return -ENOMEM;
}
+ INIT_LIST_HEAD(&dt->link);
/*
* For built-in modules, di is a partial cursor into the
* builtin dyndbg data; the descriptors are the subrange
@@ -1241,12 +1352,19 @@ static int ddebug_add_module(struct _ddebug_info *di)
*/
dt->info = *di;
dd_set_module_subrange(i, cm, &dt->info, maps);
+ dd_set_module_subrange(i, cli, &dt->info, users);
+
+ if (dt->info.maps.len)
+ ddebug_apply_class_maps(&dt->info);
+ if (dt->info.users.len)
+ ddebug_apply_class_users(&dt->info);
mutex_lock(&ddebug_lock);
list_add_tail(&dt->link, &ddebug_tables);
mutex_unlock(&ddebug_lock);
- vpr_info("%3u debug prints in module %s\n", di->descs.len, di->mod_name);
+ vpr_info("%3u debug prints in module %s\n",
+ dt->info.descs.len, dt->info.mod_name);
return 0;
}
@@ -1396,8 +1514,10 @@ static int __init dynamic_debug_init(void)
struct _ddebug_info di = {
.descs.start = __start___dyndbg_descs,
.maps.start = __start___dyndbg_class_maps,
+ .users.start = __start___dyndbg_class_users,
.descs.len = __stop___dyndbg_descs - __start___dyndbg_descs,
.maps.len = __stop___dyndbg_class_maps - __start___dyndbg_class_maps,
+ .users.len = __stop___dyndbg_class_users - __start___dyndbg_class_users,
};
#ifdef CONFIG_MODULES
diff --git a/lib/test_dynamic_debug.c b/lib/test_dynamic_debug.c
index 9e8e028461ad..512bac3179ad 100644
--- a/lib/test_dynamic_debug.c
+++ b/lib/test_dynamic_debug.c
@@ -6,11 +6,30 @@
* Jim Cromie <jim.cromie@gmail.com>
*/
-#define pr_fmt(fmt) "test_dd: " fmt
+/*
+ * This file is built 2x, also making test_dynamic_debug_submod.ko,
+ * whose 2-line src file #includes this file. This gives us a _submod
+ * clone with identical pr_debugs, without further maintenance.
+ *
+ * If things are working properly, they should operate identically
+ * when printed or adjusted by >control. This eases visual perusal of
+ * the logs, and simplifies testing, by easing the proper accounting
+ * of expectations.
+ *
+ * It also puts both halves of the subsystem _DEFINE & _USE use case
+ * together, and integrates the common ENUM providing both class_ids
+ * and class-names to both _DEFINErs and _USERs. I think this makes
+ * the usage clearer.
+ */
+#if defined(TEST_DYNAMIC_DEBUG_SUBMOD)
+ #define pr_fmt(fmt) "test_dd_submod: " fmt
+#else
+ #define pr_fmt(fmt) "test_dd: " fmt
+#endif
#include <linux/module.h>
-/* run tests by reading or writing sysfs node: do_prints */
+/* re-gen output by reading or writing sysfs node: do_prints */
static void do_prints(void); /* device under test */
static int param_set_do_prints(const char *instr, const struct kernel_param *kp)
@@ -39,14 +58,36 @@ module_param_cb(do_prints, ¶m_ops_do_prints, NULL, 0600);
* Additionally, here:
* - tie together sysname, mapname, bitsname, flagsname
*/
-#define DD_SYS_WRAP(_model, _flags) \
- static u32 bits_##_model; \
- static struct ddebug_class_param _flags##_model = { \
+#define DYNAMIC_DEBUG_CLASSMAP_PARAM_(_model, _flags, _init) \
+ static u32 bits_##_model = _init; \
+ static struct ddebug_class_param _flags##_##_model = { \
.bits = &bits_##_model, \
.flags = #_flags, \
.map = &map_##_model, \
}; \
- module_param_cb(_flags##_##_model, ¶m_ops_dyndbg_classes, &_flags##_model, 0600)
+ module_param_cb(_flags##_##_model, ¶m_ops_dyndbg_classes, \
+ &_flags##_##_model, 0600)
+#ifdef DEBUG
+#define DYNAMIC_DEBUG_CLASSMAP_PARAM(_model, _flags) \
+ DYNAMIC_DEBUG_CLASSMAP_PARAM_(_model, _flags, ~0)
+#else
+#define DYNAMIC_DEBUG_CLASSMAP_PARAM(_model, _flags) \
+ DYNAMIC_DEBUG_CLASSMAP_PARAM_(_model, _flags, 0)
+#endif
+
+/*
+ * Demonstrate/test DISJOINT & LEVEL typed classmaps with a sys-param.
+ *
+ * To comport with DRM debug-category (an int), classmaps map names to
+ * ids (also an int). So a classmap starts with an enum; DRM has enum
+ * debug_category: with DRM_UT_<CORE,DRIVER,KMS,etc>. We use the enum
+ * values as class-ids, and stringified enum-symbols as classnames.
+ *
+ * Modules with multiple CLASSMAPS must have enums with distinct
+ * value-ranges, as arranged below with explicit enum_sym = X inits.
+ * To clarify this sharing, declare the 2 enums now, for the 2
+ * different classmap types
+ */
/* numeric input, independent bits */
enum cat_disjoint_bits {
@@ -60,26 +101,51 @@ enum cat_disjoint_bits {
D2_LEASE,
D2_DP,
D2_DRMRES };
-DECLARE_DYNDBG_CLASSMAP(map_disjoint_bits, DD_CLASS_TYPE_DISJOINT_BITS, 0,
- "D2_CORE",
- "D2_DRIVER",
- "D2_KMS",
- "D2_PRIME",
- "D2_ATOMIC",
- "D2_VBL",
- "D2_STATE",
- "D2_LEASE",
- "D2_DP",
- "D2_DRMRES");
-DD_SYS_WRAP(disjoint_bits, p);
-DD_SYS_WRAP(disjoint_bits, T);
-
-/* numeric verbosity, V2 > V1 related */
-enum cat_level_num { V0 = 14, V1, V2, V3, V4, V5, V6, V7 };
-DECLARE_DYNDBG_CLASSMAP(map_level_num, DD_CLASS_TYPE_LEVEL_NUM, 14,
- "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7");
-DD_SYS_WRAP(level_num, p);
-DD_SYS_WRAP(level_num, T);
+
+/* numeric verbosity, V2 > V1 related. V0 is > D2_DRMRES */
+enum cat_level_num { V0 = 16, V1, V2, V3, V4, V5, V6, V7 };
+
+/* recapitulate DRM's multi-classmap setup */
+#if !defined(TEST_DYNAMIC_DEBUG_SUBMOD)
+/*
+ * In single user, or parent / coordinator (drm.ko) modules, define
+ * classmaps on the client enums above, and then declares the PARAMS
+ * ref'g the classmaps. Each is exported.
+ */
+DYNAMIC_DEBUG_CLASSMAP_DEFINE(map_disjoint_bits, DD_CLASS_TYPE_DISJOINT_BITS,
+ D2_CORE,
+ "D2_CORE",
+ "D2_DRIVER",
+ "D2_KMS",
+ "D2_PRIME",
+ "D2_ATOMIC",
+ "D2_VBL",
+ "D2_STATE",
+ "D2_LEASE",
+ "D2_DP",
+ "D2_DRMRES");
+
+DYNAMIC_DEBUG_CLASSMAP_DEFINE(map_level_num, DD_CLASS_TYPE_LEVEL_NUM,
+ V0, "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7");
+
+/*
+ * now add the sysfs-params
+ */
+
+DYNAMIC_DEBUG_CLASSMAP_PARAM(disjoint_bits, p);
+DYNAMIC_DEBUG_CLASSMAP_PARAM(level_num, p);
+
+#else /* TEST_DYNAMIC_DEBUG_SUBMOD */
+
+/*
+ * in submod/drm-drivers, use the classmaps defined in top/parent
+ * module above.
+ */
+
+DYNAMIC_DEBUG_CLASSMAP_USE(map_disjoint_bits);
+DYNAMIC_DEBUG_CLASSMAP_USE(map_level_num);
+
+#endif
/* stand-in for all pr_debug etc */
#define prdbg(SYM) __pr_debug_cls(SYM, #SYM " msg\n")
@@ -115,6 +181,7 @@ static void do_levels(void)
static void do_prints(void)
{
+ pr_debug("do_prints:\n");
do_cats();
do_levels();
}
diff --git a/lib/test_dynamic_debug_submod.c b/lib/test_dynamic_debug_submod.c
new file mode 100644
index 000000000000..672aabf40160
--- /dev/null
+++ b/lib/test_dynamic_debug_submod.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kernel module for testing dynamic_debug
+ *
+ * Authors:
+ * Jim Cromie <jim.cromie@gmail.com>
+ */
+
+/*
+ * clone the parent, inherit all the properties, for consistency and
+ * simpler accounting in test expectations.
+ */
+#define TEST_DYNAMIC_DEBUG_SUBMOD
+#include "test_dynamic_debug.c"
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
` (27 preceding siblings ...)
2026-07-21 20:57 ` [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP Jim Cromie
@ 2026-07-21 20:57 ` Jim Cromie
2026-07-21 21:11 ` sashiko-bot
28 siblings, 1 reply; 38+ messages in thread
From: Jim Cromie @ 2026-07-21 20:57 UTC (permalink / raw)
To: Andrew Morton, Jason Baron, Greg Kroah-Hartman, Shuah Khan,
Simona Vetter, Luis Chamberlain, Petr Pavlu
Cc: linux-kernel, linux-doc, dri-devel, linux-arch, linux-modules,
linux-kselftest, Jim Cromie
re-enable FT_classmap_inheritance, and comment out writes to missing
test-mod params. tbd where they are.
fix-some-tests-sysl test-tweaks
selftests/dyndbg: sync all results checksums
happened on gandalf, at end of progress_from master+5 in this tree.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
.../selftests/dynamic_debug/dyndbg_selftest.sh | 88 +++++++++++++++++-----
1 file changed, 71 insertions(+), 17 deletions(-)
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 947f23dcb4ce..49c2a79929c6 100755
--- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -181,7 +181,7 @@ function slice_and_hash_ddctrl {
}
function verify_modprobe_param_logging {
- # $1 - parameter name (e.g. do_classes)
+ # $1 - parameter name (e.g. do_prints)
# $2 - parameter value (e.g. 1)
# $3 - short descriptive tag (e.g. classes)
local param="$1"
@@ -199,12 +199,12 @@ function verify_modprobe_param_logging {
log_start
modprobe test_dynamic_debug "${param}=${val}"
- # If it is a state-controlling parameter, trigger the print-workload 'do_classes=1'
+ # If it is a state-controlling parameter, trigger the print-workload 'do_prints=1'
# inside the same syslog dmesg capture bookends to verify their actual pr_debug logging!
if [ "$param" = "p_disjoint_bits" ] || [ "$param" = "p_level_num" ]; then
LOCAL_DDCMDS="${LOCAL_DDCMDS}; echo 1 > "
- LOCAL_DDCMDS="${LOCAL_DDCMDS}/sys/module/test_dynamic_debug/parameters/do_classes"
- echo 1 > /sys/module/test_dynamic_debug/parameters/do_classes
+ LOCAL_DDCMDS="${LOCAL_DDCMDS}/sys/module/test_dynamic_debug/parameters/do_prints"
+ echo 1 > /sys/module/test_dynamic_debug/parameters/do_prints
fi
log_stop
@@ -217,8 +217,10 @@ function verify_modprobe_param_logging {
# Verify that the resulting control-file state is completely cleared
local hash_unset=$(slice_and_hash_ddctrl '\[test_dynamic_debug\]')
- local default_hash="fd89900c8614f23c3a6e8a8d45aa3280"
+
# Pristine fully-disabled slice hash
+ local default_hash="fd89900c8614f23c3a6e8a8d45aa3280"
+ default_hash="d8d188e03eb33d970453c06b8f3f25a4"
if [ "$hash_unset" != "$default_hash" ]; then
echo -e "${RED}: Runtime unsetting failed for ${param}! " \
@@ -249,7 +251,7 @@ function FT_grammar_ok {
# use 4 keywords (max 9 words inc flags)
ddcmd "module foo file bar.c func buz class D2_CORE +_" # 4 keywords
- #ddcmd "module foo file bar.c func buz class D2 line 100 +_" # 5 keywords
+ ddcmd "module foo file bar.c func buz class D2 line 100 +_" # 5 keywords
# 3. Dedicated lineno range grammar assertions (side-effect-free proofs)
ddcmd "line 42 +_" # test exact line syntax
@@ -493,9 +495,9 @@ function FT_test_classes {
verify_file_slice '\[test_dynamic_debug\]'
# 2. Verify state transition and live-printing end-to-end via ddcmd_load!
- ddcmd_load "class,D2_CORE,+pmf@class,D2_KMS,+pls@class,D2_ATOMIC,+pml" \
+ ddcmd_load "class,D2_CORE,+pmf;class,D2_KMS,+pls;class,D2_ATOMIC,+pml" \
'\[test_dynamic_debug\]' \
- "/sys/module/test_dynamic_debug/parameters/do_classes" "1"
+ "/sys/module/test_dynamic_debug/parameters/do_prints" "1"
ifrmmod test_dynamic_debug
}
@@ -508,8 +510,8 @@ function FT_classmap_inheritance {
ddcmd =_
# modprobe with class enablements
- modprobe test_dynamic_debug \
- dyndbg=class,D2_CORE,+pf@class,D2_KMS,+pt@class,D2_ATOMIC,+pm
+ #modprobe test_dynamic_debug \
+ #dyndbg=class,D2_CORE,+pf@class,D2_KMS,+pt@class,D2_ATOMIC,+pm
verify_file_slice '\[test_dynamic_debug\]'
@@ -517,8 +519,8 @@ function FT_classmap_inheritance {
verify_file_slice '\[test_dynamic_debug_submod\]'
# Verify 3 class changes
- ddcmd "class,D2_CORE,+pmf @ class,D2_KMS,+plt @ class,D2_ATOMIC,+pml" \
- 'test_dynamic_debug' "# add some prefixes"
+ #ddcmd "class,D2_CORE,+pmf @ class,D2_KMS,+plt @ class,D2_ATOMIC,+pml" \
+ #'test_dynamic_debug' "# add some prefixes"
# now work the classmap-params
# fresh start, to clear all above flags (test-fn limits)
@@ -572,8 +574,8 @@ function FT_classmap_inheritance {
# --- Live Content Fingerprinting Phase ---
log_start
- echo 1 > /sys/module/test_dynamic_debug/parameters/do_classes
- echo 1 > /sys/module/test_dynamic_debug_submod/parameters/do_classes
+ echo 1 > /sys/module/test_dynamic_debug/parameters/do_prints
+ echo 1 > /sys/module/test_dynamic_debug_submod/parameters/do_prints
log_stop
ifrmmod test_dynamic_debug_submod
@@ -622,8 +624,8 @@ builtin_tests=(
# Modular Feature Tests (Require CONFIG_MODULES=y and test_dynamic_debug*.ko available)
modular_tests=(
#FT_test_classes
- #FT_classmap_inheritance
- #FT_modprobe_w_param
+ FT_classmap_inheritance
+ FT_modprobe_w_param
)
# ==============================================================================
@@ -640,7 +642,6 @@ modular_tests=(
# ==============================================================================
function GOLDEN_RECORDS {
cat << 'EOF' | {
-# --- Unregistered Baselines ---
#K= f3dbd5afb9aa1750f93275b634499e22 FT_grammar_errs.1 dmesg
#K= 200c01632c52a63f6d186da1c6460740 FT_grammar_errs.2 dmesg
#K= 7d7141900ce6e32f15c99202309c63a4 FT_grammar_errs.3 dmesg
@@ -689,6 +690,59 @@ function GOLDEN_RECORDS {
#K= da6bd1c6a299290150668186f8263b82 FT_basic_queries.4 "kernel/params.c"
#K= 82572e8d20c4b567afac783006d1a935 FT_basic_queries.5 "kernel/params.c"
#K= baea1247680e8151c121539f4b90a6d8 FT_basic_queries.6 "kernel/params.c"
+#K= 68b329da9893e34099c7d8ad5cb9c940 FT_classmap_inheritance.1 "\[test_dynamic_debug\]"
+#K= 7f9bbb8e3cbaeb819c9db4ad314fe91f FT_classmap_inheritance.2 "\[test_dynamic_debug_submod\]"
+#K= e595e746a30eac106ad0041b78b9bb04 FT_classmap_inheritance.3 "test_dynamic_debug"
+#K= 781cd443ba8e9655318c53e32bf7650c FT_classmap_inheritance.4 "test_dynamic_debug"
+#K= 710b9cc6ba3a9db08025fd8f0cd01058 FT_classmap_inheritance.5 "test_dynamic_debug"
+#K= 6e78ea40996f4bb355764a023822c89d FT_classmap_inheritance.6 "\[test_dynamic_debug\]"
+#K= 8efc97ee6ad63ae1b34c3898550fbdeb FT_classmap_inheritance.7 "test_dynamic_debug"
+#K= 5d93156ac2b46d188c3cab04a0287b68 FT_classmap_inheritance.8 dmesg
+#K= ec88354e5d563adfb7816b09588ce22a FT_modprobe_w_param.1 dmesg
+#K= c05be478400d61b0ec7dd3659e1063bb FT_modprobe_w_param.2 dmesg
+#K= 681053af96e96530f309a755a86bb86e FT_modprobe_w_param.3 dmesg
+#K= 0567348e035ea0be726ea88e41cbc7f9 FT_modprobe_w_param.4 dmesg
+#K= e682773b51040743f1881b85ed375ddb FT_modprobe_w_param.5 dmesg
+#K= 05d1e9c5f416dfaeab7842945e4af7e1 FT_modprobe_w_param.6 dmesg
+#K= de9880bb8fa627f4f5599d13ad382105 FT_modprobe_w_param.7 dmesg
+#K= 57fb0642f3d9c33fc1d58ca728481ffb FT_modprobe_w_param.8 dmesg
+#K= 7be7034fec3a8e0f757720d00b026669 FT_modprobe_w_param.9 dmesg
+#K= 925d4de5a038cac3356176036717d572 FT_modprobe_w_param.10 dmesg
+#K= f81c7e22d1dd97608a8ceaca5a0ac677 FT_modprobe_w_param.11 dmesg
+#K= f573825da4a1128466530d869ce6def1 FT_modprobe_w_param.12 dmesg
+#K= 9a9d67addbc1732aa9e4e7747261208c FT_modprobe_w_param.13 dmesg
+#K= 7fba79ed343651fc81f302aec98b0f5a FT_modprobe_w_param.14 dmesg
+#K= b3f7deb9df2a80205ec2f475aad44c3d FT_modprobe_w_param.15 dmesg
+#K= 7c1d3772035558d575ca866d4d972e87 FT_modprobe_w_param.16 dmesg
+#K= 48fe52f49c4927cdf79e937762f50e4f FT_modprobe_w_param.17 dmesg
+#K= be389ee9ddfe5308e4350090ddd2d16a FT_modprobe_w_param.18 dmesg
+#K= 1524a2631d44a1abc2eca683908c9e94 FT_modprobe_w_param.19 dmesg
+#K= e7c39d0b9b293d0147a2f6fcd0f164b3 FT_modprobe_w_param.20 dmesg
+#K= bdae90265d318a2cb148060130257cbf FT_modprobe_w_param.21 dmesg
+#K= 4759b6d7cb14acaf0cc33811728f77cf FT_modprobe_w_param.22 dmesg
+#K= 22659835c1f7a94f82cd8ec7e1796d7f FT_modprobe_w_param.23 dmesg
+#K= 81bf838321eb584b5adc7edb321560d3 FT_modprobe_w_param.24 dmesg
+#K= 9353df3b283410f886a638b6996118e5 FT_modprobe_w_param.25 dmesg
+#K= 28125d16b047544025754071f00d5465 FT_modprobe_w_param.26 dmesg
+#K= 2ad304b6e6ce3a63e1a24644c4c21f8c FT_modprobe_w_param.27 dmesg
+#K= 1524a2631d44a1abc2eca683908c9e94 FT_modprobe_w_param.28 dmesg
+#K= 9e3a99d008531d5a02344730398be088 FT_modprobe_w_param.29 dmesg
+#K= 4c13f861a474d1931587d893e6038c95 FT_modprobe_w_param.30 dmesg
+#K= dadffb7601c368815dcf756fb65fdabf FT_modprobe_w_param.31 dmesg
+#K= 22659835c1f7a94f82cd8ec7e1796d7f FT_modprobe_w_param.32 dmesg
+#K= 4d9c8b506f9b21306cc3f26bb0754c70 FT_modprobe_w_param.33 dmesg
+#K= 2c0dd2e7e1b515d612c2060252e76834 FT_modprobe_w_param.34 dmesg
+#K= 9d0780573256ca9257239a386d4dc6bb FT_modprobe_w_param.35 dmesg
+#K= 2ad304b6e6ce3a63e1a24644c4c21f8c FT_modprobe_w_param.36 dmesg
+#K= 68b329da9893e34099c7d8ad5cb9c940 FT_modprobe_w_param.37 dmesg
+#K= 06585a61fecd49f826b624a493f8186a FT_modprobe_w_param.38 dmesg
+#K= 9a4d3232a7d9263158dbf59b4685b32c FT_modprobe_w_param.39 dmesg
+#K= 6abbd7b226a87b21aebab6f037ece451 FT_modprobe_w_param.40 dmesg
+#K= 68b329da9893e34099c7d8ad5cb9c940 FT_modprobe_w_param.41 dmesg
+#K= 14cf091423c69a188ba45cc39414c1b9 FT_modprobe_w_param.42 dmesg
+#K= 713b6e5c4af67d59a1896c8181a7e1b5 FT_modprobe_w_param.43 dmesg
+#K= 67fa99b23bfafccc66b023970b71cbd8 FT_modprobe_w_param.44 dmesg
+#K= 68b329da9893e34099c7d8ad5cb9c940 FT_modprobe_w_param.45 dmesg
EOF
# Read the K-recs and skip those for tests that can't run
while read -r line; do
--
2.55.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change
2026-07-21 20:57 ` [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change Jim Cromie
@ 2026-07-21 21:05 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:05 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly added NULL format check uses an un-ratelimited, context-free `pr_info()` inside a globally locked inner loop.
--
--- Patch [11]: [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change ---
commit 47cab4604933a3e3a136a1d101a11ae69fe36598
Author: Jim Cromie <jim.cromie@gmail.com>
Subject: dyndbg: factor ddebug_match_desc out from ddebug_change
This commit factors out the per-descriptor filtering logic from the inner loop
of ddebug_change() into a separate boolean function, ddebug_match_desc(). It
also introduces a check to prevent operating on debug descriptors with a NULL
format string.
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 18a71a9108d3e..577a079160727 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -172,6 +172,56 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
> * callsites, normally the same as number of changes. If verbose,
> * logs the changes. Takes ddebug_lock.
> */
> +static bool ddebug_match_desc(const struct ddebug_query *query,
> + struct _ddebug *dp,
> + int valid_class)
> +{
[ ... ]
> + /* match against the format */
> + if (query->format) {
> + if (!dp->format) {
> + pr_info("encountered a NULL format\n");
[Severity: Medium]
Could this un-ratelimited pr_info() spam the kernel log?
Since ddebug_match_desc() is executed inside a nested loop holding
ddebug_lock, if there is a malformed descriptor in the system, querying the
format via debugfs could trigger this message repeatedly on every format
query.
Additionally, because the message lacks context, it doesn't provide clues
to help identify which callsite is missing the format string. Would it be
better to include details like dp->filename, dp->function, or dp->lineno
here, and perhaps use pr_info_once() or pr_warn_ratelimited() to avoid
log flooding?
> + return false;
> + }
> + if (*query->format == '^') {
> + char *p;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=11
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug
2026-07-21 20:56 ` [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug Jim Cromie
@ 2026-07-21 21:06 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:06 UTC (permalink / raw)
To: Jim Cromie; +Cc: linux-modules, dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] The script uses predictable temporary file names in `/tmp` based on the process ID, introducing a Time-of-Check to Time-of-Use (TOCTOU) symlink vulnerability.
- [Medium] The golden record MD5 hashing of dynamic debug control diffs includes source code line numbers, strictly coupling the test to file topology and causing spurious failures on unrelated code changes.
- [Medium] The framework uses exact MD5 hashes of dmesg slices, leading to severe flakiness when concurrent kernel activity occurs.
- [Medium] The script unconditionally skips all tests on monolithic kernels due to an overly broad initial `lsmod` check.
- [Medium] The dmesg log parsing does not account for `CONFIG_PRINTK_CALLER=y`, causing MD5 hashes to change on every test run due to PID inclusion.
--
--- Patch [2]: [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug ---
commit acdd3977962a0d4f508ca6d6378e26344aaef61d
Author: Jim Cromie <jim.cromie@gmail.com>
selftests/dyndbg: Add kselftest script to verify dynamic-debug
Add a kselftest script to attempt full validation of dynamic-debug
behavior. The script tests query grammar as documented, responses to
bad input, and proper/expected effects on both the control-file
display of current state, and on pr_debug logging behavior.
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 0000000000000..1dafeaabc9768
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -0,0 +1,799 @@
[ ... ]
> [ -e /proc/dynamic_debug/control ] || {
> echo -e "${RED}: this test requires CONFIG_DYNAMIC_DEBUG=y ${NC}"
> exit $ksft_skip # nothing to test here, no good reason to fail.
> }
>
> lsmod >/dev/null 2>&1 || {
> echo -e "${RED}: lsmod requires /proc/modules ${NC}"
> exit $ksft_skip # maybe later we can do more
> }
[Severity: Medium]
Does this early exit prevent the script from running the builtin tests
on a monolithic kernel?
Since the builtin_tests are explicitly designed to validate dynamic_debug
behavior on built-in code without requiring module support, it seems like
skipping the entire script here disables valid testing on kernels compiled
without CONFIG_MODULES=y.
[ ... ]
diff --git a/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh b/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh
index 0000000000000..c6d60495d7baa
--- /dev/null
+++ b/tools/testing/selftests/dynamic_debug/syslog_hash_validation.sh
@@ -0,0 +1,384 @@
[ ... ]
> # Default APP to DYNDBG if not already set
> APP="${APP:-DYNDBG}"
> APP_LOWER=$(echo "$APP" | tr '[:upper:]' '[:lower:]')
>
> # Global files for tracking seen, unregistered, and drifted hashes
> SEEN_HASHES_FILE="/tmp/${APP_LOWER}_seen_hashes_$$"
> UNREG_HASHES_FILE="/tmp/${APP_LOWER}_unreg_hashes_$$"
> DRIFT_HASHES_FILE="/tmp/${APP_LOWER}_drift_hashes_$$"
[Severity: High]
Are these predictable file paths in the world-writable /tmp directory
vulnerable to a symlink attack?
If a malicious local user creates a symlink matching this PID pattern
right before the script writes to it, could it allow the script to
overwrite sensitive files, since it blindly appends to these paths later
on?
Using mktemp might be a safer approach here to prevent Time-of-Check
to Time-of-Use exploits.
[ ... ]
> function verify_dmesg_slice {
> # Slices dmesg, computes its hash, and verifies it against the database.
> # $1 - unique test key (e.g. normal_513)
> # $2 - optional start marker (defaults to ${APP}_START_${label})
> # $3 - optional end marker (defaults to ${APP}_END_${label})
>
> local label="$1"
> local app="${APP:-DYNDBG}"
> local start_marker="${2:-${app}_START_${label}_$$}"
> local end_marker="${3:-${app}_END_${label}_$$}"
> local extra_args="dmesg"
>
> # 1. Capture the log slice (exactly once!)
> local log_slice=$(dmesg | sed -n "/$start_marker/,/$end_marker/p" | \
> grep -E -v "$start_marker|$end_marker" | \
> sed -e 's/^\[[^]]*\] //' )
[Severity: Medium]
Does this regex correctly handle kernels configured with
CONFIG_PRINTK_CALLER=y?
When printk caller info is enabled, a second bracket group containing the
thread ID is present in the dmesg output. Since the PID changes per run,
will the resulting MD5 hash constantly mismatch because the second bracket
was not stripped before hashing?
[Severity: Medium]
Can background kernel activity disrupt the MD5 hashes calculated from
this slice?
If unrelated subsystem prints (like networking, RCU, or USB) occur between
the log_start and log_stop markers, they will be captured here and alter
the exact hash. Should there be a way to filter the dmesg slice to only
include dyndbg-related lines to prevent test flakiness in noisy
environments?
[ ... ]
> function verify_after_change {
> # Verifies the transition between the stored 'before' state and the current state
> # $1 - optional unique test key (resolved via stack if empty)
[ ... ]
> # 1. Capture the 'after' state (exactly once!)
> local after_slice=$(slice_by_grep "$BEFORE_CAPTURE_PATTERN" "$BEFORE_CAPTURE_FILE")
>
> # 2. Generate the unified diff, stripped of volatile diff headers AND hunk line-numbers
> local transition_diff=$(diff -u <(echo "$BEFORE_CAPTURE_SLICE") <(echo "$after_slice") | \
> tail -n +3 | \
> sed -E 's/^@@ -[0-9]+.* \+[0-9]+.* @@/@@/g')
>
> # 3. Compute its fingerprint
> local fingerprint=$(echo "$transition_diff" | tr -d '\r' | md5sum | cut -d' ' -f1)
[Severity: Medium]
Does the generated transition_diff still include source code line numbers
from the control file?
The sed command strips the chunk headers, but the body text from
/proc/dynamic_debug/control inherently outputs the format filename:lineno.
If unrelated upstream commits add or remove lines in tested files like
kernel/params.c, won't this cause the tests to spuriously fail because
the hashed line numbers drifted?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=2
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info
2026-07-21 20:57 ` [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info Jim Cromie
@ 2026-07-21 21:09 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:09 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Built-in modules without their own dynamic debug class maps incorrectly inherit the entire array of built-in class maps, causing cross-contamination of debug class settings.
--
--- Patch [25]: [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info ---
commit 9690152a047a31662e977dac229f6c3d6cb68cd2
Author: Jim Cromie <jim.cromie@gmail.com>
dyndbg,module: make proper substructs in _ddebug_info
This commit recomposes struct _ddebug_info by inserting proper sub-structs
for descriptors and class maps, and updates the dynamic debug core to use
these new structures.
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 60b67face46e1..e7742fcca50ba 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -1185,12 +1186,12 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
> * the builtin/modular classmap vector/section. Save the start
> * and length of the subrange at its edges.
> */
> - for_subvec(i, cm, di, classes) {
> + for_subvec(i, cm, di, maps) {
> if (!strcmp(cm->mod_name, dt->mod_name)) {
> if (!nc) {
> v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
> i, cm->mod_name, cm->base, cm->length, cm->map_type);
> - dt->classes = cm;
> + dt->info.maps.start = cm;
> }
> nc++;
> } else if (nc) {
> @@ -1199,7 +1200,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
> }
> }
> if (nc) {
> - dt->num_classes = nc;
> + dt->info.maps.len = nc;
> vpr_info("module:%s attached %d classes\n", dt->mod_name, nc);
> }
> }
[Severity: Medium]
When a module has no classes and nc remains 0, does dt->info.maps retain
the values originally copied from di?
Since dt->info = *di is performed before this function is called, this means
the module will incorrectly inherit the entire global array of built-in
class maps. Could this cause cross-contamination of debug class settings
between unrelated modules?
[ ... ]
> @@ -1212,10 +1213,10 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
> {
> struct ddebug_table *dt;
>
> - if (!di->num_descs)
> + if (!di->descs.len)
> return 0;
>
> - v3pr_info("add-module: %s %d sites\n", modname, di->num_descs);
> + v3pr_info("add-module: %s %d sites\n", modname, di->descs.len);
>
> dt = kzalloc_obj(*dt);
> if (dt == NULL) {
> @@ -1229,19 +1230,18 @@ static int ddebug_add_module(struct _ddebug_info *di, const char *modname)
> * this struct ddebug_table.
> */
> dt->mod_name = modname;
> - dt->ddebugs = di->descs;
> - dt->num_ddebugs = di->num_descs;
> + dt->info = *di;
>
> INIT_LIST_HEAD(&dt->link);
>
> - if (di->classes && di->num_classes)
> + if (di->maps.len)
> ddebug_attach_module_classes(dt, di);
[Severity: Medium]
If di->maps.len > 0 (which is true for built-ins since di spans all global
maps), but the module itself has no classes, ddebug_attach_module_classes()
won't reset dt->info.maps.len to 0.
Would it be better to clear dt->info.maps.len and dt->info.maps.start here
before calling ddebug_attach_module_classes() to prevent the module from
accidentally keeping the global state?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=25
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 24/29] dyndbg: pin class param storage to u32
2026-07-21 20:57 ` [PATCH v7 24/29] dyndbg: pin class param storage to u32 Jim Cromie
@ 2026-07-21 21:10 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:10 UTC (permalink / raw)
To: Jim Cromie; +Cc: linux-modules, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Bitwise shift out-of-bounds on 32-bit architectures when evaluating `BIT(bi)` in `ddebug_apply_class_bitmap()`.
--
--- Patch [24]: [PATCH v7 24/29] dyndbg: pin class param storage to u32 ---
commit 87a65c25e24b4e05bdc18ddf16105e9237d922db
Author: Jim Cromie <jim.cromie@gmail.com>
dyndbg: pin class param storage to u32
This commit changes the bits and lvl fields in struct ddebug_class_param
to use u32 instead of unsigned long. This ensures consistent sizing across
different architectures.
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 59e3c0eccbede..60b67face46e1 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -643,24 +643,27 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
> int bi, ct;
>
> if (*new_bits != old_bits)
> - v2pr_info("apply bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
> + v2pr_info("apply bitmap: 0x%x to: 0x%x for %s\n", *new_bits,
> old_bits, query_modname ?: "'*'");
>
> for (bi = 0; bi < map->length; bi++) {
> - if (test_bit(bi, new_bits) == test_bit(bi, &old_bits))
> + bool new_b = !!(*new_bits & BIT(bi));
> + bool old_b = !!(old_bits & BIT(bi));
[Severity: Medium]
Can this result in an out-of-bounds bitwise shift on 32-bit architectures?
The BIT(bi) macro expands to (1UL << bi). On 32-bit systems, 1UL is 32
bits wide. If a module defines a classmap with 32 or more classes
(map->length >= 32), shifting by 32 or more triggers undefined behavior.
On most 32-bit architectures, the CPU masks the shift count modulo 32, meaning
BIT(32) evaluates to BIT(0). This causes the loop to incorrectly re-evaluate
and alter class 0 instead of processing the intended class, leading to
incorrect functional behavior when configuring dynamic debug.
> + if (new_b == old_b)
> continue;
>
> snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=24
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance
2026-07-21 20:57 ` [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance Jim Cromie
@ 2026-07-21 21:11 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:11 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The test `FT_classmap_inheritance` is broken by commenting out the entire `modprobe test_dynamic_debug` command.
- [Low] Redundant and confusing double assignment to `default_hash`.
--
--- Patch [29]: [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance ---
commit 368562dd09d0a9983482e5b9ecbe159f50ea9ede
Author: Jim Cromie <jim.cromie@gmail.com>
selftests/dyndbg: enable FT_classmap_inheritance
This commit re-enables FT_classmap_inheritance in the dynamic debug
selftests. It also updates parameter names and syncs result checksums
to match a recent test run.
> diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> index 947f23dcb4ce7..49c2a79929c6f 100755
> --- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> +++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
[ ... ]
> @@ -217,8 +217,10 @@ function verify_modprobe_param_logging {
>
> # Verify that the resulting control-file state is completely cleared
> local hash_unset=$(slice_and_hash_ddctrl '\[test_dynamic_debug\]')
> - local default_hash="fd89900c8614f23c3a6e8a8d45aa3280"
> +
> # Pristine fully-disabled slice hash
> + local default_hash="fd89900c8614f23c3a6e8a8d45aa3280"
> + default_hash="d8d188e03eb33d970453c06b8f3f25a4"
[Severity: Low]
Is this double assignment to default_hash intentional? The first value is
immediately overwritten, leaving redundant dead code behind in
verify_modprobe_param_logging.
[ ... ]
> @@ -508,8 +510,8 @@ function FT_classmap_inheritance {
> ddcmd =_
>
> # modprobe with class enablements
> - modprobe test_dynamic_debug \
> - dyndbg=class,D2_CORE,+pf@class,D2_KMS,+pt@class,D2_ATOMIC,+pm
> + #modprobe test_dynamic_debug \
> + #dyndbg=class,D2_CORE,+pf@class,D2_KMS,+pt@class,D2_ATOMIC,+pm
>
> verify_file_slice '\[test_dynamic_debug\]'
[Severity: Medium]
Does commenting out the entire modprobe command break the test? Because the
module is no longer loaded, verify_file_slice in FT_classmap_inheritance
will check an empty state.
[ ... ]
> @@ -689,6 +690,59 @@ function GOLDEN_RECORDS {
> #K= da6bd1c6a299290150668186f8263b82 FT_basic_queries.4 "kernel/params.c"
> #K= 82572e8d20c4b567afac783006d1a935 FT_basic_queries.5 "kernel/params.c"
> #K= baea1247680e8151c121539f4b90a6d8 FT_basic_queries.6 "kernel/params.c"
> +#K= 68b329da9893e34099c7d8ad5cb9c940 FT_classmap_inheritance.1 "\[test_dynamic_debug\]"
[Severity: Medium]
Is it expected that this golden record in GOLDEN_RECORDS matches the MD5 hash
of an empty string (68b329da9893e34099c7d8ad5cb9c940)? This appears to
cement a broken test that asserts the module is absent rather than verifying
classmap inheritance.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=29
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below
2026-07-21 20:57 ` [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below Jim Cromie
@ 2026-07-21 21:11 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:11 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The refactored `param_set_dyndbg_module_classes` is structurally incapable of correctly applying existing global classmap settings to newly loaded modules, and it corrupts the global state.
--
--- Patch [20]: [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below ---
commit fd7e6888285afde6b911cef67e7e2452e7180dba
Author: Jim Cromie <jim.cromie@gmail.com>
dyndbg: refactor param_set_dyndbg_classes and below
This commit refactors the param_set_dyndbg_classes callchain to support
module-specific classmap updates, primarily intended for modprobe handling.
It splits the function into upper and lower parts, threading a modname
parameter through to ddebug_exec_queries. It also adds a check in
ddebug_apply_class_bitmap to skip printing if bits haven't changed.
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index f66e5373e61e2..a619cefd801f7 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -625,7 +626,9 @@ static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,
> int matches = 0;
> int bi, ct;
>
> - v2pr_info("apply: 0x%lx to: 0x%lx\n", *new_bits, *old_bits);
> + if (*new_bits != *old_bits)
> + v2pr_info("apply bitmap: 0x%lx to: 0x%lx for %s\n", *new_bits,
> + *old_bits, query_modname ?: "'*'");
>
> for (bi = 0; bi < map->length; bi++) {
> if (test_bit(bi, new_bits) == test_bit(bi, old_bits))
> continue;
[Severity: Medium]
When applying the current global state to a newly probed module, will this
continue statement incorrectly skip all configuration queries?
Since old_bits represents the global state, test_bit() will evaluate to true
when applying the same global configuration to a new module, silently
skipping required query updates.
> snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
> test_bit(bi, new_bits) ? '+' : '-', dcp->flags);
>
[ ... ]
> @@ -683,8 +693,8 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
> KP_NAME(kp), inrep, CLASSMAP_BITMASK(map->length));
> inrep &= CLASSMAP_BITMASK(map->length);
> }
> - v2pr_info("bits:%lx > %s\n", inrep, KP_NAME(kp));
> - totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits);
> + v2pr_info("bits:0x%lx > %s.%s\n", inrep, mod_name ?: "*", KP_NAME(kp));
> + totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits, mod_name);
> *dcp->bits = inrep;
[Severity: Medium]
Does this code inadvertently corrupt the global state if a module-specific
override is applied?
It appears that *dcp->bits (the global parameter state) is unconditionally
overwritten with inrep, even when a mod_name is provided.
Should the state update be conditional on mod_name being NULL, to prevent
modprobe handling from modifying the global state?
> break;
> case DD_CLASS_TYPE_LEVEL_NUM:
> @@ -697,7 +707,7 @@ int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
> old_bits = CLASSMAP_BITMASK(*dcp->lvl);
> new_bits = CLASSMAP_BITMASK(inrep);
> v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));
> - totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits);
> + totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits, mod_name);
> *dcp->lvl = inrep;
[Severity: Medium]
Similarly, is the global *dcp->lvl state unconditionally overwritten here
when a module-specific override is applied for LEVEL_NUM map types?
> break;
> default:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=20
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info
2026-07-21 20:57 ` [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info Jim Cromie
@ 2026-07-21 21:13 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:13 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] When processing a built-in module that has no class maps, `ddebug_attach_module_classes()` fails to reset `dt->info.maps.len` to 0, causing the module to erroneously inherit the global array of all built-in class maps.
--
--- Patch [26]: [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info ---
commit bd18115a5b87d80ee2f5c15c166c46a301cbd184
Author: Jim Cromie <jim.cromie@gmail.com>
dyndbg: move mod_name down from struct ddebug_table to _ddebug_info
struct _ddebug_info already has most of dyndbg's info for a module;
push debug_table.mod_name down into it, finishing the encapsulation.
This commit moves the module name from struct ddebug_table into
struct _ddebug_info. This encapsulates the module information, allowing
several callchains to pass _ddebug_info directly rather than the full table.
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index e7742fcca50ba..905277fa495b4 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -1187,7 +1187,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
> * and length of the subrange at its edges.
> */
> for_subvec(i, cm, di, maps) {
> - if (!strcmp(cm->mod_name, dt->mod_name)) {
> + if (!strcmp(cm->mod_name, dt->info.mod_name)) {
> if (!nc) {
> v2pr_info("start subrange, class[%d]: module:%s base:%d len:%d ty:%d\n",
> i, cm->mod_name, cm->base, cm->length, cm->map_type);
> @@ -1201,7 +1201,7 @@ static void ddebug_attach_module_classes(struct ddebug_table *dt, struct _ddebug
> }
> if (nc) {
> dt->info.maps.len = nc;
> - vpr_info("module:%s attached %d classes\n", dt->mod_name, nc);
> + vpr_info("module:%s attached %d classes\n", dt->info.mod_name, nc);
> }
> }
[Severity: Medium]
When processing a built-in module that has no class maps, does
ddebug_attach_module_classes() fail to reset dt->info.maps.len to 0?
During boot, dynamic_debug_init() creates a stack variable di where maps.start
and maps.len cover the entire __dyndbg_class_maps section. For each built-in
module, ddebug_add_module(&di) is called. It performs a shallow copy
dt->info = *di, assigning the full length.
It then calls ddebug_attach_module_classes(). If the module has no class maps,
the loop finds no matches (nc == 0), skipping the if (nc) block:
if (nc) {
dt->info.maps.len = nc;
vpr_info("module:%s attached %d classes\n", dt->info.mod_name, nc);
}
Because of this, does dt->info.maps.len improperly retain the length of all
built-in class maps, causing the module to erroneously inherit the global array
of all built-in class maps?
This logic error could allow users to apply unrelated class strings to a
module's debug prints via the control interface, leading to incorrect functional
behavior of the dynamic debug API.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=26
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP
2026-07-21 20:57 ` [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP Jim Cromie
@ 2026-07-21 21:18 ` sashiko-bot
0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-07-21 21:18 UTC (permalink / raw)
To: Jim Cromie; +Cc: dri-devel, linux-modules
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] ddebug_sync_classbits unconditionally treats LEVEL_NUM parameters as bitmasks, corrupting stored verbosity levels and incorrectly configuring callsites.
- [High] Incorrect arithmetic for used classmaps with offsets completely breaks class control and leads to out-of-bounds array reads.
- [High] Module classmap initialization silently fails because configuration queries are executed before the module is published to the global tracking list.
- [Medium] ddebug_match_apply_kparam fails to verify that the kernel parameter's class map matches the target map, applying all loaded dyndbg parameters indiscriminately.
- [Medium] Unprotected read-modify-write on shared classmap bits causes a data race during concurrent module loads or sysfs writes.
--
--- Patch [28]: [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP ---
commit bd4f2adcad3c31e1b42bccb83f503513dbc0eb2e
Author: Jim Cromie <jim.cromie@gmail.com>
Subject: dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP
This patch replaces DECLARE_DYNDBG_CLASSMAP with two new macros:
DYNAMIC_DEBUG_CLASSMAP_DEFINE and DYNAMIC_DEBUG_CLASSMAP_USE. It splits the
classmap declarations into definitions (exported by core) and uses (from
drivers) to solve initialization ordering issues and avoid duplicate classmap
allocations.
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
[ ... ]
> @@ -160,20 +163,39 @@ static void v3pr_info_dq(const struct ddebug_query *query, const char *msg)
[ ... ]
> static struct ddebug_class_map *ddebug_find_valid_class(struct _ddebug_info const *di,
> const char *query_class,
> int *class_id)
> {
[ ... ]
> + for_subvec(i, cli, di, users) {
> + idx = match_string(cli->map->class_names, cli->map->length, query_class);
> + if (idx >= 0) {
> + v2pr_di_info(di, "class-ref: %s -> %s.%s ",
> + cli->mod_name, cli->map->mod_name, query_class);
> + *class_id = idx + cli->map->base - cli->offset;
[Severity: High]
Should this subtract the offset? When a module uses a classmap with a non-zero
offset, subtracting it here instead of adding it appears to yield the wrong
class ID, breaking sysfs control ID mapping.
> + return cli->map;
> + }
> + }
> + *class_id = -ENOENT;
> + return NULL;
> }
[ ... ]
> @@ -1090,12 +1113,17 @@ static bool ddebug_class_in_range(const int class_id, const struct ddebug_class_
> static const char *ddebug_class_name(struct _ddebug_info *di, struct _ddebug *dp)
> {
> struct ddebug_class_map *map;
> + struct ddebug_class_user *cli;
> int i;
>
> for_subvec(i, map, di, maps)
> if (ddebug_class_in_range(dp->class_id, map))
> return map->class_names[dp->class_id - map->base];
>
> + for_subvec(i, cli, di, users)
> + if (ddebug_class_in_range(dp->class_id, cli->map))
> + return cli->map->class_names[dp->class_id - cli->map->base - cli->offset];
[Severity: High]
Does this compute a negative array index? If an unrelated callsite falls in
the unadjusted range checked by ddebug_class_in_range(), subtracting both
base and offset from dp->class_id could result in a negative index. This
could lead to an out-of-bounds read on cli->map->class_names.
> +
> return NULL;
> }
[ ... ]
> @@ -1176,6 +1204,87 @@ static const struct proc_ops proc_fops = {
[ ... ]
> +static void ddebug_sync_classbits(const struct kernel_param *kp, const char *modname)
> +{
> + const struct ddebug_class_param *dcp = kp->arg;
> +
> + /* clamp initial bitvec, mask off hi-bits */
> + if (*dcp->bits & ~CLASSMAP_BITMASK(dcp->map->length)) {
> + *dcp->bits &= CLASSMAP_BITMASK(dcp->map->length);
[Severity: High]
Does this logic corrupt LEVEL_NUM classmaps? By unconditionally treating the
union field *dcp->bits as a bitmask, this overwrites the integer level stored
in *dcp->lvl for DD_CLASS_TYPE_LEVEL_NUM. Treating an integer level as a
bitmask seems to incorrectly enable disjoint classes and corrupt the stored
verbosity level.
[Severity: Medium]
Could this unprotected read-modify-write on *dcp->bits cause a data race?
Since this can execute during concurrent module loads or sysfs writes without
ddebug_lock, it looks like concurrent updates to the shared parent classmap
bitmask could be lost.
> + v2pr_info("preset classbits: %x\n", *dcp->bits);
> + }
> + /* force class'd prdbgs (in USEr module) to match (DEFINEr module) class-param */
> + ddebug_apply_class_bitmap(dcp, dcp->bits, ~0, modname);
> + ddebug_apply_class_bitmap(dcp, dcp->bits, 0, modname);
> +}
> +
> +static void ddebug_match_apply_kparam(const struct kernel_param *kp,
> + const struct ddebug_class_map *map,
> + const char *mod_name)
> +{
> + struct ddebug_class_param *dcp;
> +
> + if (kp->ops != ¶m_ops_dyndbg_classes)
> + return;
> +
> + dcp = (struct ddebug_class_param *)kp->arg;
> +
> + if (dcp) {
> + v2pr_info(" kp:%s.%s =0x%x", mod_name, kp->name, *dcp->bits);
> + vpr_cm_info(map, " %s maps ", mod_name);
> + ddebug_sync_classbits(kp, mod_name);
[Severity: Medium]
Is a check comparing dcp->map to map missing here? Because this verifies
kp->ops but omits verifying that the kernel parameter's class map matches the
target map, it seems all dyndbg parameters of the defining module will be
applied to every classmap indiscriminately.
> + }
> +}
[ ... ]
> @@ -1213,6 +1322,7 @@ static int ddebug_add_module(struct _ddebug_info *di)
[ ... ]
> + if (dt->info.maps.len)
> + ddebug_apply_class_maps(&dt->info);
> + if (dt->info.users.len)
> + ddebug_apply_class_users(&dt->info);
>
> mutex_lock(&ddebug_lock);
> list_add_tail(&dt->link, &ddebug_tables);
> mutex_unlock(&ddebug_lock);
[Severity: High]
Will module classmap initialization fail here? The initialization functions
ddebug_apply_class_maps() and ddebug_apply_class_users() execute queries
against the global ddebug_tables list. Since list_add_tail() is called
after these functions return, it appears ddebug_change() will fail to find
the newly loaded module, silently dropping the initial configuration.
>
> - vpr_info("%3u debug prints in module %s\n", di->descs.len, di->mod_name);
> + vpr_info("%3u debug prints in module %s\n",
> + dt->info.descs.len, dt->info.mod_name);
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-dd-maint-2-v7-0-010fbe73b311@gmail.com?part=28
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2026-07-21 21:18 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 20:56 [PATCH v7 00/29] fix dynamic-debug classmaps API for DRM Jim Cromie
2026-07-21 20:56 ` [PATCH v7 01/29] params: fix a pr_debug(" %p ") use - already in MM-* Jim Cromie
2026-07-21 20:56 ` [PATCH v7 02/29] selftests/dyndbg: Add kselftest script to verify dynamic-debug Jim Cromie
2026-07-21 21:06 ` sashiko-bot
2026-07-21 20:56 ` [PATCH v7 03/29] drm: Fix incorrect ccflags-y spelling inside Makefile Jim Cromie
2026-07-21 20:56 ` [PATCH v7 04/29] drm: fix config dependent unused variable warning Jim Cromie
2026-07-21 20:56 ` [PATCH v7 05/29] drm: Mark CONFIG_DRM_USE_DYNAMIC_DEBUG as unBROKEN Jim Cromie
2026-07-21 20:56 ` [PATCH v7 06/29] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h Jim Cromie
2026-07-21 20:56 ` [PATCH v7 07/29] vmlinux.lds.h: drop unused HEADERED_SECTION* macros Jim Cromie
2026-07-21 20:56 ` [PATCH v7 08/29] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386 Jim Cromie
2026-07-21 20:56 ` [PATCH v7 09/29] vmlinux.lds.h: remove redundant ALIGN(8) directives Jim Cromie
2026-07-21 20:56 ` [PATCH v7 10/29] dyndbg.lds.S: fix lost dyndbg sections in modules Jim Cromie
2026-07-21 20:57 ` [PATCH v7 11/29] dyndbg: factor ddebug_match_desc out from ddebug_change Jim Cromie
2026-07-21 21:05 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 12/29] dyndbg: add stub macro for DECLARE_DYNDBG_CLASSMAP Jim Cromie
2026-07-21 20:57 ` [PATCH v7 13/29] dyndbg: reword "class unknown," to "class:_UNKNOWN_" Jim Cromie
2026-07-21 20:57 ` [PATCH v7 14/29] dyndbg-API: remove DD_CLASS_TYPE_(DISJOINT|LEVEL)_NAMES and code Jim Cromie
2026-07-21 20:57 ` [PATCH v7 15/29] dyndbg: drop NUM_TYPE_ARGS Jim Cromie
2026-07-21 20:57 ` [PATCH v7 16/29] dyndbg: bump num-tokens in a query-cmd from 9 to 15 Jim Cromie
2026-07-21 20:57 ` [PATCH v7 17/29] dyndbg: reduce verbose/debug clutter Jim Cromie
2026-07-21 20:57 ` [PATCH v7 18/29] lib/parser: add match_wildcard_hyphen() for agnostic matching Jim Cromie
2026-07-21 20:57 ` [PATCH v7 19/29] dyndbg: use KBUILD_MODFILE for unique builtin module names Jim Cromie
2026-07-21 20:57 ` [PATCH v7 20/29] dyndbg: refactor param_set_dyndbg_classes and below Jim Cromie
2026-07-21 21:11 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 21/29] dyndbg: tighten fn-sig of ddebug_apply_class_bitmap Jim Cromie
2026-07-21 20:57 ` [PATCH v7 22/29] dyndbg: replace classmap list with an array-slice Jim Cromie
2026-07-21 20:57 ` [PATCH v7 23/29] dyndbg: macrofy a 2-index for-loop pattern Jim Cromie
2026-07-21 20:57 ` [PATCH v7 24/29] dyndbg: pin class param storage to u32 Jim Cromie
2026-07-21 21:10 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 25/29] dyndbg,module: make proper substructs in _ddebug_info Jim Cromie
2026-07-21 21:09 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 26/29] dyndbg: move mod_name down from struct ddebug_table to _ddebug_info Jim Cromie
2026-07-21 21:13 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 27/29] dyndbg: hoist classmap-filter-by-modname up to ddebug_add_module Jim Cromie
2026-07-21 20:57 ` [PATCH v7 28/29] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP Jim Cromie
2026-07-21 21:18 ` sashiko-bot
2026-07-21 20:57 ` [PATCH v7 29/29] selftests/dyndbg: enable FT_classmap_inheritance Jim Cromie
2026-07-21 21:11 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox