Skip to content

Commit a63bea1

Browse files
committed
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/[email protected] v1: https://lore.kernel.org/[email protected] ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 2c7e4a2 + 224a6e6 commit a63bea1

File tree

3 files changed

+112
-32
lines changed

3 files changed

+112
-32
lines changed

drivers/net/netconsole.c

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,22 @@ static DEFINE_SPINLOCK(target_list_lock);
8686
static DEFINE_MUTEX(target_cleanup_list_lock);
8787

8888
/*
89-
* Console driver for extended netconsoles. Registered on the first use to
90-
* avoid unnecessarily enabling ext message formatting.
89+
* Console driver for netconsoles. Register only consoles that have
90+
* an associated target of the same type.
9191
*/
92-
static struct console netconsole_ext;
92+
static struct console netconsole_ext, netconsole;
9393

9494
struct netconsole_target_stats {
9595
u64_stats_t xmit_drop_count;
9696
u64_stats_t enomem_count;
9797
struct u64_stats_sync syncp;
9898
};
9999

100+
enum console_type {
101+
CONS_BASIC = BIT(0),
102+
CONS_EXTENDED = BIT(1),
103+
};
104+
100105
/* Features enabled in sysdata. Contrary to userdata, this data is populated by
101106
* the kernel. The fields are designed as bitwise flags, allowing multiple
102107
* features to be set in sysdata_fields.
@@ -455,6 +460,33 @@ static ssize_t sysdata_release_enabled_show(struct config_item *item,
455460
return sysfs_emit(buf, "%d\n", release_enabled);
456461
}
457462

463+
/* Iterate in the list of target, and make sure we don't have any console
464+
* register without targets of the same type
465+
*/
466+
static void unregister_netcons_consoles(void)
467+
{
468+
struct netconsole_target *nt;
469+
u32 console_type_needed = 0;
470+
unsigned long flags;
471+
472+
spin_lock_irqsave(&target_list_lock, flags);
473+
list_for_each_entry(nt, &target_list, list) {
474+
if (nt->extended)
475+
console_type_needed |= CONS_EXTENDED;
476+
else
477+
console_type_needed |= CONS_BASIC;
478+
}
479+
spin_unlock_irqrestore(&target_list_lock, flags);
480+
481+
if (!(console_type_needed & CONS_EXTENDED) &&
482+
console_is_registered(&netconsole_ext))
483+
unregister_console(&netconsole_ext);
484+
485+
if (!(console_type_needed & CONS_BASIC) &&
486+
console_is_registered(&netconsole))
487+
unregister_console(&netconsole);
488+
}
489+
458490
/*
459491
* This one is special -- targets created through the configfs interface
460492
* are not enabled (and the corresponding netpoll activated) by default.
@@ -488,8 +520,18 @@ static ssize_t enabled_store(struct config_item *item,
488520
goto out_unlock;
489521
}
490522

491-
if (nt->extended && !console_is_registered(&netconsole_ext))
523+
if (nt->extended && !console_is_registered(&netconsole_ext)) {
524+
netconsole_ext.flags |= CON_ENABLED;
492525
register_console(&netconsole_ext);
526+
}
527+
528+
/* User might be enabling the basic format target for the very
529+
* first time, make sure the console is registered.
530+
*/
531+
if (!nt->extended && !console_is_registered(&netconsole)) {
532+
netconsole.flags |= CON_ENABLED;
533+
register_console(&netconsole);
534+
}
493535

494536
/*
495537
* Skip netpoll_parse_options() -- all the attributes are
@@ -517,6 +559,10 @@ static ssize_t enabled_store(struct config_item *item,
517559
list_move(&nt->list, &target_cleanup_list);
518560
spin_unlock_irqrestore(&target_list_lock, flags);
519561
mutex_unlock(&target_cleanup_list_lock);
562+
/* Unregister consoles, whose the last target of that type got
563+
* disabled.
564+
*/
565+
unregister_netcons_consoles();
520566
}
521567

522568
ret = strnlen(buf, count);
@@ -1691,8 +1737,8 @@ static int __init init_netconsole(void)
16911737
{
16921738
int err;
16931739
struct netconsole_target *nt, *tmp;
1740+
u32 console_type_needed = 0;
16941741
unsigned int count = 0;
1695-
bool extended = false;
16961742
unsigned long flags;
16971743
char *target_config;
16981744
char *input = config;
@@ -1708,9 +1754,10 @@ static int __init init_netconsole(void)
17081754
}
17091755
/* Dump existing printks when we register */
17101756
if (nt->extended) {
1711-
extended = true;
1757+
console_type_needed |= CONS_EXTENDED;
17121758
netconsole_ext.flags |= CON_PRINTBUFFER;
17131759
} else {
1760+
console_type_needed |= CONS_BASIC;
17141761
netconsole.flags |= CON_PRINTBUFFER;
17151762
}
17161763

@@ -1729,9 +1776,10 @@ static int __init init_netconsole(void)
17291776
if (err)
17301777
goto undonotifier;
17311778

