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@xxxxxxxxx> --- . 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