* [PATCH 1/5] jump_label: add queueing API for batched static key updates
2026-04-03 14:23 [PATCH 0/5] jump_label: mitigate dynamic-debug induced IPI storms Jim Cromie
@ 2026-04-03 14:23 ` Jim Cromie
2026-04-03 14:23 ` [PATCH 2/5] x86/alternative.c: sort text-pokes before flushing the queue Jim Cromie
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jim Cromie @ 2026-04-03 14:23 UTC (permalink / raw)
To: peterz, gregkh
Cc: jpoimboe, jbaron, aliceryhl, rostedt, ardb, x86, linux-kernel,
Jim Cromie
Introduce a new queueing API for static keys to allow batching of
instruction updates. This is provided for subsystems like
dynamic-debug, which may toggle thousands of static keys in a single
operation. Currently, this suffers from (N)$ overhead due to repeated
machine-wide `stop_machine` or `text_poke_sync` synchronizations.
The new API includes:
- static_key_enable_queued(key)
- static_key_disable_queued(key)
- static_key_apply_queued() (the global synchronization barrier)
- Corresponding static_branch_*_queued(x) macros.
The _queued functionality is achieved by adding a 'bool apply'
parameter to 3 existing midlayer static functions, and using it to
decide whether or not to call arch_jump_label_transform_apply(). By
not explicitly calling it, we allow the transform queue to fill 1st.
This amortizes the cost of the IPI sync to 1/N (256 on x86).
1. __jump_label_update(struct static_key *key, bool apply)
this implements the if (apply) arch_jump_label_transform_apply();
2. __jump_label_mod_update(struct static_key *key, bool apply)
extends _queued feature to modules' static-keys
3. rename jump_label_update(struct static_key *key)
to __jump_label_update_key(struct static_key *key, bool apply)
and wrap it with 1-liners:
jump_label_update() & jump_label_update_queued()
On architectures supporting HAVE_JUMP_LABEL_BATCH, the _queued variants
lazily fill the architecture's patch queue.
It is safe to mix queued and immediate updates; any non-queued
static_key_enable/disable call will effectively "flush" any previously
queued changes as a side effect. Or just call static_key_apply_queued().
On architectures that do not need a patch queue (e.g., those with atomic
patching) or that do not support batching, the API transparently falls back
to immediate patching.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/linux/jump_label.h | 18 +++++++
kernel/jump_label.c | 104 +++++++++++++++++++++++++++++--------
2 files changed, 100 insertions(+), 22 deletions(-)
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index fdb79dd1ebd8..283ee9360026 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -234,6 +234,9 @@ extern void static_key_slow_dec_cpuslocked(struct static_key *key);
extern int static_key_count(struct static_key *key);
extern void static_key_enable(struct static_key *key);
extern void static_key_disable(struct static_key *key);
+extern void static_key_enable_queued(struct static_key *key);
+extern void static_key_disable_queued(struct static_key *key);
+extern void static_key_apply_queued(void);
extern void static_key_enable_cpuslocked(struct static_key *key);
extern void static_key_disable_cpuslocked(struct static_key *key);
extern enum jump_label_type jump_label_init_type(struct jump_entry *entry);
@@ -340,6 +343,18 @@ static inline void static_key_disable(struct static_key *key)
atomic_set(&key->enabled, 0);
}
+static inline void static_key_enable_queued(struct static_key *key)
+{
+ static_key_enable(key);
+}
+
+static inline void static_key_disable_queued(struct static_key *key)
+{
+ static_key_disable(key);
+}
+
+static inline void static_key_apply_queued(void) {}
+
#define static_key_enable_cpuslocked(k) static_key_enable((k))
#define static_key_disable_cpuslocked(k) static_key_disable((k))
@@ -535,6 +550,9 @@ extern bool ____wrong_branch_error(void);
#define static_branch_enable(x) static_key_enable(&(x)->key)
#define static_branch_disable(x) static_key_disable(&(x)->key)
+#define static_branch_enable_queued(x) static_key_enable_queued(&(x)->key)
+#define static_branch_disable_queued(x) static_key_disable_queued(&(x)->key)
+#define static_branch_apply_queued() static_key_apply_queued()
#define static_branch_enable_cpuslocked(x) static_key_enable_cpuslocked(&(x)->key)
#define static_branch_disable_cpuslocked(x) static_key_disable_cpuslocked(&(x)->key)
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 7cb19e601426..9826aefd77e5 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -91,6 +91,7 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
}
static void jump_label_update(struct static_key *key);
+static void jump_label_update_queued(struct static_key *key);
/*
* There are similar definitions for the !CONFIG_JUMP_LABEL case in jump_label.h.
@@ -250,10 +251,57 @@ void static_key_disable(struct static_key *key)
}
EXPORT_SYMBOL_GPL(static_key_disable);
+void static_key_enable_queued(struct static_key *key)
+{
+ STATIC_KEY_CHECK_USE(key);
+
+ if (atomic_read(&key->enabled) > 0) {
+ /*
+ * already enabled, don't act.
+ * warn if static-key-inc was used.
+ */
+ WARN_ON_ONCE(atomic_read(&key->enabled) != 1);
+ return;
+ }
+
+ scoped_guard(jump_label_lock) {
+ if (atomic_read(&key->enabled) == 0) {
+ /*
+ * set transitional value, update,
+ * then set stable enabled state
+ */
+ atomic_set(&key->enabled, -1);
+ jump_label_update_queued(key);
+ atomic_set_release(&key->enabled, 1);
+ }
+ }
+}
+
+void static_key_disable_queued(struct static_key *key)
+{
+ STATIC_KEY_CHECK_USE(key);
+
+ if (atomic_read(&key->enabled) != 1) {
+ /*
+ * not simply enabled; is disabled, inc-enabled, or
+ * in transition. don't act. warn if inc-enabled.
+ */
+ WARN_ON_ONCE(atomic_read(&key->enabled) != 0);
+ return;
+ }
+
+ scoped_guard(jump_label_lock) {
+ /*
+ * update if simply enabled. dont act if in transition.
+ */
+ if (atomic_cmpxchg(&key->enabled, 1, 0) == 1)
+ jump_label_update_queued(key);
+ }
+}
+
static bool static_key_dec_not_one(struct static_key *key)
{
int v;
-
/*
* Go into the slow path if key::enabled is less than or equal than
* one. One is valid to shut down the key, anything less than one
@@ -488,28 +536,16 @@ static bool jump_label_can_update(struct jump_entry *entry, bool init)
return true;
}
-#ifndef HAVE_JUMP_LABEL_BATCH
static void __jump_label_update(struct static_key *key,
struct jump_entry *entry,
struct jump_entry *stop,
- bool init)
+ bool init, bool apply)
{
for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
- if (jump_label_can_update(entry, init))
- arch_jump_label_transform(entry, jump_label_type(entry));
- }
-}
-#else
-static void __jump_label_update(struct static_key *key,
- struct jump_entry *entry,
- struct jump_entry *stop,
- bool init)
-{
- for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
-
if (!jump_label_can_update(entry, init))
continue;
+#ifdef HAVE_JUMP_LABEL_BATCH
if (!arch_jump_label_transform_queue(entry, jump_label_type(entry))) {
/*
* Queue is full: Apply the current queue and try again.
@@ -517,10 +553,24 @@ static void __jump_label_update(struct static_key *key,
arch_jump_label_transform_apply();
BUG_ON(!arch_jump_label_transform_queue(entry, jump_label_type(entry)));
}
+#else
+ arch_jump_label_transform(entry, jump_label_type(entry));
+#endif
}
- arch_jump_label_transform_apply();
+#ifdef HAVE_JUMP_LABEL_BATCH
+ if (apply)
+ arch_jump_label_transform_apply();
+#endif
}
+
+void static_key_apply_queued(void)
+{
+#ifdef HAVE_JUMP_LABEL_BATCH
+ jump_label_lock();
+ arch_jump_label_transform_apply();
+ jump_label_unlock();
#endif
+}
void __init jump_label_init(void)
{
@@ -671,7 +721,7 @@ static int __jump_label_mod_text_reserved(void *start, void *end)
return ret;
}
-static void __jump_label_mod_update(struct static_key *key)
+static void __jump_label_mod_update(struct static_key *key, bool apply)
{
struct static_key_mod *mod;
@@ -692,7 +742,7 @@ static void __jump_label_mod_update(struct static_key *key)
else
stop = m->jump_entries + m->num_jump_entries;
__jump_label_update(key, mod->entries, stop,
- m && m->state == MODULE_STATE_COMING);
+ m && m->state == MODULE_STATE_COMING, apply);
}
}
@@ -762,7 +812,7 @@ static int jump_label_add_module(struct module *mod)
/* Only update if we've changed from our initial state */
do_poke:
if (jump_label_type(iter) != jump_label_init_type(iter))
- __jump_label_update(key, iter, iter_stop, true);
+ __jump_label_update(key, iter, iter_stop, true, true);
}
return 0;
@@ -892,7 +942,7 @@ int jump_label_text_reserved(void *start, void *end)
return ret;
}
-static void jump_label_update(struct static_key *key)
+static void jump_label_update_key(struct static_key *key, bool apply)
{
struct jump_entry *stop = __stop___jump_table;
bool init = system_state < SYSTEM_RUNNING;
@@ -901,7 +951,7 @@ static void jump_label_update(struct static_key *key)
struct module *mod;
if (static_key_linked(key)) {
- __jump_label_mod_update(key);
+ __jump_label_mod_update(key, apply);
return;
}
@@ -916,7 +966,17 @@ static void jump_label_update(struct static_key *key)
entry = static_key_entries(key);
/* if there are no users, entry can be NULL */
if (entry)
- __jump_label_update(key, entry, stop, init);
+ __jump_label_update(key, entry, stop, init, apply);
+}
+
+static void jump_label_update(struct static_key *key)
+{
+ jump_label_update_key(key, true);
+}
+
+static void jump_label_update_queued(struct static_key *key)
+{
+ jump_label_update_key(key, false);
}
#ifdef CONFIG_STATIC_KEYS_SELFTEST
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/5] x86/alternative.c: sort text-pokes before flushing the queue
2026-04-03 14:23 [PATCH 0/5] jump_label: mitigate dynamic-debug induced IPI storms Jim Cromie
2026-04-03 14:23 ` [PATCH 1/5] jump_label: add queueing API for batched static key updates Jim Cromie
@ 2026-04-03 14:23 ` Jim Cromie
2026-04-03 14:23 ` [PATCH 3/5] dyndbg: use static-key queueing API in dynamic-debug Jim Cromie
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jim Cromie @ 2026-04-03 14:23 UTC (permalink / raw)
To: peterz, gregkh
Cc: jpoimboe, jbaron, aliceryhl, rostedt, ardb, x86, linux-kernel,
Jim Cromie
Until now, x86 can queue jump_label text-pokes as long as the
poke-addr is monotonically increasing, but flushes the queue when a
new poke-addr is less than the previous.
Dynamic-debug now uses the queued static-key API, but the advantage is
limited; we see a ~2x reduction in IPIs. Although the pr_debug
descriptors are ordered, the patch-address in them are not; about 1/2
violate the ordering constraint.
So this patch drops that requirement, and sorts the text-pokes by
their address before applying them. Doing so lets us fill the queue
before sorting then flushing, giving a dramatic ~125x reduction in
IPIs over the traditional single IPI per pr_debug.
Other arches don't need a queue, and so have nothing to sort.
#> dd_ipis
[ 23.100381] dyndbg: query 0: "module !virtio* +p " mod:*
[ 23.103432] dyndbg: query 1: "-p" mod:*
Delta-CAL (IPI): 242
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
arch/x86/kernel/alternative.c | 42 ++++++++++++++++-------------------
lib/dynamic_debug.c | 1 +
2 files changed, 20 insertions(+), 23 deletions(-)
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index e87da25d1236..92987954b8aa 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -2,6 +2,7 @@
#define pr_fmt(fmt) "SMP alternatives: " fmt
#include <linux/mmu_context.h>
+#include <linux/sort.h>
#include <linux/perf_event.h>
#include <linux/vmalloc.h>
#include <linux/memory.h>
@@ -2823,6 +2824,18 @@ static __always_inline int patch_cmp(const void *tpl_a, const void *tpl_b)
return 0;
}
+static int text_poke_loc_cmp(const void *a, const void *b)
+{
+ const struct smp_text_poke_loc *tpl_a = a;
+ const struct smp_text_poke_loc *tpl_b = b;
+
+ if (tpl_a->rel_addr < tpl_b->rel_addr)
+ return -1;
+ if (tpl_a->rel_addr > tpl_b->rel_addr)
+ return 1;
+ return 0;
+}
+
noinstr int smp_text_poke_int3_handler(struct pt_regs *regs)
{
struct smp_text_poke_loc *tpl;
@@ -2935,6 +2948,10 @@ void smp_text_poke_batch_finish(void)
if (!text_poke_array.nr_entries)
return;
+ if (text_poke_array.nr_entries > 1)
+ sort(text_poke_array.vec, text_poke_array.nr_entries,
+ sizeof(struct smp_text_poke_loc), text_poke_loc_cmp, NULL);
+
lockdep_assert_held(&text_mutex);
/*
@@ -3151,28 +3168,6 @@ static void __smp_text_poke_batch_add(void *addr, const void *opcode, size_t len
}
}
-/*
- * We hard rely on the text_poke_array.vec being ordered; ensure this is so by flushing
- * early if needed.
- */
-static bool text_poke_addr_ordered(void *addr)
-{
- WARN_ON_ONCE(!addr);
-
- if (!text_poke_array.nr_entries)
- return true;
-
- /*
- * If the last current entry's address is higher than the
- * new entry's address we'd like to add, then ordering
- * is violated and we must first flush all pending patching
- * requests:
- */
- if (text_poke_addr(text_poke_array.vec + text_poke_array.nr_entries-1) > addr)
- return false;
-
- return true;
-}
/**
* smp_text_poke_batch_add() -- update instruction on live kernel on SMP, batched
@@ -3189,8 +3184,9 @@ static bool text_poke_addr_ordered(void *addr)
*/
void __ref smp_text_poke_batch_add(void *addr, const void *opcode, size_t len, const void *emulate)
{
- if (text_poke_array.nr_entries == TEXT_POKE_ARRAY_MAX || !text_poke_addr_ordered(addr))
+ if (text_poke_array.nr_entries == TEXT_POKE_ARRAY_MAX)
smp_text_poke_batch_finish();
+
__smp_text_poke_batch_add(addr, opcode, len, emulate);
}
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 18a71a9108d3..b5060749464e 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -264,6 +264,7 @@ static int ddebug_change(const struct ddebug_query *query,
}
}
mutex_unlock(&ddebug_lock);
+ v2pr_info("applied %d queued updates to sites in total\n", nfound);
if (!nfound && verbose)
pr_info("no matches for query\n");
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/5] dyndbg: use static-key queueing API in dynamic-debug
2026-04-03 14:23 [PATCH 0/5] jump_label: mitigate dynamic-debug induced IPI storms Jim Cromie
2026-04-03 14:23 ` [PATCH 1/5] jump_label: add queueing API for batched static key updates Jim Cromie
2026-04-03 14:23 ` [PATCH 2/5] x86/alternative.c: sort text-pokes before flushing the queue Jim Cromie
@ 2026-04-03 14:23 ` Jim Cromie
2026-04-03 14:24 ` [PATCH 4/5] dyndbg: factor ddebug_match_desc out from ddebug_change (for demonstration only) Jim Cromie
2026-04-03 14:24 ` [PATCH 5/5] lib/dynamic_debug: add negation support to queries Jim Cromie
4 siblings, 0 replies; 6+ messages in thread
From: Jim Cromie @ 2026-04-03 14:23 UTC (permalink / raw)
To: peterz, gregkh
Cc: jpoimboe, jbaron, aliceryhl, rostedt, ardb, x86, linux-kernel,
Jim Cromie
Use the new static-key queueing API in dynamic-debug to reduce IPIs.
this replaces the 2 calls to static_branch_{enable.disable}() in
ddebug_change() with their _queued() counterparts, and adds a call to
static_branch_apply_queued() at the bottom.
This gives a theoretical reduction of IPIs up to 256x, and a practical
reduction by 120-180x.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
lib/dynamic_debug.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index b5060749464e..9bee7dde5148 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -250,9 +250,9 @@ static int ddebug_change(const struct ddebug_query *query,
#ifdef CONFIG_JUMP_LABEL
if (dp->flags & _DPRINTK_FLAGS_PRINT) {
if (!(newflags & _DPRINTK_FLAGS_PRINT))
- static_branch_disable(&dp->key.dd_key_true);
+ static_branch_disable_queued(&dp->key.dd_key_true);
} else if (newflags & _DPRINTK_FLAGS_PRINT) {
- static_branch_enable(&dp->key.dd_key_true);
+ static_branch_enable_queued(&dp->key.dd_key_true);
}
#endif
v4pr_info("changed %s:%d [%s]%s %s => %s\n",
@@ -263,6 +263,7 @@ static int ddebug_change(const struct ddebug_query *query,
dp->flags = newflags;
}
}
+ static_branch_apply_queued();
mutex_unlock(&ddebug_lock);
v2pr_info("applied %d queued updates to sites in total\n", nfound);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/5] dyndbg: factor ddebug_match_desc out from ddebug_change (for demonstration only)
2026-04-03 14:23 [PATCH 0/5] jump_label: mitigate dynamic-debug induced IPI storms Jim Cromie
` (2 preceding siblings ...)
2026-04-03 14:23 ` [PATCH 3/5] dyndbg: use static-key queueing API in dynamic-debug Jim Cromie
@ 2026-04-03 14:24 ` Jim Cromie
2026-04-03 14:24 ` [PATCH 5/5] lib/dynamic_debug: add negation support to queries Jim Cromie
4 siblings, 0 replies; 6+ messages in thread
From: Jim Cromie @ 2026-04-03 14:24 UTC (permalink / raw)
To: peterz, gregkh
Cc: jpoimboe, jbaron, aliceryhl, rostedt, ardb, x86, linux-kernel,
Jim Cromie, Louis Chauvet
ddebug_change() is a big (~100 lines) function with a nested for loop.
The outer loop walks the per-module ddebug_tables list, and does
module stuff: it filters on a query's "module FOO*" and "class BAR",
failures here skip the entire inner loop.
The inner loop (60 lines) scans a module's descriptors. It starts
with a long block of filters on function, line, format, and the
validated "BAR" class (or the legacy/_DPRINTK_CLASS_DFLT).
These filters "continue" past pr_debugs that don't match the query
criteria, before it falls through the code below that counts matches,
then adjusts the flags and static-keys. This is unnecessarily hard to
think about.
So move the per-descriptor filter-block into a boolean function:
ddebug_match_desc(desc), and change each "continue" to "return false".
This puts a clear interface in place, so any future changes are either
inside, outside, or across this interface.
also fix checkpatch complaints about spaces and braces.
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
lib/dynamic_debug.c | 83 +++++++++++++++++++++++++--------------------
1 file changed, 47 insertions(+), 36 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 9bee7dde5148..0b13810b03db 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -172,6 +172,52 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
* callsites, normally the same as number of changes. If verbose,
* logs the changes. Takes ddebug_lock.
*/
+static bool ddebug_match_desc(const struct ddebug_query *query,
+ struct _ddebug *dp,
+ int valid_class)
+{
+ /* match site against query-class */
+ if (dp->class_id != valid_class)
+ return false;
+
+ /* match against the source filename */
+ if (query->filename &&
+ !match_wildcard(query->filename, dp->filename) &&
+ !match_wildcard(query->filename,
+ kbasename(dp->filename)) &&
+ !match_wildcard(query->filename,
+ trim_prefix(dp->filename)))
+ return false;
+
+ /* match against the function */
+ if (query->function &&
+ !match_wildcard(query->function, dp->function))
+ return false;
+
+ /* match against the format */
+ if (query->format) {
+ if (*query->format == '^') {
+ char *p;
+ /* anchored search. match must be at beginning */
+ p = strstr(dp->format, query->format + 1);
+ if (p != dp->format)
+ return false;
+ } else if (!strstr(dp->format, query->format)) {
+ return false;
+ }
+ }
+
+ /* match against the line number range */
+ if (query->first_lineno &&
+ dp->lineno < query->first_lineno)
+ return false;
+ if (query->last_lineno &&
+ dp->lineno > query->last_lineno)
+ return false;
+
+ return true;
+}
+
static int ddebug_change(const struct ddebug_query *query,
struct flag_settings *modifiers)
{
@@ -204,42 +250,7 @@ static int ddebug_change(const struct ddebug_query *query,
for (i = 0; i < dt->num_ddebugs; i++) {
struct _ddebug *dp = &dt->ddebugs[i];
- /* match site against query-class */
- if (dp->class_id != valid_class)
- continue;
-
- /* match against the source filename */
- if (query->filename &&
- !match_wildcard(query->filename, dp->filename) &&
- !match_wildcard(query->filename,
- kbasename(dp->filename)) &&
- !match_wildcard(query->filename,
- trim_prefix(dp->filename)))
- continue;
-
- /* match against the function */
- if (query->function &&
- !match_wildcard(query->function, dp->function))
- continue;
-
- /* match against the format */
- if (query->format) {
- if (*query->format == '^') {
- char *p;
- /* anchored search. match must be at beginning */
- p = strstr(dp->format, query->format+1);
- if (p != dp->format)
- continue;
- } else if (!strstr(dp->format, query->format))
- continue;
- }
-
- /* match against the line number range */
- if (query->first_lineno &&
- dp->lineno < query->first_lineno)
- continue;
- if (query->last_lineno &&
- dp->lineno > query->last_lineno)
+ if (!ddebug_match_desc(query, dp, valid_class))
continue;
nfound++;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 5/5] lib/dynamic_debug: add negation support to queries
2026-04-03 14:23 [PATCH 0/5] jump_label: mitigate dynamic-debug induced IPI storms Jim Cromie
` (3 preceding siblings ...)
2026-04-03 14:24 ` [PATCH 4/5] dyndbg: factor ddebug_match_desc out from ddebug_change (for demonstration only) Jim Cromie
@ 2026-04-03 14:24 ` Jim Cromie
4 siblings, 0 replies; 6+ messages in thread
From: Jim Cromie @ 2026-04-03 14:24 UTC (permalink / raw)
To: peterz, gregkh
Cc: jpoimboe, jbaron, aliceryhl, rostedt, ardb, x86, linux-kernel,
Jim Cromie
This allow users to invert the selection of most keywords, excluding
lineno and class. For example:
echo "!module virtio* +p" > /proc/dynamic_debug/control
When testing a kernel running in virtme-ng, this cmd prevents flooding
the logs with virtio activity, and spoiling the the test scenario.
NOTE:
This is convenient, but not perfect; it cannot also avoid flooding
from pr_debugs in serial_core or other potential sources.
A more robust command is:
echo "!module virtio* +p % module serial -p" > /proc/dynamic_debug/control
It also "interferes" with evolution of the grammar; particularly
around and/or combinations. For example this could become meaningful,
though perhaps inadvisable:
echo " !module virtio* !module serial +p "
lineno is currently excluded, pending review of its usefulness vs
ambiguities, for example " !lineno 1-20 " has straightforward meaning.
!class FOO is unlikely to be justifiable; it defeats the "protection
thru specificity" that classes are designed for.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
lib/dynamic_debug.c | 81 +++++++++++++++++++++++++++++++--------------
1 file changed, 57 insertions(+), 24 deletions(-)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 0b13810b03db..3addfa06e54d 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -58,6 +58,10 @@ struct ddebug_query {
const char *format;
const char *class_string;
unsigned int first_lineno, last_lineno;
+ unsigned int filename_neg:1;
+ unsigned int module_neg:1;
+ unsigned int function_neg:1;
+ unsigned int format_neg:1;
};
struct ddebug_iter {
@@ -139,11 +143,12 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
fmtlen--;
}
- v3pr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u class=%s\n",
+ v3pr_info("%s: func%s=\"%s\" file%s=\"%s\" module%s=\"%s\" format%s=\"%.*s\" lineno=%u-%u class=%s\n",
msg,
- query->function ?: "",
- query->filename ?: "",
- query->module ?: "",
+ query->function_neg ? "!" : "", query->function ?: "",
+ query->filename_neg ? "!" : "", query->filename ?: "",
+ query->module_neg ? "!" : "", query->module ?: "",
+ query->format_neg ? "!" : "",
fmtlen, query->format ?: "",
query->first_lineno, query->last_lineno, query->class_string);
}
@@ -176,35 +181,38 @@ static bool ddebug_match_desc(const struct ddebug_query *query,
struct _ddebug *dp,
int valid_class)
{
+ bool match;
+
/* match site against query-class */
if (dp->class_id != valid_class)
return false;
/* match against the source filename */
- if (query->filename &&
- !match_wildcard(query->filename, dp->filename) &&
- !match_wildcard(query->filename,
- kbasename(dp->filename)) &&
- !match_wildcard(query->filename,
- trim_prefix(dp->filename)))
- return false;
+ if (query->filename) {
+ match = match_wildcard(query->filename, dp->filename) ||
+ match_wildcard(query->filename, kbasename(dp->filename)) ||
+ match_wildcard(query->filename, trim_prefix(dp->filename));
+ if (match == query->filename_neg)
+ return false;
+ }
/* match against the function */
- if (query->function &&
- !match_wildcard(query->function, dp->function))
- return false;
+ if (query->function) {
+ match = match_wildcard(query->function, dp->function);
+ if (match == query->function_neg)
+ return false;
+ }
/* match against the format */
if (query->format) {
if (*query->format == '^') {
- char *p;
/* anchored search. match must be at beginning */
- p = strstr(dp->format, query->format + 1);
- if (p != dp->format)
- return false;
- } else if (!strstr(dp->format, query->format)) {
- return false;
+ match = (strstr(dp->format, query->format + 1) == dp->format);
+ } else {
+ match = !!strstr(dp->format, query->format);
}
+ if (match == query->format_neg)
+ return false;
}
/* match against the line number range */
@@ -234,9 +242,11 @@ static int ddebug_change(const struct ddebug_query *query,
list_for_each_entry(dt, &ddebug_tables, link) {
/* match against the module name */
- if (query->module &&
- !match_wildcard(query->module, dt->mod_name))
- continue;
+ if (query->module) {
+ bool match = match_wildcard(query->module, dt->mod_name);
+ if (match == query->module_neg)
+ continue;
+ }
if (query->class_string) {
map = ddebug_find_valid_class(dt, query->class_string, &valid_class);
@@ -399,6 +409,16 @@ static int parse_linerange(struct ddebug_query *query, const char *first)
return 0;
}
+static char *check_neg(char *src, unsigned int *neg)
+{
+ if (*src == '!') {
+ *neg = 1;
+ return src + 1;
+ }
+ *neg = 0;
+ return src;
+}
+
static int check_set(const char **dest, char *src, char *name)
{
int rc = 0;
@@ -441,12 +461,15 @@ static int ddebug_parse_query(char *words[], int nwords,
}
for (i = 0; i < nwords; i += 2) {
- char *keyword = words[i];
+ unsigned int neg;
+ char *keyword = check_neg(words[i], &neg);
char *arg = words[i+1];
if (!strcmp(keyword, "func")) {
+ query->function_neg = neg;
rc = check_set(&query->function, arg, "func");
} else if (!strcmp(keyword, "file")) {
+ query->filename_neg = neg;
if (check_set(&query->filename, arg, "file"))
return -EINVAL;
@@ -464,16 +487,26 @@ static int ddebug_parse_query(char *words[], int nwords,
return -EINVAL;
}
} else if (!strcmp(keyword, "module")) {
+ query->module_neg = neg;
rc = check_set(&query->module, arg, "module");
} else if (!strcmp(keyword, "format")) {
string_unescape_inplace(arg, UNESCAPE_SPACE |
UNESCAPE_OCTAL |
UNESCAPE_SPECIAL);
+ query->format_neg = neg;
rc = check_set(&query->format, arg, "format");
} else if (!strcmp(keyword, "line")) {
+ if (neg) {
+ pr_err("negation not supported for \"line\"\n");
+ return -EINVAL;
+ }
if (parse_linerange(query, arg))
return -EINVAL;
} else if (!strcmp(keyword, "class")) {
+ if (neg) {
+ pr_err("negation not supported for \"class\"\n");
+ return -EINVAL;
+ }
rc = check_set(&query->class_string, arg, "class");
} else {
pr_err("unknown keyword \"%s\"\n", keyword);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread