From: Jim Cromie <[email protected]>

classmap-v1 code protected class'd pr_debugs from unintended
changes by unclassed/_DFLT queries:

  # - to declutter examples:
  alias ddcmd='echo $* > /proc/dynamic_debug/control'

  # IOW, this should NOT alter drm.debug settings
  ddcmd -p

  # Instead, you must name the class to change it.
  # Protective but tedious
  ddcmd class DRM_UT_CORE +p

  # Or do it the (old school) subsystem way
  # This is ABI !!
  echo 1 > /sys/module/drm/parameters/debug

Since the debug sysfs-node is ABI, if dyndbg is going to implement it,
it must also honor its settings; it must at least protect against
accidental changes to its classes from legacy queries.

The protection allows all previously conceived queries to work the way
they always have; ie select the same set of pr_debugs, despite the
inclusion of whole new classes of pr_debugs.

But that choice has 2 downsides:

1. "name the class to change it" makes a tedious long-winded
interface, needing many commands to set DRM_UT_* one at a time.

2. It makes the class keyword special in some sense; the other
keywords skip only on query mismatch, otherwise the code falls thru to
adjust the pr-debug site.

 Jason Baron    didn't like v1 on point 2.
 Louis Chauvet  didn't like recent rev on point 1 tedium.

But that said: /sys/ is ABI, so this must be reliable:

  #> echo 0x1f > /sys/module/drm/parameters/debug

It 'just works' without dyndbg underneath; we must deliver that same
stability.  Convenience is secondary.

The new resolution:

