diff options
author | Jim Cromie <jim.cromie@gmail.com> | 2022-09-05 00:40:51 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-09-07 18:04:49 +0300 |
commit | aad0214f30264c19044a77fc4776def349b76fc4 (patch) | |
tree | 0d09e73182897b306c5b0051f5eb7af8f35b071f | |
parent | 3fc95d80a536e49e38ba4f79ca60cb4e64f99b3b (diff) | |
download | linux-aad0214f30264c19044a77fc4776def349b76fc4.tar.xz |
dyndbg: add DECLARE_DYNDBG_CLASSMAP macro
Using DECLARE_DYNDBG_CLASSMAP, modules can declare up to 31 classnames.
By doing so, they authorize dyndbg to manipulate class'd prdbgs (ie:
__pr_debug_cls, and soon drm_*dbg), ala::
:#> echo class DRM_UT_KMS +p > /proc/dynamic_debug/control
The macro declares and initializes a static struct ddebug_class_map::
- maps approved class-names to class_ids used in module,
by array order. forex: DRM_UT_*
- class-name vals allow validation of "class FOO" queries
using macro is opt-in
- enum class_map_type - determines interface, behavior
Each module has its own class-type and class_id space, and only known
class-names will be authorized for a manipulation. Only DRM modules
should know and respont to this:
:#> echo class DRM_UT_CORE +p > control # across all modules
pr_debugs (with default class_id) are still controllable as before.
DECLARE_DYNDBG_CLASSMAP(_var, _maptype, _base, classes...) is::
_var: name of the static struct var. user passes to module_param_cb()
if they want a sysfs node.
_maptype: this is hard-coded to DD_CLASS_TYPE_DISJOINT_BITS for now.
_base: usually 0, it allows splitting 31 classes into subranges, so
that multiple classes / sysfs-nodes can share the module's
class-id space.
classes: list of class_name strings, these are mapped to class-ids
starting at _base. This class-names list must have a
corresponding ENUM, with SYMBOLS that match the literals,
and 1st enum val = _base.
enum class_map_type has 4 values, on 2 factors::
- classes are disjoint/independent vs relative/x<y/verbosity.
disjoint is basis, verbosity by overlay.
- input NUMBERS vs [+-]CLASS_NAMES
uints, ideally hex. vs +DRM_UT_CORE,-DRM_UT_KMS
DD_CLASS_TYPE_DISJOINT_BITS: classes are separate, one per bit.
expecting hex input. built for drm.debug, basis for other types.
DD_CLASS_TYPE_DISJOINT_NAMES: input is a CSV of [+-]CLASS_NAMES,
classes are independent, like DISJOINT
DD_CLASS_TYPE_LEVEL_NUM: input is numeric level, 0-N.
0 implies silence. use printk to break that.
relative levels applied on bitmaps.
DD_CLASS_TYPE_LEVEL_NAMES: input is a CSV of [+-]CLASS_NAMES,
names like: ERR,WARNING,NOTICE,INFO,DEBUG
avoiding EMERG,ALERT,CRIT,ERR - no point.
NOTES:
The macro places the initialized struct ddebug_class_map into the
__dyndbg_classes section. That draws an 'orphan' warning which we
handle in the next commit. The struct attributes are necessary:
__aligned(8) fixed null-ptr derefs, and __used is needed by drm
drivers, which declare class-maps, but don't also declare a
sysfs-param, and thus dont ref the classmap. The __used insures that
the linkage is made, then the class-map is found at load-time.
While its possible to handle both NAMES and NUMBERS in the same
sys-interface, there is ambiguity to avoid, by disallowing them
together. Later, if ambiguities are resolved, 2 new enums can permit
both inputs, on verbose & independent types separately, and authors
can select the interface style they like.
The plan is to implement LEVELS in the callbacks, outside of
ddebug_exec_query(), which for simplicity will treat the CLASSES in
the map as disjoint. The callbacks can see map-type, and apply ++/--
loops (or bitops) to force the relative meanings across the
class-bitmap.
RFC: That leaves 2 issues:
1. doing LEVELs in callbacks means that the native >control interface
doesn't enforce the LEVELS relationship, so you could confusingly have
V3 enabled, but V1 disabled. OTOH, the control iface already allows
infinite tweaking of the underlying callsites; sysfs node readback can
only tell the user what they previously wrote.
2. All dyndbg >control reduces to a query/command, includes +/-, which
is at-root a kernel patching operation with +/- semantics. And the
_NAMES handling exposes it to the user, making it API-adjacent.
And its not just >control where +/- gets used (which is settled), the
new place is with sysfs-nodes exposing _*_NAMES classes, and here its
subtly different.
_DISJOINT_NAMES: is simple, independent
_LEVEL_NAMES: masks-on bits 0 .. N-1, N..max off
# turn on L3,L2,L1 others off
echo +L3 > /sys/module/test_dynamic_debug/parameters/p_level_names
# turn on L2,L1 others off
echo -L3 > /sys/module/test_dynamic_debug/parameters/p_level_names
IOW, the - changes the threshold-on bitpos by 1.
Alternatively, we could treat the +/- as half-duplex, where -L3 turns
off L>2 (and ignores L1), and +L2 would turn on L<=2 (and ignore others).
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Link: https://lore.kernel.org/r/20220904214134.408619-15-jim.cromie@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | include/linux/dynamic_debug.h | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 3c9690da44d9..98dbf1d49984 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -56,6 +56,61 @@ struct _ddebug { #endif } __attribute__((aligned(8))); +enum 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_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 + */ +}; + +struct ddebug_class_map { + struct list_head link; + struct module *mod; + 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 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; |