From nobody Thu Apr 2 20:20:49 2026 Received: from mail-oa1-f53.google.com (mail-oa1-f53.google.com [209.85.160.53]) (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 A3C0E425CC9 for ; Thu, 26 Mar 2026 18:55:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774551322; cv=none; b=EfBHrorxNN0X0VUHusIqRk6mqdKjIoVVimSEnTgZoStL+P835qWwzqZ83JuzJdlgCIryn51Eb3+Qbv8QkHJsWQqkYbzWHjUiFK+es8E38hb03IfASC07wUYNjr7/PJw7a/Ql2JxtxlyjpUDBsPBTYC4HO5LH1rTi8cI0AbepbB8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774551322; c=relaxed/simple; bh=ffYea5QwG/vcMRALURtxHTtwAd5CEp54EzMGG1f4Al8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RP61e8n5ZMgPKlJH7tMo00duui7d+k3CM3F3bWHftOp8mcUFbT44fqy9DagNJcEnVwnno5K1LXeZlcK4yOg91JsXjZLqFnAw+de7jVpePogA5J5G/N0lp8Jy9VX6SQgtUpNA8PQz3FnUqvIwczWKxj18i90frroACHR7jFcCEEY= 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=mIiBLeQx; arc=none smtp.client-ip=209.85.160.53 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="mIiBLeQx" Received: by mail-oa1-f53.google.com with SMTP id 586e51a60fabf-417571c6083so746791fac.2 for ; Thu, 26 Mar 2026 11:55:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774551319; x=1775156119; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=J42RjL3XW7iYvn8zHG5csoL/H2RaSYA/baR8+V+mULo=; b=mIiBLeQxGXxky+y+0rcsaoZI84iyumUZjjqR6XhcIyQuqdYoV/vrdyRCL4YYNFHfxL p0mAo/as21/IFkZ+oNBzlfrcBU+/yieMVuABwtMcjbNnsNiStaSr/A+OnqSHCJCMn68H +ufUVPRVvXBlN2/g5K+6KvUB9Z7RwUVWSk+4zbfXBSQsPOQMT9vbZgj9idKXJvPM8lRb p6Y+LJGMvTbsuFHPyem9JSH4qiZm4QPW+4zOa9ZckjaQqACRQS934JQnAGCH7ikBn8ol W3HmIx2pkbGoo7PScT97eo/jszDAN3BhBPKr2aSTJ7oRizD+aFDNPpTL5UD6q2pLH5Aw GzwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774551319; x=1775156119; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=J42RjL3XW7iYvn8zHG5csoL/H2RaSYA/baR8+V+mULo=; b=RRcSJFsngZnOHa8VFlUzHSgSD5E/p+djV4BQUiBJVpycwi55Z9nnsorydZJoHHgqR8 41Y+Ti43qiAKTczxONP9hApJ2RY/++8v5iN2ApR3sYj8hF1uQRCaSsyyL+XYXviD/JoQ aVo7gxEjU1rbGUvHDL0Etof5K0IPg6QeH1/hUFNa9SxeD+WTeakzhHR0d0Be8FlCzESj RCPJaREgp/v8JdzUxsbRaiGsg3GVhsjkfAF2iHA9EtkdrgXLquPgjwI8TIqNN41IKzK8 HXhJGP3DqiFSNXy1PX+9kS3jHrF6QLwWWVv/D8Mc4VoNa11WgpGOZ53mQC9LS9tm1Kqo xvBw== X-Gm-Message-State: AOJu0YyHFH9Dfdf05pTgrO8Ut9BL8KkkT5IgmsDlWkXw8WBTcaOH+rs7 kfUjGDh4ddrpIpr2YTh9CU6rS2GGUmO/D+entIwVSyCktR0/rAkfaNyhQr9D9Q== X-Gm-Gg: ATEYQzz/fbaqVZnYauxFGKSR7RZp+bp5lOJXsgQW6iht+GSheP1uWv1GSgn+Qoy/1Yz y+8/GqVBDn9ucIIK1FvuEmpHjbjmw+oxCEef64ZPqy368ulAeGbltzpOiqxvxBvDGpidTCAtT6w iXqgNhtzoWc1yRL94jVUpSlQLkvYzF0qKGPuDKK714g1NE25xExysqwY9K3WOUZ12XtCqc6E6ml 5Pw4Qn6TfvWtgqpg9E9qkCaApVkvU4D0NCsdgzI2v9gHtZX17puaeWeJULtX9T0Le3g+GMYN+Jo emhNCQPTONVw97YfqhzzU6UaM6AwqPVM2aDp+o8XzF29oE3Mr9qofvh0pLCh4Ba0pmGJ68U+QPj tjqRbRcK94fZpgp0yEUkIfaJ+MT46YjpmlzzIKNqGljBkFwWwgXfnMj3oFwmPkDFrjo72HBXxRu QwfD3pL1yPD6vJddFoqS2pGkpndGIA79rApWxWiJlcWLGVuxZt X-Received: by 2002:a05:6870:d8cc:b0:417:c2d1:fc36 with SMTP id 586e51a60fabf-41ca7051971mr4708391fac.33.1774551319350; Thu, 26 Mar 2026 11:55:19 -0700 (PDT) Received: from frodo (c-98-38-17-99.hsd1.co.comcast.net. [98.38.17.99]) by smtp.googlemail.com with ESMTPSA id 586e51a60fabf-41cc7760c08sm3075171fac.4.2026.03.26.11.55.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Mar 2026 11:55:18 -0700 (PDT) From: Jim Cromie To: linux-kernel@vger.kernel.org, airlied@gmail.com, simona@ffwll.ch, jbaron@akamai.com, gregkh@linuxfoundation.org Cc: jim.cromie@gmail.com, mripard@kernel.org, tzimmermann@suse.de, maarten.lankhorst@linux.intel.com, jani.nikula@intel.com, ville.syrjala@linux.intel.com, christian.koenig@amd.com, matthew.auld@intel.com, arunpravin.paneerselvam@amd.com, louis.chauvet@bootlin.com, skhan@linuxfoundation.org, pmladek@suse.com, ukaszb@chromium.org, dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org, amd-gfx@lists.freedesktop.org Subject: [PATCH v12 33/69] dyndbg: resolve "protection" of class'd pr_debug Date: Thu, 26 Mar 2026 12:53:37 -0600 Message-ID: <20260326185413.1205870-34-jim.cromie@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260326185413.1205870-1-jim.cromie@gmail.com> References: <20260326185413.1205870-1-jim.cromie@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" classmap-v1 code protected class'd pr_debugs from unintended changes by unclassed/_DFLT queries: # - to declutter examples: alias ddcmd=3D'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=3DVAL 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. Reviewed-by: Louis Chauvet Signed-off-by: Jim Cromie --- -v12 minor fixup after squashing subsequent commits to previous ones --- include/linux/dynamic_debug.h | 14 ++-- lib/dynamic_debug.c | 137 ++++++++++++++++++++++++++-------- 2 files changed, 115 insertions(+), 36 deletions(-) diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 1cae9a2f32d7..1b401f398a3c 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -76,6 +76,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; @@ -281,7 +282,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 unsigned long _name##_bvec; \ @@ -294,10 +300,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 6430f77c7b9c..66879a40b822 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -206,6 +206,55 @@ ddebug_find_valid_class(struct _ddebug_info const *di,= const char *query_class, return NULL; } =20 +static bool ddebug_class_map_in_range(const int class_id, const struct _dd= ebug_class_map *map) +{ + return (class_id >=3D map->base && + class_id < map->base + map->length); +} + +static bool ddebug_class_user_in_range(const int class_id, const struct _d= debug_class_user *user) +{ + return ddebug_class_map_in_range(class_id - user->offset, user->map); +} + +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 @@ -214,11 +263,10 @@ ddebug_find_valid_class(struct _ddebug_info const *di= , const char *query_class, */ 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 !=3D valid_class) - return false; + struct _ddebug_class_map *site_map; =20 /* match against the source filename */ if (query->filename && @@ -255,7 +303,28 @@ static bool ddebug_match_desc(const struct ddebug_quer= y *query, dp->lineno > query->last_lineno) return false; =20 - 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 =3D=3D selected_class); + } + /* query class __DEFAULT__ by omission. */ + if (dp->class_id =3D=3D _DPRINTK_CLASS_DFLT) { + /* un-classed site */ + return true; + } + /* site is class'd */ + site_map =3D 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); } =20 static int ddebug_change(const struct ddebug_query *query, struct flag_set= tings *modifiers) @@ -265,33 +334,31 @@ static int ddebug_change(const struct ddebug_query *q= uery, struct flag_settings unsigned int newflags; unsigned int nfound =3D 0; struct flagsbuf fbuf, nbuf; - struct _ddebug_class_map *map =3D NULL; - int valid_class; + int selected_class; =20 /* search for matching ddebugs */ mutex_lock(&ddebug_lock); list_for_each_entry(dt, &ddebug_tables, link) { struct _ddebug_info *di =3D &dt->info; + struct _ddebug_class_map *mods_map; =20 /* match against the module name */ if (query->module && !match_wildcard(query->module, di->mod_name)) continue; =20 + selected_class =3D _DPRINTK_CLASS_DFLT; if (query->class_string) { - map =3D ddebug_find_valid_class(&dt->info, query->class_string, - &valid_class); - if (!map) + mods_map =3D 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 =3D _DPRINTK_CLASS_DFLT; } =20 for (i =3D 0; i < di->descs.len; i++) { struct _ddebug *dp =3D &di->descs.start[i]; =20 - if (!ddebug_match_desc(query, dp, valid_class)) + if (!ddebug_match_desc(query, dp, di, selected_class)) continue; =20 nfound++; @@ -656,6 +723,7 @@ static int ddebug_exec_queries(char *query, const char = *modname) } =20 /* apply a new class-param setting */ + static int ddebug_apply_class_bitmap(const struct _ddebug_class_param *dcp, const unsigned long *new_bits, const unsigned long old_bits, @@ -1107,17 +1175,6 @@ static void *ddebug_proc_next(struct seq_file *m, vo= id *p, loff_t *pos) return dp; } =20 -static bool ddebug_class_map_in_range(const int class_id, const struct _dd= ebug_class_map *map) -{ - return (class_id >=3D map->base && - class_id < map->base + map->length); -} - -static bool ddebug_class_user_in_range(const int class_id, const struct _d= debug_class_user *user) -{ - return ddebug_class_map_in_range(class_id - user->offset, user->map); -} - static const char *ddebug_class_name(struct _ddebug_info *di, struct _ddeb= ug *dp) { struct _ddebug_class_map *map; @@ -1242,25 +1299,36 @@ static void ddebug_sync_classbits(const struct kern= el_param *kp, const char *mod } } =20 -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; =20 if (kp->ops !=3D ¶m_ops_dyndbg_classes) - return; + return NULL; =20 dcp =3D (struct _ddebug_class_param *)kp->arg; =20 - if (map =3D=3D dcp->map) { + return (map =3D=3D 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 =3D ddebug_get_classmap_kparam(kp, map); + + if (dcp) { + map->controlling_param =3D dcp; v2pr_info(" kp:%s.%s =3D0x%lx", mod_name, kp->name, *dcp->bits); vpr_cm_info(map, " %s maps ", mod_name); ddebug_sync_classbits(kp, mod_name); } } =20 -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; #if IS_ENABLED(CONFIG_MODULES) @@ -1280,6 +1348,13 @@ static void ddebug_apply_params(const struct _ddebug= _class_map *cm, const char * } } =20 +/* + * 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; --=20 2.53.0