From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oa2-f35.google.com (mail-oa2-f35.google.com [74.125.231.99]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E91E58FD16 for ; Wed, 23 Sep 2026 22:35:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.99 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790202940; cv=none; b=TyegzhemwLLtxRiH7LS8zXUulx3y9NbELMqrHnZ64VwYrvgXCgWhltcYMUkpF0ARJBQC8ZI4vKaBw8q/KbZ4iQ/XP0oMXunznBxN7mhvbjUp0nHhxsArWikYOyUE6ywzB4Xb3v/Ns+zAc5bwmkwRpPeLra3oTAkJgFInFZGowxw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790202940; c=relaxed/simple; bh=Ikw0Nqk8U8ALwPbK9IYcWzZ0a+T/onn2NTtp2P/2iBg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=a/hEQmoNH2OZfAx3r5dsVKiseE1UaZSUWmzJ0W4qj2NPXQvN+MX9oEasi0STkNBY6ORIoGYiBcu5nkfKU7uGbVzudsdc/CMhyRTBZMyB43W/O91lfXTzsBJ4v/R9Er9ThLU3wjvf4/yAQIHXtlza1ohvms9HKYUl3eJvjL8au9w= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UM3F/Uq2; arc=none smtp.client-ip=74.125.231.99 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UM3F/Uq2" Received: by mail-oa2-f35.google.com with SMTP id 586e51a60fabf-486e0f56cddso780759fac.3 for ; Wed, 23 Sep 2026 15:35:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790202932; x=1790807732; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Nnjn/UI+xCZOo1KkIcYJIK0iFnau4hyLmT4Bm2wXAQE=; b=UM3F/Uq2ZmViN0xLjiMINQyh76e/GewActg/e3tnPtP+0KYNRjKNkEm+dCXCjMhPcv helgn9f9NwfYVvbTkXWLkH8txKwTqNTbvn9gQcn4DbMEYGyFSJdZK4HYhraD+/zQZiL/ mkSLmnISvsHlrfNR2kdcxqPiAZu/a1VA12MgZ0U3cGh+DLq289aouAQh3rA+no9z629X w10ximJ6Na7BMETzuGbNyRRj8konNa0+s3F5nPsaPNF+IiicxmNM3WfHIU/OO48CuKil I8yViL1CBOe2qWOJEVwO8+89S7CXRAymyAm8ByLmxkgCOP0ZDYPPlJF1/jfGZakZrjbB GY2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790202932; x=1790807732; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Nnjn/UI+xCZOo1KkIcYJIK0iFnau4hyLmT4Bm2wXAQE=; b=sdaswNZPZz17fguk6jGQkDdM9RO1lEVCQDJ6Un8NgQ5ZX/h0o+4Hu6W0YwziK4dADf tDphhkVKn7RrD4xmZM35Td7765u2/JGNHDx1l51+vU9PA9A3B32KXibQ78kaps/ia23n Sus0A642fvfLmXEXjSwTv+gO5domRfb8vNn1eDjGAe8yqYlFHwJXGVKjiNrDVkYwSnGo 0xKd3gxoIcov9FiAzyx3WmbYZIjbgom66Fz8SDidmgv7Mbup8BU65yzUvLWFKkWCsGL9 v5SRn4ipUJ/JLb8PS3LqMZST9zmNTFtjwimsmMDPEgNO/8oHjY8ncgnAHD8FaqVPhrGj Ln4w== X-Gm-Message-State: AFuF++kv6d5X7JWX497fHYE1Hg8dW9uTwvPkXs9MfFrJE6X2vJMgPMFD /Vje+BiiBbRgAL8x6204VCVsCePm5K5HgFWnudktfBFO7dNCt6IO4edH X-Gm-Gg: AYBFou0uhWHa9NPQSmcusbPdT5OOZ8zlPqEEZeTzd+xBTR+0TtT+XQIcS7mPEsBvvzV aNizcxsL3HobQz2qC5nInKZxa+tCsds6pnUm1/GdatkjNdnsMXC87zmqkIsJVd3BwbxF4505/xF VAabNr/ErfuRzQPtHLaLIwSKcKA/uEU3PJb03iAU5DSjDxex1q2ABCJ8nsakOkYbxBp9W1PQbRe 77MSLlQme5CfKbAXRNM8VYrT1+BKnm8ElS+UbtaAU0oUzDBYT1J5JpgcF/uRNRYL0E2vAzHXfE6 bea4j7/0ou0GwjI4N+KP+7p3J3qWLrenygrj4RLm6I+O6udw+6XUSVFkR79MEBHYjhHXN4McwTF iW3Saky+VW+PJJcI1S2JYmYO7uSmDOB5tdp2gIPgdWlSpL/WHShpR+s6MdgKLDgrve7eM0Ls6V6 GZDnbOh38O6rq/WiDY4HjoVigtD2vsOGdmp6EZEvqcsyTDdAN9q2PtqFpdiefu1SHdQTHguogQM z4ecOPOYxaoEDnw4NfdDBsAaDU9Ip7jwvPFYZXYNXKpS/Qo598CnWkdP6ondn8HAhkGeCVEyfk8 Oqtiz8xP33Ol5lsSKFA= X-Received: by 2002:a05:6870:e196:b0:483:5ee4:1b2e with SMTP id 586e51a60fabf-491e52913c8mr718244fac.6.1790202931608; Wed, 23 Sep 2026 15:35:31 -0700 (PDT) Received: from [100.82.231.29] (c-98-38-17-99.hsd1.co.comcast.net. [98.38.17.99]) by smtp.googlemail.com with ESMTPSA id 586e51a60fabf-491ee1ac486sm559401fac.16.2026.09.23.15.35.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 15:35:30 -0700 (PDT) From: Jim Cromie Date: Wed, 23 Sep 2026 16:34:47 -0600 Subject: [PATCH v11 23/38] dyndbg-API: replace DECLARE_DYNDBG_CLASSMAP Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260923-dd-cmap-part2-clean-v11-23-9b6c217fdf2f@gmail.com> References: <20260923-dd-cmap-part2-clean-v11-0-9b6c217fdf2f@gmail.com> In-Reply-To: <20260923-dd-cmap-part2-clean-v11-0-9b6c217fdf2f@gmail.com> To: Jason Baron , Shuah Khan , Andrew Morton , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Arnd Bergmann , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , Aaron Tomlin , Jonathan Corbet , Greg Kroah-Hartman , Nathan Chancellor , Nicolas Schier , Shuah Khan , Randy Dunlap , "Rafael J. Wysocki" , Pavel Machek , Len Brown , Jonathan Corbet , Petr Mladek , Steven Rostedt , John Ogness , Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-arch@vger.kernel.org, linux-modules@vger.kernel.org, linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-pm@vger.kernel.org, Jim Cromie , Louis Chauvet X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1790202874; l=33516; i=jim.cromie@gmail.com; s=20260203; h=from:subject:message-id; bh=Ikw0Nqk8U8ALwPbK9IYcWzZ0a+T/onn2NTtp2P/2iBg=; b=ILNy024cH7u4igJN65eboHRPvs6mgX10YdvU4YMDcR6iSeIZmG5GoWvCJsrg8LJ54PEvSTZhB Ibr+sVrkNVUD5PqFVH+EeECjyTaNlxZwuQXzL9VzW0I0uOIEKP0mZi2 X-Developer-Key: i=jim.cromie@gmail.com; a=ed25519; pk=C6E5ODlPQo7ZBynATXH9wg7K6HxP0pIXyf4s38Qw0XE= DECLARE_DYNDBG_CLASSMAP had a flaw: multiple invocations across both core and driver modules broke the K&R "define once, refer afterwards" principle, causing boot-time parameter propagation failures. Replace it with an explicit client-server model: - DYNAMIC_DEBUG_CLASSMAP_DEFINE: invoked once in the core module, creating and exporting the classmap. - DYNAMIC_DEBUG_CLASSMAP_USE: invoked across driver/helper modules, placing a reference record into the __dyndbg_class_users section. At module load time, dyndbg scans __dyndbg_class_users, resolves the reference to the parent classmap, finds the associated controlling kernel parameter, and automatically applies the initial bitmap to the loading module. Fixes: aad0214f3026 ("dyndbg: add DECLARE_DYNDBG_CLASSMAP macro") Cc: linux-doc@vger.kernel.org Reviewed-by: Louis Chauvet Signed-off-by: Jim Cromie --- v11: . introduce ddebug_apply_class_maps() alongside ddebug_apply_class_users() in ddebug_add_module() for complete lifecycle symmetry between definers and users at module load time. . drop init_bits and misleading _DPRINTK_FLAGS_DEFAULT check in ddebug_sync_classbits(), passing 0 directly as old_bits. Initial sync only asserts active parameter bits (+p), leaving local -DDEBUG overrides intact. Divergent state can be synchronized at runtime by cycling the parameter knob (noted by Sashiko review). . document publication ordering and deadlock avoidance in ddebug_add_module() and ddebug_sync_classbits(). v10: . pass init_bits computed from (_DPRINTK_FLAGS_DEFAULT & _DPRINTK_FLAGS_PRINT) in ddebug_sync_classbits() to properly disable -DDEBUG callsites. --- include/asm-generic/dyndbg.lds.h | 6 +- include/linux/dynamic_debug.h | 164 +++++++++++++++++++++++++--- kernel/module/main.c | 3 + lib/Kconfig.debug | 24 ++++- lib/Makefile | 3 + lib/dynamic_debug.c | 227 +++++++++++++++++++++++++++++++++++++-- lib/test_dynamic_debug.c | 131 +++++++++++++++++----- lib/test_dynamic_debug_submod.c | 14 +++ 8 files changed, 515 insertions(+), 57 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 #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 bf6039191fa7..61f9fd4aa7af 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -83,19 +83,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 { + const 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_ __sections + * together, each is a vec_: 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; @@ -107,10 +118,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 { @@ -129,17 +146,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, \ @@ -148,6 +234,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, ...); @@ -309,12 +433,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_ + * macros for the interface, along with an enum for the + * + * __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)) */ @@ -322,6 +452,9 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor, #include #include +#define DYNAMIC_DEBUG_CLASSMAP_DEFINE(_var, _mapty, _base, ...) +#define DYNAMIC_DEBUG_CLASSMAP_USE(_var) +#define DYNAMIC_DEBUG_CLASSMAP_USE_(_var, _offset) #define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) #define DYNAMIC_DEBUG_BRANCH(descriptor) false #define DECLARE_DYNDBG_CLASSMAP(...) @@ -368,8 +501,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 81a31ebdf332..13f7930df8fb 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2791,6 +2791,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 c3f448f3b8f1..2abf28d6d063 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -3138,12 +3138,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 dfab958327c5..9cdf0a430f5f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -85,6 +85,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 18289ca7170b..8c314a52d8f9 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -29,6 +29,7 @@ #include #include #include + #include #include #include @@ -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; } @@ -239,8 +261,7 @@ static bool ddebug_match_desc(const struct ddebug_query *query, * callsites, normally the same as number of changes. If verbose, * logs the changes. Takes ddebug_lock. */ -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; @@ -261,7 +282,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 { @@ -594,7 +616,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) { @@ -773,6 +795,7 @@ int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp) default: return -1; } + return 0; } EXPORT_SYMBOL(param_get_dyndbg_classes); @@ -1085,15 +1108,28 @@ static bool ddebug_class_in_range(const int class_id, const struct ddebug_class_ class_id < map->base + map->length); } +static bool ddebug_user_class_in_range(const int class_id, const struct ddebug_class_user *cli) +{ + if (!cli || !cli->map) + return false; + int base = cli->map->base + cli->offset; + return (class_id >= base && class_id < base + cli->map->length); +} + 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_user_class_in_range(dp->class_id, cli)) + return cli->map->class_names[dp->class_id - cli->map->base - cli->offset]; + return NULL; } @@ -1174,6 +1210,163 @@ 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]); \ + }) + +/* + * Modules which define classmaps get them initialized by + * param-callback via module.c:parse_one. Modules which use other's + * classmaps must be initialized explicitly. + */ +static inline u32 ddebug_class_param_to_bits(const struct ddebug_class_param *dcp) +{ + const struct ddebug_class_map *map = dcp->map; + + switch (map->map_type) { + case DD_CLASS_TYPE_DISJOINT_BITS: + return *dcp->bits & CLASSMAP_BITMASK(map->length); + case DD_CLASS_TYPE_LEVEL_NUM: + return CLASSMAP_BITMASK(min_t(u32, *dcp->lvl, map->length)); + default: + return 0; + } +} + +static void __maybe_unused ddebug_class_param_clamp_input(u32 *inrep, const struct kernel_param *kp) +{ + const struct ddebug_class_param *dcp = kp->arg; + const struct ddebug_class_map *map = dcp->map; + + switch (map->map_type) { + 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%x exceeds mask: 0x%x, masking\n", + KP_NAME(kp), *inrep, CLASSMAP_BITMASK(map->length)); + *inrep &= CLASSMAP_BITMASK(map->length); + } + break; + case DD_CLASS_TYPE_LEVEL_NUM: + /* input is bitpos, of highest verbosity to be enabled */ + if (*inrep > map->length) { + pr_warn("%s: level:%d exceeds max:%d, clamping\n", + KP_NAME(kp), *inrep, map->length); + *inrep = map->length; + } + break; + } +} + +/* + * Called for class-users and native classmaps during module load. + * + * Initial sync assumes 0 as old_bits, so it only asserts (+p) classes + * explicitly set by the parameter. -DDEBUGs invalidate this assumption + * for some pr_debugs; a user can override the developer/builder choice + * by toggling the param at runtime. + * + * Parameters are sampled via READ_ONCE without ddebug_lock; + * holding ddebug_lock here would deadlock against ddebug_change(). + */ +static void ddebug_sync_classbits(const struct kernel_param *kp, const char *modname) +{ + const struct ddebug_class_param *dcp = kp->arg; + u32 val, new_bits; + + if (!dcp || !dcp->map) + return; + + switch (dcp->map->map_type) { + case DD_CLASS_TYPE_DISJOINT_BITS: + val = READ_ONCE(*dcp->bits); + new_bits = val; + v2pr_info(" %s: classbits: 0x%x\n", KP_NAME(kp), new_bits); + ddebug_apply_class_bitmap(dcp, &new_bits, 0, modname); + break; + case DD_CLASS_TYPE_LEVEL_NUM: + val = READ_ONCE(*dcp->lvl); + new_bits = CLASSMAP_BITMASK(val); + v2pr_info(" %s: lvl:%d bits:0x%x\n", KP_NAME(kp), val, new_bits); + ddebug_apply_class_bitmap(dcp, &new_bits, 0, modname); + break; + default: + pr_err("bad map type %d\n", dcp->map->map_type); + return; + } +} + +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 && dcp->map == map) { + 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 (!cm) + return; +#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); + } +} + +/* + * Symmetrically bind controlling parameters and sync initial classbits: + * - ddebug_apply_class_maps(): for native classmaps defined by this module. + * - ddebug_apply_class_users(): for imported classmaps referenced via USE. + */ +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, di->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 @@ -1211,6 +1404,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) @@ -1223,6 +1417,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 @@ -1239,12 +1434,24 @@ 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); mutex_lock(&ddebug_lock); list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); + /* + * Now that dt is on the list, ddebug_change() (which also + * takes the lock) can find it, so apply_class_*() can lookup + * the class-param (if any) and set pr_debugs accordingly, + * arming them before the module's init() runs. + */ + if (dt->info.maps.len) + ddebug_apply_class_maps(&dt->info); + if (dt->info.users.len) + ddebug_apply_class_users(&dt->info); - 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; } @@ -1394,8 +1601,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..34e51996aa20 100644 --- a/lib/test_dynamic_debug.c +++ b/lib/test_dynamic_debug.c @@ -6,11 +6,30 @@ * Jim Cromie */ -#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 -/* 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_. 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,53 @@ 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. V1 is > D2_DRMRES */ +enum cat_level_num { V1 = 16, 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, + V1, "V1", "V2", "V3", "V4", "V5", "V6", "V7"); + +#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, 7); + +enum cat_level_offset { Vu1 = V1 + 7, Vu2, Vu3, Vu4, Vu5, Vu6, Vu7 }; + +#endif + +/* + * now add the sysfs-params + */ + +DYNAMIC_DEBUG_CLASSMAP_PARAM(disjoint_bits, p); +DYNAMIC_DEBUG_CLASSMAP_PARAM(level_num, p); /* stand-in for all pr_debug etc */ #define prdbg(SYM) __pr_debug_cls(SYM, #SYM " msg\n") @@ -104,6 +172,7 @@ static void do_levels(void) { pr_debug("doing levels\n"); +#if !defined(TEST_DYNAMIC_DEBUG_SUBMOD) prdbg(V1); prdbg(V2); prdbg(V3); @@ -111,10 +180,20 @@ static void do_levels(void) prdbg(V5); prdbg(V6); prdbg(V7); +#else + prdbg(Vu1); + prdbg(Vu2); + prdbg(Vu3); + prdbg(Vu4); + prdbg(Vu5); + prdbg(Vu6); + prdbg(Vu7); +#endif } 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 + */ + +/* + * 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