If ABI is the blocking issue, then no ABI means no blocking issue.
IOW, if the classmap has no presence under /sys/*, ie no PARAM, there
is no ABI to guard, and no reason to enforce a tedious interface.

In the future, if DRM wants to alter this protection, that is
practical, but I think default-on is the correct mode.

So atm classes without a PARAM are unprotected at >control, allowing
admins their shortcuts.  I think this could satisfy all viewpoints.

That said, theres also a possibility of wildcard classes:

   #> ddcmd class '*' +p

Currently, the query-class is exact-matched against each module's
classmaps.names.  This gives precise behavior, a good basis.

But class wildcards are possible, they just did'nt appear useful for
DRM, whose classmap names are a flat DRM_UT_* namespace.

IOW, theres no useful selectivity there:

   #> ddcmd class "DRM_*" +p            # these enable every DRM_* class
   #> ddcmd class "DRM_UT_*" +p

   #> ddcmd class "DRM_UT_V*" +p        # finally select just 1: DRM_UT_VBL
   #> ddcmd class "DRM_UT_D*" +p        # but this gets 3

   #> ddcmd class "D*V*" +p             # here be dragons

But there is debatable utility in the feature.

   #> ddcmd class __DEFAULT__ -p        # what about this ?
   #> ddcmd -p                          # thats what this does. automatically

Anyway, this patch does:

1. adds link field from _ddebug_class_map to the .controlling_param

2. sets it in ddebug_match_apply_kparam(), during modprobe/init,
   when options like drm.debug=VAL are handled.

3. ddebug_class_has_param() now checks .controlling_param

4. ddebug_class_wants_protection() macro renames 3.
   this frames it as a separable policy decision

5. ddebug_match_desc() gets the most attention:

a. move classmap consideration to the bottom
   this insures all other constraints act 1st.
   allows simpler 'final' decisions.

b. split class choices cleanly on query:
   class FOO vs none, and class'd vs _DPRINTK_CLASS_DFLT site.

c. calls 4 when applying a class-less query to a class'd pr_debug
   here we need a new fn to find the classmap with this .class_id

d. calls new ddebug_find_classmap_by_class_id().
   when class-less query looks at a class'd pr_debug.
   finds classmap, which can then decide, currently by PARAM existence.

NOTES:

protection is only against class-less queries, explicit "class FOO"
adjustments are allowed (that is the mechanism).

The drm.debug sysfs-node heavily under-specifies the class'd pr_debugs
it controls; none of the +mfls prefixing flags have any effect, and
each callsite remains individually controllable. drm.debug just
toggles the +p flag for all the modules' class'd pr_debugs.

Signed-off-by: Jim Cromie <[email protected]>
Reviewed-by: Louis Chauvet <[email protected]>
---
v2: RvB after SoB

old-v12

minor fixup after squashing subsequent commits to previous ones
---
 include/linux/dynamic_debug.h                      |  14 ++-
 lib/dynamic_debug.c                                | 130 +++++++++++++++++----
 .../selftests/dynamic_debug/dyndbg_selftest.sh     |   4 +-
 3 files changed, 121 insertions(+), 27 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index a740b3fabc09..d00605ef651e 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -92,6 +92,7 @@ enum ddebug_class_map_type {
  * map @class_names 0..N to consecutive constants starting at @base.
  */
 struct ddebug_class_map {
+       struct ddebug_class_param *controlling_param;
        const struct module *mod;       /* NULL for builtins */
        const char *mod_name;           /* needed for builtins */
        const char **class_names;
@@ -299,7 +300,12 @@ struct ddebug_class_param {
  *
  * Creates a sysfs-param to control the classes defined by the
  * exported classmap, with bits 0..N-1 mapped to the classes named.
- * This version keeps class-state in a private long int.
+ *
+ * Since sysfs-params are ABI, this also protects the classmap'd
+ * pr_debugs from un-class'd `echo -p > /proc/dynamic_debug/control`
+ * changes.
+ *
+ * This keeps class-state in a private long int.
  */
 #define DYNAMIC_DEBUG_CLASSMAP_PARAM(_name, _var, _flags)              \
        static u32 _name##_bvec;                                        \
@@ -312,10 +318,8 @@ struct ddebug_class_param {
  * @_var:   name of the (exported) classmap var defining the classes/bits
  * @_flags: flags to be toggled, typically just 'p'
  *
- * Creates a sysfs-param to control the classes defined by the
- * exported clasmap, with bits 0..N-1 mapped to the classes named.
- * This version keeps class-state in user @_bits.  This lets drm check
- * __drm_debug elsewhere too.
+ * Like DYNAMIC_DEBUG_CLASSMAP_PARAM, but maintains param-state in
+ * extern @_bits.  This lets DRM check __drm_debug elsewhere too.
  */
 #define DYNAMIC_DEBUG_CLASSMAP_PARAM_REF(_name, _bits, _var, _flags)   \
        __DYNAMIC_DEBUG_CLASSMAP_PARAM(_name, _bits, _var, _flags)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 93a5a481c8b8..a5d813ad323a 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -71,6 +71,10 @@ struct flag_settings {
        unsigned int mask;
 };
 
+static bool ddebug_class_map_in_range(const int class_id,
+                                     const struct ddebug_class_map *map);
+static bool ddebug_class_user_in_range(const int class_id,
+                                      const struct ddebug_class_user *user);
 static DEFINE_MUTEX(ddebug_lock);
 static LIST_HEAD(ddebug_tables);
 static int verbose;
@@ -200,6 +204,46 @@ static struct ddebug_class_map 
*ddebug_find_valid_class(struct _ddebug_info cons
        return NULL;
 }
 
+
+
+static struct ddebug_class_map *
+ddebug_find_map_by_class_id(struct _ddebug_info *di, int class_id)
+{
+       struct ddebug_class_map *map;
+       struct ddebug_class_user *cli;
+       int i;
+
+       for_subvec(i, map, di, maps)
+               if (ddebug_class_map_in_range(class_id, map))
+                       return map;
+
+       for_subvec(i, cli, di, users)
+               if (ddebug_class_user_in_range(class_id, cli))
+                       return cli->map;
+
+       return NULL;
+}
+
+/*
+ * classmaps-V1 protected classes from changes by legacy commands
+ * (those selecting _DPRINTK_CLASS_DFLT by omission).  This had the
+ * downside that saying "class FOO" for every change can get tedious.
+ *
+ * V2 is smarter, it protects class-maps if the defining module also
+ * calls DYNAMIC_DEBUG_CLASSMAP_PARAM to create a sysfs parameter.
+ * Since the author wants the knob, we should assume they intend to
+ * use it (in preference to "class FOO +p" >control), and want to
+ * trust its settings.  This gives protection when its useful, and not
+ * when its just tedious.
+ */
+static inline bool ddebug_class_has_param(const struct ddebug_class_map *map)
+{
+       return !!(map->controlling_param);
+}
+
+/* re-framed as a policy choice */
+#define ddebug_class_wants_protection(map) (ddebug_class_has_param(map))
+
 /*
  * Search the tables for _ddebug's which match the given `query' and
  * apply the `flags' and `mask' to them.  Returns number of matching
@@ -208,11 +252,10 @@ static struct ddebug_class_map 
*ddebug_find_valid_class(struct _ddebug_info cons
  */
 static bool ddebug_match_desc(const struct ddebug_query *query,
                              struct _ddebug *dp,
-                             int valid_class)
+                             struct _ddebug_info *di,
+                             int selected_class)
 {
-       /* match site against query-class */
-       if (dp->class_id != valid_class)
-               return false;
+       struct ddebug_class_map *site_map;
 
        /* match against the source filename */
        if (query->filename &&
@@ -256,7 +299,28 @@ static bool ddebug_match_desc(const struct ddebug_query 
*query,
            dp->lineno > query->last_lineno)
                return false;
 
-       return true;
+       /*
+        * above are all satisfied, so we can make final decisions:
+        * 1- class FOO or implied class __DEFAULT__
+        * 2- site.is_classed or not
+        */
+       if (query->class_string) {
+               /* class FOO given, exact match required */
+               return (dp->class_id == selected_class);
+       }
+       /* query class __DEFAULT__ by omission. */
+       if (dp->class_id == _DPRINTK_CLASS_DFLT) {
+               /* un-classed site */
+               return true;
+       }
+       /* site is class'd */
+       site_map = ddebug_find_map_by_class_id(di, dp->class_id);
+       if (!site_map) {
+               WARN_ONCE(1, "unknown class_id %d, check %s's CLASSMAP 
definitions", dp->class_id, di->mod_name);
+               return false;
+       }
+       /* module(-param) decides protection */
+       return !ddebug_class_wants_protection(site_map);
 }
 
 static int ddebug_change(const struct ddebug_query *query, struct 
flag_settings *modifiers)
@@ -266,13 +330,13 @@ static int ddebug_change(const struct ddebug_query 
*query, struct flag_settings
        unsigned int newflags;
        unsigned int nfound = 0;
        struct flagsbuf fbuf, nbuf;
-       struct ddebug_class_map *map = NULL;
-       int valid_class;
+       int selected_class;
 
        /* search for matching ddebugs */
        mutex_lock(&ddebug_lock);
        list_for_each_entry(dt, &ddebug_tables, link) {
                struct _ddebug_info *di = &dt->info;
+               struct ddebug_class_map *mods_map;
 
                /* match against the module name */
                if (query->module &&
@@ -280,20 +344,18 @@ static int ddebug_change(const struct ddebug_query 
*query, struct flag_settings
                    !match_wildcard_hyphen(query->module, 
kbasename(di->mod_name)))
                        continue;
 
+               selected_class = _DPRINTK_CLASS_DFLT;
                if (query->class_string) {
-                       map = ddebug_find_valid_class(&dt->info, 
query->class_string,
-                                                     &valid_class);
-                       if (!map)
+                       mods_map = ddebug_find_valid_class(di, 
query->class_string,
+                                                          &selected_class);
+                       if (!mods_map)
                                continue;
-               } else {
-                       /* constrain query, do not touch class'd callsites */
-                       valid_class = _DPRINTK_CLASS_DFLT;
                }
 
                for (i = 0; i < di->descs.len; i++) {
                        struct _ddebug *dp = &di->descs.start[i];
 
-                       if (!ddebug_match_desc(query, dp, valid_class))
+                       if (!ddebug_match_desc(query, dp, di, selected_class))
                                continue;
 
                        nfound++;
@@ -1147,7 +1209,6 @@ static bool ddebug_class_user_in_range(const int 
class_id, const struct ddebug_c
                return false;
        return ddebug_class_map_in_range(class_id - user->offset, user->map);
 }
-
 static const char *ddebug_class_name(struct _ddebug_info *di, struct _ddebug 
*dp)
 {
        struct ddebug_class_map *map;
@@ -1298,16 +1359,25 @@ static void ddebug_sync_classbits(const struct 
kernel_param *kp, const char *mod
        }
 }
 
-static void ddebug_match_apply_kparam(const struct kernel_param *kp,
-                                     const struct ddebug_class_map *map,
-                                     const char *mod_name)
+static struct ddebug_class_param *
+ddebug_get_classmap_kparam(const struct kernel_param *kp,
+                          const struct ddebug_class_map *map)
 {
        struct ddebug_class_param *dcp;
 
        if (kp->ops != &param_ops_dyndbg_classes)
-               return;
+               return NULL;
 
        dcp = (struct ddebug_class_param *)kp->arg;
+       return (map == dcp->map)
+               ? dcp : (struct ddebug_class_param *)NULL;
+}
+
+static void ddebug_match_apply_kparam(const struct kernel_param *kp,
+                                     struct ddebug_class_map *map,
+                                     const char *mod_name)
+{
+       struct ddebug_class_param *dcp = ddebug_get_classmap_kparam(kp, map);
 
        if (dcp && dcp->map == map) {
                v2pr_info(" kp:%s.%s =0x%x", mod_name, kp->name, *dcp->bits);
@@ -1316,7 +1386,7 @@ static void ddebug_match_apply_kparam(const struct 
kernel_param *kp,
        }
 }
 
-static void ddebug_apply_params(const struct ddebug_class_map *cm, const char 
*mod_name)
+static void ddebug_apply_params(struct ddebug_class_map *cm, const char 
*mod_name)
 {
        const struct kernel_param *kp;
 
@@ -1339,6 +1409,26 @@ static void ddebug_apply_params(const struct 
ddebug_class_map *cm, const char *m
        }
 }
 
+#if 0
+/*
+ * called from add_module, ie early. it can find controlling kparams,
+ * which can/does? enable protection of this classmap from class-less
+ * queries, on the grounds that the user created the kparam, means to
+ * use it, and expects it to reflect reality.  We should oblige him,
+ * and protect those classmaps from classless "-p" changes.
+ */
+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);
+}
+#endif
+
 static void ddebug_apply_class_users(const struct _ddebug_info *di)
 {
        struct ddebug_class_user *cli;
diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh 
b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
index 0bb3c3e11df7..194e9c9d4544 100755
--- a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
+++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
@@ -686,8 +686,8 @@ function GOLDEN_RECORDS {
 #K= d4923595eea382923aee64aed15c7c35 FT_test_classes.1
 #K= a15ec4843acd721fbdfddc0b512c8032 FT_test_classes.2
 #K= 40a294034c886787960f4c751b196da9 FT_test_classes.3
-#K= 38e813e9025107ac3e24226b8d487a92 FT_classmap_inheritance.1
-#K= 9b82b12a35ad98ef26183db15071f70e FT_classmap_inheritance.2
+#K= 3af642df3771be04ab4428ce7f6d53a2 FT_classmap_inheritance.1
+#K= d6135911e9cff22d701ad0c3fdbb1c35 FT_classmap_inheritance.2
 #K= d4937472530af6fdcb0a2440d4a366ea FT_classmap_inheritance.3
 #K= fea6f925b829f75a5b2d4e837738fa12 FT_classmap_inheritance.4
 #K= 7e92245008439ee79fe2460aeaa16a9b FT_classmap_inheritance.5

-- 
2.55.0


Reply via email to