1732-
if (extended)
1779+
if (console_type_needed & CONS_EXTENDED)
17331780
register_console(&netconsole_ext);
1734-
register_console(&netconsole);
1781+
if (console_type_needed & CONS_BASIC)
1782+
register_console(&netconsole);
17351783
pr_info("network logging started\n");
17361784

17371785
return err;
@@ -1761,7 +1809,8 @@ static void __exit cleanup_netconsole(void)
17611809

17621810
if (console_is_registered(&netconsole_ext))
17631811
unregister_console(&netconsole_ext);
1764-
unregister_console(&netconsole);
1812+
if (console_is_registered(&netconsole))
1813+
unregister_console(&netconsole);
17651814
dynamic_netconsole_exit();
17661815
unregister_netdevice_notifier(&netconsole_netdev_notifier);
17671816

tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ function set_network() {
9595
}
9696

9797
function create_dynamic_target() {
98+
local FORMAT=${1:-"extended"}
99+
98100
DSTMAC=$(ip netns exec "${NAMESPACE}" \
99101
ip link show "${DSTIF}" | awk '/ether/ {print $2}')
100102

@@ -106,6 +108,16 @@ function create_dynamic_target() {
106108
echo "${DSTMAC}" > "${NETCONS_PATH}"/remote_mac
107109
echo "${SRCIF}" > "${NETCONS_PATH}"/dev_name
108110

111+
if [ "${FORMAT}" == "basic" ]
112+
then
113+
# Basic target does not support release
114+
echo 0 > "${NETCONS_PATH}"/release
115+
echo 0 > "${NETCONS_PATH}"/extended
116+
elif [ "${FORMAT}" == "extended" ]
117+
then
118+
echo 1 > "${NETCONS_PATH}"/extended
119+
fi
120+
109121
echo 1 > "${NETCONS_PATH}"/enabled
110122
}
111123

@@ -159,6 +171,7 @@ function listen_port_and_save_to() {
159171

160172
function validate_result() {
161173
local TMPFILENAME="$1"
174+
local FORMAT=${2:-"extended"}
162175

163176
# TMPFILENAME will contain something like:
164177
# 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM
@@ -176,16 +189,20 @@ function validate_result() {
176189
exit "${ksft_fail}"
177190
fi
178191

179-
if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then
180-
echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2
181-
cat "${TMPFILENAME}" >&2
182-
exit "${ksft_fail}"
192+
# userdata is not supported on basic format target,
193+
# thus, do not validate it.
194+
if [ "${FORMAT}" != "basic" ];
195+
then
196+
if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then
197+
echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2
198+
cat "${TMPFILENAME}" >&2
199+
exit "${ksft_fail}"
200+
fi
183201
fi
184202

185203
# Delete the file once it is validated, otherwise keep it
186204
# for debugging purposes
187205
rm "${TMPFILENAME}"
188-
exit "${ksft_pass}"
189206
}
190207

191208
function check_for_dependencies() {

tools/testing/selftests/drivers/net/netcons_basic.sh

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,35 @@ check_for_dependencies
3232
echo "6 5" > /proc/sys/kernel/printk
3333
# Remove the namespace, interfaces and netconsole target on exit
3434
trap cleanup EXIT
35-
# Create one namespace and two interfaces
36-
set_network
37-
# Create a dynamic target for netconsole
38-
create_dynamic_target
39-
# Set userdata "key" with the "value" value
40-
set_user_data
41-
# Listed for netconsole port inside the namespace and destination interface
42-
listen_port_and_save_to "${OUTPUT_FILE}" &
43-
# Wait for socat to start and listen to the port.
44-
wait_local_port_listen "${NAMESPACE}" "${PORT}" udp
45-
# Send the message
46-
echo "${MSG}: ${TARGET}" > /dev/kmsg
47-
# Wait until socat saves the file to disk
48-
busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}"
49-
50-
# Make sure the message was received in the dst part
51-
# and exit
52-
validate_result "${OUTPUT_FILE}"
35+
36+
# Run the test twice, with different format modes
37+
for FORMAT in "basic" "extended"
38+
do
39+
echo "Running with target mode: ${FORMAT}"
40+
# Create one namespace and two interfaces
41+
set_network
42+
# Create a dynamic target for netconsole
43+
create_dynamic_target "${FORMAT}"
44+
# Only set userdata for extended format
45+
if [ "$FORMAT" == "extended" ]
46+
then
47+
# Set userdata "key" with the "value" value
48+
set_user_data
49+
fi
50+
# Listed for netconsole port inside the namespace and destination interface
51+
listen_port_and_save_to "${OUTPUT_FILE}" &
52+
# Wait for socat to start and listen to the port.
53+
wait_local_port_listen "${NAMESPACE}" "${PORT}" udp
54+
# Send the message
55+
echo "${MSG}: ${TARGET}" > /dev/kmsg
56+
# Wait until socat saves the file to disk
57+
busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}"
58+
59+
# Make sure the message was received in the dst part
60+
# and exit
61+
validate_result "${OUTPUT_FILE}" "${FORMAT}"
62+
cleanup
63+
done
64+
65+
trap - EXIT
66+
exit "${ksft_pass}"

0 commit comments

Comments
 (0)