summaryrefslogtreecommitdiff
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2025-06-10 14:22:17 -0700
committerJakub Kicinski <kuba@kernel.org>2025-06-10 14:22:17 -0700
commita63bea11d45523c7ed9f7b927532c810ad52db36 (patch)
tree4104a911f7acc5a38cd01e1cbb137b2d3e1a8b7a /drivers/net/netconsole.c
parent2c7e4a2663a1ab5a740c59c31991579b6b865a26 (diff)
parent224a6e602fb371b42ba5f854f32191d23b7e140a (diff)
Merge branch 'netconsole-optimize-console-registration-and-improve-testing'
Breno Leitao says: ==================== netconsole: Optimize console registration and improve testing During performance analysis of console subsystem latency, I discovered that netconsole registers console handlers even when no active targets exist. These orphaned console handlers are invoked on every printk() call, get the lock, iterate through empty target lists, and consume CPU cycles without performing any useful work. This patch series addresses the inefficiency by: 1. Implementing dynamic console registration/unregistration based on target availability, ensuring console handlers are only active when needed 2. Adding automatic cleanup of unused console registrations when targets are disabled or removed 3. Extending the selftest suite to cover non-extended console format, which was previously untested The optimization reduces printk() overhead by eliminating unnecessary function calls and list traversals when netconsole targets are not configured, improving overall system performance during heavy logging scenarios. v2: https://lore.kernel.org/20250602-netcons_ext-v2-0-ef88d999326d@debian.org v1: https://lore.kernel.org/20250528-netcons_ext-v1-1-69f71e404e00@debian.org ==================== Link: https://patch.msgid.link/20250609-netcons_ext-v3-0-5336fa670326@debian.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c67
1 files changed, 58 insertions, 9 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 4289ccd3e41b..21077aff061c 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -86,10 +86,10 @@ static DEFINE_SPINLOCK(target_list_lock);
static DEFINE_MUTEX(target_cleanup_list_lock);
/*
- * Console driver for extended netconsoles. Registered on the first use to
- * avoid unnecessarily enabling ext message formatting.
+ * Console driver for netconsoles. Register only consoles that have
+ * an associated target of the same type.
*/
-static struct console netconsole_ext;
+static struct console netconsole_ext, netconsole;
struct netconsole_target_stats {
u64_stats_t xmit_drop_count;
@@ -97,6 +97,11 @@ struct netconsole_target_stats {
struct u64_stats_sync syncp;
};
+enum console_type {
+ CONS_BASIC = BIT(0),
+ CONS_EXTENDED = BIT(1),
+};
+
/* Features enabled in sysdata. Contrary to userdata, this data is populated by
* the kernel. The fields are designed as bitwise flags, allowing multiple
* features to be set in sysdata_fields.
@@ -455,6 +460,33 @@ static ssize_t sysdata_release_enabled_show(struct config_item *item,
return sysfs_emit(buf, "%d\n", release_enabled);
}
+/* Iterate in the list of target, and make sure we don't have any console
+ * register without targets of the same type
+ */
+static void unregister_netcons_consoles(void)
+{
+ struct netconsole_target *nt;
+ u32 console_type_needed = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&target_list_lock, flags);
+ list_for_each_entry(nt, &target_list, list) {
+ if (nt->extended)
+ console_type_needed |= CONS_EXTENDED;
+ else
+ console_type_needed |= CONS_BASIC;
+ }
+ spin_unlock_irqrestore(&target_list_lock, flags);
+
+ if (!(console_type_needed & CONS_EXTENDED) &&
+ console_is_registered(&netconsole_ext))
+ unregister_console(&netconsole_ext);
+
+ if (!(console_type_needed & CONS_BASIC) &&
+ console_is_registered(&netconsole))
+ unregister_console(&netconsole);
+}
+
/*
* This one is special -- targets created through the configfs interface
* are not enabled (and the corresponding netpoll activated) by default.
@@ -488,8 +520,18 @@ static ssize_t enabled_store(struct config_item *item,
goto out_unlock;
}
- if (nt->extended && !console_is_registered(&netconsole_ext))
+ if (nt->extended && !console_is_registered(&netconsole_ext)) {
+ netconsole_ext.flags |= CON_ENABLED;
register_console(&netconsole_ext);
+ }
+
+ /* User might be enabling the basic format target for the very
+ * first time, make sure the console is registered.
+ */
+ if (!nt->extended && !console_is_registered(&netconsole)) {
+ netconsole.flags |= CON_ENABLED;
+ register_console(&netconsole);
+ }
/*
* Skip netpoll_parse_options() -- all the attributes are
@@ -517,6 +559,10 @@ static ssize_t enabled_store(struct config_item *item,
list_move(&nt->list, &target_cleanup_list);
spin_unlock_irqrestore(&target_list_lock, flags);
mutex_unlock(&target_cleanup_list_lock);
+ /* Unregister consoles, whose the last target of that type got
+ * disabled.
+ */
+ unregister_netcons_consoles();
}
ret = strnlen(buf, count);
@@ -1691,8 +1737,8 @@ static int __init init_netconsole(void)
{
int err;
struct netconsole_target *nt, *tmp;
+ u32 console_type_needed = 0;
unsigned int count = 0;
- bool extended = false;
unsigned long flags;
char *target_config;
char *input = config;
@@ -1708,9 +1754,10 @@ static int __init init_netconsole(void)
}
/* Dump existing printks when we register */
if (nt->extended) {
- extended = true;
+ console_type_needed |= CONS_EXTENDED;
netconsole_ext.flags |= CON_PRINTBUFFER;
} else {
+ console_type_needed |= CONS_BASIC;
netconsole.flags |= CON_PRINTBUFFER;
}
@@ -1729,9 +1776,10 @@ static int __init init_netconsole(void)
if (err)
goto undonotifier;
- if (extended)
+ if (console_type_needed & CONS_EXTENDED)
register_console(&netconsole_ext);
- register_console(&netconsole);
+ if (console_type_needed & CONS_BASIC)
+ register_console(&netconsole);
pr_info("network logging started\n");
return err;
@@ -1761,7 +1809,8 @@ static void __exit cleanup_netconsole(void)
if (console_is_registered(&netconsole_ext))
unregister_console(&netconsole_ext);
- unregister_console(&netconsole);
+ if (console_is_registered(&netconsole))
+ unregister_console(&netconsole);
dynamic_netconsole_exit();
unregister_netdevice_notifier(&netconsole_netdev_notifier);