[PATCH v3 16/41] dyndbg: add drm.debug style bitmap support
Jim Cromie
jim.cromie at gmail.com
Mon Jul 18 06:36:00 UTC 2022
Add kernel_param_ops and callbacks to apply a class-map to a
sysfs-node, which then can control classes defined in that class-map.
This supports uses like:
echo 0x3 > /sys/module/drm/parameters/debug
IE add these:
- int param_set_dyndbg_classes()
- int param_get_dyndbg_classes()
- struct kernel_param_ops param_ops_dyndbg_classes
Following the model of kernel/params.c STANDARD_PARAM_DEFS, these are
non-static and exported. This might be unnecessary here.
get/set use an augmented kernel_param; the arg refs a new struct
ddebug_classes_bitmap_param, initialized by DYNAMIC_DEBUG_CLASSBITS
macro, which contains:
BITS: a pointer to the user module's ulong holding the bits/state. By
ref'g the client's bit-state _var, we coordinate with existing code
(such as drm_debug_enabled) which uses the _var, so it works
unchanged, even as the foundation is switched out underneath it..
Using a ulong allows use of BIT() etc.
FLAGS: dyndbg.flags toggled by changes to bitmap. Usually just "p".
MAP: a pointer to struct ddebug_classes_map, which maps those
class-names to .class_ids 0..N that the module is using. This
class-map is declared & initialized by DEFINE_DYNDBG_CLASSMAP.
map-type: add support here for DD_CLASS_DISJOINT, DD_CLASS_VERBOSE.
These 2 class-types both expect an integer; _DISJOINT treats input
like a bit-vector (ala drm.debug), and sets each bit accordingly.
_VERBOSE treats input like a bit-pos:N, then sets bits(0..N)=1, and
bits(N+1..max)=0. This applies (bit<N) semantics on top of disjoint
bits.
cases DD_CLASS_SYMBOLIC, DD_CLASS_LEVELS are included for the complete
picture, with commented out call to a following commit.
NOTES:
this now includes SYMBOLIC/LEVELS support, too tedious to keep
separate thru all the tweaking.
get-param undoes the bit-pos -> bitmap transform that set-param does
on VERBOSE inputs, this gives the read-what-was-written property.
_VERBOSE is overlay on _DISJOINT:
verbose-maps still need class-names, even though theyre not usable at
the sysfs interface (unlike with _SYMBOLIC/_LEVELS).
- It must have a "V0" name,
something below "V1" to turn "V1" off.
__pr_debug_cls(V0,..) is printk, don't do that.
- "class names" is required at the >control interface.
- relative levels are not enforced at >control
IOW this is possible, and maybe confusing:
echo class V3 +p > control
echo class V1 -p > control
IMO thats ok, relative verbosity is an interface property.
Signed-off-by: Jim Cromie <jim.cromie at gmail.com>
---
. drop kp->mod->name as unneeded (build-dependent) <lkp>
---
include/linux/dynamic_debug.h | 18 ++++
lib/dynamic_debug.c | 193 ++++++++++++++++++++++++++++++++++
2 files changed, 211 insertions(+)
diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index f57076e02767..b50bdd5c8184 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -113,6 +113,12 @@ struct ddebug_class_map {
#define NUM_TYPE_ARGS(eltype, ...) \
(sizeof((eltype[]) {__VA_ARGS__}) / sizeof(eltype))
+struct ddebug_classes_bitmap_param {
+ unsigned long *bits;
+ char flags[8];
+ const struct ddebug_class_map *map;
+};
+
#if defined(CONFIG_DYNAMIC_DEBUG_CORE)
int ddebug_add_module(struct _ddebug *tab, unsigned int num_debugs,
@@ -274,6 +280,10 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
KERN_DEBUG, prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii)
+struct kernel_param;
+int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp);
+int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp);
+
/* for test only, generally expect drm.debug style macro wrappers */
#define __pr_debug_cls(cls, fmt, ...) do { \
BUILD_BUG_ON_MSG(!__builtin_constant_p(cls), \
@@ -322,6 +332,14 @@ static inline int ddebug_dyndbg_module_param_cb(char *param, char *val,
rowsize, groupsize, buf, len, ascii); \
} while (0)
+struct kernel_param;
+static inline int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
+{ return 0; }
+static inline int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
+{ return 0; }
+
#endif /* !CONFIG_DYNAMIC_DEBUG_CORE */
+extern const struct kernel_param_ops param_ops_dyndbg_classes;
+
#endif
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 4c27bbe5187e..dd27dc514aa3 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -596,6 +596,199 @@ static int ddebug_exec_queries(char *query, const char *modname)
return nfound;
}
+static int ddebug_apply_class_bitmap(const struct ddebug_classes_bitmap_param *dcp,
+ unsigned long inbits)
+{
+#define QUERY_SIZE 128
+ char query[QUERY_SIZE];
+ const struct ddebug_class_map *map = dcp->map;
+ int matches = 0;
+ int bi, ct;
+
+ v2pr_info("in: 0x%lx on: 0x%lx\n", inbits, *dcp->bits);
+
+ for (bi = 0; bi < map->length; bi++) {
+ if (test_bit(bi, &inbits) == test_bit(bi, dcp->bits))
+ continue;
+
+ snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],
+ test_bit(bi, &inbits) ? '+' : '-', dcp->flags);
+
+ ct = ddebug_exec_queries(query, NULL);
+ matches += ct;
+
+ v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,
+ ct, map->class_names[bi], inbits);
+ }
+ return matches;
+}
+
+/* support for [+-] symbolic-name boolean list */
+static int param_set_dyndbg_class_strings(const char *instr, const struct kernel_param *kp)
+{
+ const struct ddebug_classes_bitmap_param *dcp = kp->arg;
+ const struct ddebug_class_map *map = dcp->map;
+ unsigned long inbits;
+ int idx, totct = 0;
+ bool wanted;
+ char *cls, *p;
+
+ cls = kstrdup(instr, GFP_KERNEL);
+ p = strchr(cls, '\n');
+ if (p)
+ *p = '\0';
+
+ vpr_info("\"%s\" > %s\n", cls, kp->name);
+ inbits = *dcp->bits;
+
+ for (; cls; cls = p) {
+ p = strchr(cls, ',');
+ if (p)
+ *p++ = '\0';
+
+ if (*cls == '-') {
+ wanted = false;
+ cls++;
+ } else {
+ wanted = true;
+ if (*cls == '+')
+ cls++;
+ }
+ idx = match_string(map->class_names, map->length, cls);
+ if (idx < 0) {
+ pr_err("%s unknown to %s\n", cls, kp->name);
+ continue;
+ }
+
+ switch (map->map_type) {
+ case DD_CLASS_TYPE_SYMBOLIC:
+ if (test_bit(idx, &inbits) == wanted) {
+ v3pr_info("no change on %s\n", cls);
+ continue;
+ }
+ inbits ^= BIT(idx);
+ break;
+ case DD_CLASS_TYPE_LEVELS:
+ /* bitmask must respect classmap ranges, this does not */
+ inbits = (1 << (idx + wanted));
+ break;
+ default:
+ pr_err("illegal map-type value %d\n", map->map_type);
+ }
+ v2pr_info("%s: bit %d: %s\n", kp->name, idx, map->class_names[idx]);
+ totct += ddebug_apply_class_bitmap(dcp, inbits);
+ }
+ kfree(cls);
+ *dcp->bits = inbits;
+ vpr_info("total matches: %d\n", totct);
+ return 0;
+}
+
+#define CLASSMAP_BITMASK(width) ((1UL << (width)) - 1)
+
+/**
+ * param_set_dyndbg_classes - bits => categories >control setter
+ * @instr: string echo>d to sysfs
+ * @kp: kp->arg has state: bits, map
+ *
+ * Enable/disable prdbgs by their "category", as given in the
+ * arguments to DYNAMIC_DEBUG_CLASSES.
+ *
+ * Returns: 0 or <0 if error.
+ */
+int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp)
+{
+ const struct ddebug_classes_bitmap_param *dcp = kp->arg;
+ const struct ddebug_class_map *map = dcp->map;
+ unsigned long inrep;
+ int rc, totct = 0;
+
+ switch (map->map_type) {
+
+ case DD_CLASS_TYPE_SYMBOLIC:
+ case DD_CLASS_TYPE_LEVELS:
+ /* CSV list of [+-]classnames */
+ return param_set_dyndbg_class_strings(instr, kp);
+
+ case DD_CLASS_TYPE_DISJOINT:
+ case DD_CLASS_TYPE_VERBOSE:
+ /* numeric input */
+ rc = kstrtoul(instr, 0, &inrep);
+ if (rc) {
+ pr_err("expecting numeric input: %s > %s\n", instr, kp->name);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: bad map type: %d\n", kp->name, map->map_type);
+ return -EINVAL;
+ }
+
+ switch (map->map_type) {
+ case DD_CLASS_TYPE_DISJOINT:
+ /* 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",
+ kp->name, inrep, CLASSMAP_BITMASK(map->length));
+ inrep &= CLASSMAP_BITMASK(map->length);
+ }
+ break;
+ case DD_CLASS_TYPE_VERBOSE:
+ /* input is bitpos, of highest verbosity enabled */
+ if (inrep > map->length) {
+ pr_warn("%s: verbosity:%ld exceeds range:%d, clamping\n",
+ kp->name, inrep, map->length);
+ inrep = map->length;
+ }
+ v2pr_info("VERBOSE: %ld > %s\n", inrep, kp->name);
+ inrep = CLASSMAP_BITMASK(inrep + 1);
+ break;
+ default:
+ pr_warn("%s: bad map type: %d\n", kp->name, map->map_type);
+ }
+ totct += ddebug_apply_class_bitmap(dcp, inrep);
+ *dcp->bits = inrep;
+
+ vpr_info("%s: total matches: %d\n", kp->name, totct);
+ return 0;
+}
+EXPORT_SYMBOL(param_set_dyndbg_classes);
+
+/**
+ * param_get_dyndbg_classes - classes reader
+ * @buffer: string description of controlled bits -> classes
+ * @kp: kp->arg has state: bits, map
+ *
+ * Reads last written bits, underlying prdbg state may have changed since.
+ * Returns: #chars written or <0 on error
+ */
+int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp)
+{
+ const struct ddebug_classes_bitmap_param *dcp = kp->arg;
+ const struct ddebug_class_map *map = dcp->map;
+ unsigned long val = *dcp->bits;
+
+ switch (map->map_type) {
+ case DD_CLASS_TYPE_SYMBOLIC:
+ case DD_CLASS_TYPE_DISJOINT:
+ case DD_CLASS_TYPE_LEVELS:
+ return scnprintf(buffer, PAGE_SIZE, "0x%lx\n", val);
+ case DD_CLASS_TYPE_VERBOSE:
+ /* convert internal bits to a level */
+ return scnprintf(buffer, PAGE_SIZE, "%lu\n",
+ find_first_zero_bit(&val, map->length) - 1);
+ default:
+ return -1;
+ }
+}
+EXPORT_SYMBOL(param_get_dyndbg_classes);
+
+const struct kernel_param_ops param_ops_dyndbg_classes = {
+ .set = param_set_dyndbg_classes,
+ .get = param_get_dyndbg_classes,
+};
+EXPORT_SYMBOL(param_ops_dyndbg_classes);
+
#define PREFIX_SIZE 64
static int remaining(int wrote)
--
2.36.1
More information about the dri-devel
mailing list