From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:16
This patchset introduces target resume capability to netconsole allowing
it to recover targets when underlying low-level interface comes back
online.
The patchset starts by refactoring netconsole state representation in
order to allow representing deactivated targets (targets that are
disabled due to interfaces going down).
It then modifies netconsole to handle NETDEV_UP events for such targets
and setups netpoll. Targets are matched with incoming interfaces
depending on how they were initially bound in netconsole (by mac or
interface name).
The patchset includes a selftest that validates netconsole target state
transitions and that target is functional after resumed.
Signed-off-by: Andre Carvalho <redacted>
---
Changes in v3:
- Resume by mac or interface name depending on how target was created.
- Attempt to resume target without holding target list lock, by moving
the target to a temporary list. This is required as netpoll may
attempt to allocate memory.
- Link to v2: https://lore.kernel.org/r/20250921-netcons-retrigger-v2-0-a0e84006237f@gmail.com
Changes in v2:
- Attempt to resume target in the same thread, instead of using
workqueue .
- Add wrapper around __netpoll_setup (patch 4).
- Renamed resume_target to maybe_resume_target and moved conditionals to
inside its implementation, keeping code more clear.
- Verify that device addr matches target mac address when target was
setup using mac.
- Update selftest to cover targets bound by mac and interface name.
- Fix typo in selftest comment and sort tests alphabetically in
Makefile.
- Link to v1:
https://lore.kernel.org/r/20250909-netcons-retrigger-v1-0-3aea904926cf@gmail.com
---
Andre Carvalho (4):
netconsole: convert 'enabled' flag to enum for clearer state management
netpoll: add wrapper around __netpoll_setup with dev reference
netconsole: resume previously deactivated target
selftests: netconsole: validate target resume
Breno Leitao (2):
netconsole: add target_state enum
netconsole: add STATE_DEACTIVATED to track targets disabled by low level
drivers/net/netconsole.c | 126 ++++++++++++++++-----
include/linux/netpoll.h | 1 +
net/core/netpoll.c | 20 ++++
tools/testing/selftests/drivers/net/Makefile | 1 +
.../selftests/drivers/net/lib/sh/lib_netcons.sh | 30 ++++-
.../selftests/drivers/net/netcons_resume.sh | 92 +++++++++++++++
6 files changed, 238 insertions(+), 32 deletions(-)
---
base-commit: a0c3aefb08cd81864b17c23c25b388dba90b9dad
change-id: 20250816-netcons-retrigger-a4f547bfc867
Best regards,
--
Andre Carvalho [off-list ref]
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:17
From: Breno Leitao <leitao@debian.org>
Introduces a enum to track netconsole target state which is going to
replace the enabled boolean.
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Andre Carvalho <redacted>
---
drivers/net/netconsole.c | 5 +++++
1 file changed, 5 insertions(+)
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:19
This patch refactors the netconsole driver's target enabled state from a
simple boolean to an explicit enum (`target_state`).
This allow the states to be expanded to a new state in the upcoming
change.
Co-developed-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Andre Carvalho <redacted>
---
drivers/net/netconsole.c | 52 ++++++++++++++++++++++++++----------------------
1 file changed, 28 insertions(+), 24 deletions(-)
@@ -275,7 +276,7 @@ static void netconsole_process_cleanups_core(void)mutex_lock(&target_cleanup_list_lock);list_for_each_entry_safe(nt,tmp,&target_cleanup_list,list){/* all entries in the cleanup_list needs to be disabled */-WARN_ON_ONCE(nt->enabled);+WARN_ON_ONCE(nt->state==STATE_ENABLED);do_netpoll_cleanup(&nt->np);/* moved the cleaned target to target_list. Need to hold both*locks
@@ -610,16 +612,16 @@ static ssize_t enabled_store(struct config_item *item,if(ret)gotoout_unlock;-nt->enabled=true;+nt->state=STATE_ENABLED;pr_info("network logging started\n");}else{/* false *//* We need to disable the netconsole before cleaning it up*otherwisewemightendupinwrite_msg()with-*nt->np.dev==NULLandnt->enabled==true+*nt->np.dev==NULLandnt->state==STATE_ENABLED*/mutex_lock(&target_cleanup_list_lock);spin_lock_irqsave(&target_list_lock,flags);-nt->enabled=false;+nt->state=STATE_DISABLED;/* Remove the target from the list, while holding*target_list_lock*/
@@ -648,7 +650,7 @@ static ssize_t release_store(struct config_item *item, const char *buf,ssize_tret;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));ret=-EINVAL;
@@ -675,7 +677,7 @@ static ssize_t extended_store(struct config_item *item, const char *buf,ssize_tret;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));ret=-EINVAL;
@@ -699,7 +701,7 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf,structnetconsole_target*nt=to_target(item);mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));mutex_unlock(&dynamic_netconsole_mutex);
@@ -720,7 +722,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf,ssize_tret=-EINVAL;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));gotoout_unlock;
@@ -742,7 +744,7 @@ static ssize_t remote_port_store(struct config_item *item,ssize_tret=-EINVAL;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));gotoout_unlock;
@@ -765,7 +767,7 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf,intipv6;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));gotoout_unlock;
@@ -790,7 +792,7 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf,intipv6;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));gotoout_unlock;
@@ -839,7 +841,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,ssize_tret=-EINVAL;mutex_lock(&dynamic_netconsole_mutex);-if(nt->enabled){+if(nt->state==STATE_ENABLED){pr_err("target (%s) is enabled, disable to update parameters\n",config_item_name(&nt->group.cg_item));gotoout_unlock;
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:20
From: Breno Leitao <leitao@debian.org>
When the low level interface brings a netconsole target down, record this
using a new STATE_DEACTIVATED state. This allows netconsole to distinguish
between targets explicitly disabled by users and those deactivated due to
interface state changes.
It also enables automatic recovery and re-enabling of targets if the
underlying low-level interfaces come back online.
From a code perspective, anything that is not STATE_ENABLED is disabled.
Mark the device that is down due to NETDEV_UNREGISTER as
STATE_DEACTIVATED, this, should be the same as STATE_DISABLED from
a code perspective.
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Andre Carvalho <redacted>
---
drivers/net/netconsole.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
@@ -575,6 +576,14 @@ static ssize_t enabled_store(struct config_item *item,if(ret)gotoout_unlock;+/* When the user explicitly enables or disables a target that is+*currentlydeactivated,resetitsstatetodisabled.TheDEACTIVATED+*stateonlytracksinterface-drivendeactivationandshould_not_+*persistwhentheusermanuallychangesthetarget'senabledstate.+*/+if(nt->state==STATE_DEACTIVATED)+nt->state=STATE_DISABLED;+ret=-EINVAL;current_enabled=nt->state==STATE_ENABLED;if(enabled==current_enabled){
@@ -1461,7 +1470,7 @@ static int netconsole_netdev_event(struct notifier_block *this,caseNETDEV_RELEASE:caseNETDEV_JOIN:caseNETDEV_UNREGISTER:-nt->state=STATE_DISABLED;+nt->state=STATE_DEACTIVATED;list_move(&nt->list,&target_cleanup_list);stopped=true;}
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:22
Introduce __netpoll_setup_hold() which wraps __netpoll_setup() and
on success holds a reference to the device. This helper requires caller
to already hold RNTL and should be paired with netpoll_cleanup to ensure
proper handling of the reference.
This helper is going to be used by netconsole to setup netpoll in
response to a NETDEV_UP event. Since netconsole always perform cleanup
using netpoll_cleanup, this will ensure that reference counting is
correct and handled entirely inside netpoll.
Signed-off-by: Andre Carvalho <redacted>
---
include/linux/netpoll.h | 1 +
net/core/netpoll.c | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+)
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:23
Attempt to resume a previously deactivated target when the associated
interface comes back (NETDEV_UP event is received) by calling
__netpoll_setup_hold on the device.
Depending on how the target was setup (by mac or interface name), the
corresponding field is compared with the device being brought up.
Targets that are candidates for resuming are removed from the target list
and added to a temporarily list, as __netpoll_setup_hold might allocate.
__netpoll_setup_hold assumes RTNL is held (which is guaranteed to be the
case when handling the event) and holds a reference to the device in case
of success. This reference will be removed upon target (or netconsole)
removal by netpoll_cleanup.
Target transitions to STATE_DISABLED in case of failures resuming it to
avoid retrying the same target indefinitely.
Signed-off-by: Andre Carvalho <redacted>
---
drivers/net/netconsole.c | 62 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 56 insertions(+), 6 deletions(-)
@@ -1445,17 +1447,50 @@ static int prepare_extradata(struct netconsole_target *nt)}#endif /* CONFIG_NETCONSOLE_DYNAMIC */+/* Attempts to resume logging to a deactivated target. */+staticvoidmaybe_resume_target(structnetconsole_target*nt,+structnet_device*ndev)+{+intret;++ret=__netpoll_setup_hold(&nt->np,ndev);+if(ret){+/* netpoll fails setup once, do not try again. */+nt->state=STATE_DISABLED;+}else{+nt->state=STATE_ENABLED;+pr_info("network logging resumed on interface %s\n",+nt->np.dev_name);+}+}++/* Check if the target was bound by mac address. */+staticboolbound_by_mac(structnetconsole_target*nt)+{+returnis_valid_ether_addr(nt->np.dev_mac);+}++/* Checks if a target matches a device. */+staticbooltarget_match(structnetconsole_target*nt,structnet_device*ndev)+{+if(bound_by_mac(nt))+return!memcmp(nt->np.dev_mac,ndev->dev_addr,ETH_ALEN);+return!strncmp(nt->np.dev_name,ndev->name,IFNAMSIZ);+}+/* Handle network interface device notifications */staticintnetconsole_netdev_event(structnotifier_block*this,unsignedlongevent,void*ptr){-unsignedlongflags;-structnetconsole_target*nt,*tmp;structnet_device*dev=netdev_notifier_info_to_dev(ptr);+structnetconsole_target*nt,*tmp;+LIST_HEAD(resume_list);boolstopped=false;+unsignedlongflags;if(!(event==NETDEV_CHANGENAME||event==NETDEV_UNREGISTER||-event==NETDEV_RELEASE||event==NETDEV_JOIN))+event==NETDEV_RELEASE||event==NETDEV_JOIN||+event==NETDEV_UP))gotodone;mutex_lock(&target_cleanup_list_lock);
@@ -1475,11 +1510,26 @@ static int netconsole_netdev_event(struct notifier_block *this,stopped=true;}}+if(nt->state==STATE_DEACTIVATED&&event==NETDEV_UP&&+target_match(nt,dev))+list_move(&nt->list,&resume_list);netconsole_target_put(nt);}spin_unlock_irqrestore(&target_list_lock,flags);mutex_unlock(&target_cleanup_list_lock);+list_for_each_entry_safe(nt,tmp,&resume_list,list){+maybe_resume_target(nt,dev);++/* At this point the target is either enabled or disabled and+*wascleanedupbeforegettingdeactivated.Eitherway,addit+*backtotargetlist.+*/+spin_lock_irqsave(&target_list_lock,flags);+list_move(&nt->list,&target_list);+spin_unlock_irqrestore(&target_list_lock,flags);+}+if(stopped){constchar*msg="had an event";
From: Andre Carvalho <hidden> Date: 2025-11-09 11:06:25
Introduce a new netconsole selftest to validate that netconsole is able
to resume a deactivated target when the low level interface comes back.
The test setups the network using netdevsim, creates a netconsole target
and then remove/add netdevsim in order to bring the same interfaces
back. Afterwards, the test validates that the target works as expected.
Targets are created via cmdline parameters to the module to ensure that
we are able to resume targets that were bound by mac and interface name.
Signed-off-by: Andre Carvalho <redacted>
---
tools/testing/selftests/drivers/net/Makefile | 1 +
.../selftests/drivers/net/lib/sh/lib_netcons.sh | 30 ++++++-
.../selftests/drivers/net/netcons_resume.sh | 92 ++++++++++++++++++++++
3 files changed, 120 insertions(+), 3 deletions(-)
@@ -186,12 +186,13 @@ function do_cleanup() {}functioncleanup(){+localTARGETPATH=${1:-${NETCONS_PATH}}# delete netconsole dynamic reconfiguration-echo0>"${NETCONS_PATH}"/enabled+echo0>"${TARGETPATH}"/enabled# Remove all the keys that got created during the selftest-find"${NETCONS_PATH}/userdata/"-mindepth1-typed-delete+find"${TARGETPATH}/userdata/"-mindepth1-typed-delete# Remove the configfs entry-rmdir"${NETCONS_PATH}"+rmdir"${TARGETPATH}"do_cleanup}
@@ -350,6 +351,29 @@ function check_netconsole_module() {fi}+functionwait_target_state(){+localTARGET=${1}+localSTATE=${2}+localFILE="${NETCONS_CONFIGFS}"/"${TARGET}"/"enabled"++if["${STATE}"=="enabled"]+then+ENABLED=1+else+ENABLED=0+fi++if[!-f"$FILE"];then+echo"FAIL: Target does not exist.">&2+exit"${ksft_fail}"+fi++slowwait2sh-c"test -n \"\$(grep \"${ENABLED}\" \"${FILE}\")\""||{+echo"FAIL: ${TARGET} is not ${STATE}.">&2+exit"${ksft_fail}"+}+}+# A wrapper to translate protocol version to udp versionfunctionwait_for_port(){localNAMESPACE=${1}
@@ -0,0 +1,92 @@+#!/usr/bin/env bash+# SPDX-License-Identifier: GPL-2.0++# This test validates that netconsole is able to resume a target that was+# deactivated when its interface was removed when the interface is brought+# back up.+#+# The test configures a netconsole target and then removes netdevsim module to+# cause the interface to disappear. Targets are configured via cmdline to ensure+# targets bound by interface name and mac address can be resumed.+# The test verifies that the target moved to disabled state before adding+# netdevsim and the interface back.+#+# Finally, the test verifies that the target is re-enabled automatically and+# the message is received on the destination interface.+#+# Author: Andre Carvalho <asantostc@gmail.com>++set-euopipefail++SCRIPTDIR=$(dirname"$(readlink-e"${BASH_SOURCE[0]}")")++source"${SCRIPTDIR}"/lib/sh/lib_netcons.sh++modprobenetdevsim2>/dev/null||true+rmmodnetconsole2>/dev/null||true++check_netconsole_module++# Run the test twice, with different cmdline parameters+forBINDMODEin"ifname""mac"+do+echo"Running with bind mode: ${BINDMODE}">&2+# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)+echo"6 5">/proc/sys/kernel/printk++# Create one namespace and two interfaces+set_network+trapdo_cleanupEXIT++# Create the command line for netconsole, with the configuration from+# the function above+CMDLINE=$(create_cmdline_str"${BINDMODE}")++# The content of kmsg will be save to the following file+OUTPUT_FILE="/tmp/${TARGET}-${BINDMODE}"++# Load the module, with the cmdline set+modprobenetconsole"${CMDLINE}"+# Expose cmdline target in configfs+mkdir${NETCONS_CONFIGFS}"/cmdline0"+trap'cleanup "${NETCONS_CONFIGFS}"/cmdline0'EXIT++# Target should be enabled+wait_target_state"cmdline0""enabled"++# Remove low level module+rmmodnetdevsim+# Target should be disabled+wait_target_state"cmdline0""disabled"++# Add back low level module+modprobenetdevsim+# Recreate namespace and two interfaces+set_network+# Target should be enabled again+wait_target_state"cmdline0""enabled"++# Listen for netconsole port inside the namespace and destination+# interface+listen_port_and_save_to"${OUTPUT_FILE}"&+# Wait for socat to start and listen to the port.+wait_local_port_listen"${NAMESPACE}""${PORT}"udp+# Send the message+echo"${MSG}: ${TARGET}">/dev/kmsg+# Wait until socat saves the file to disk+busywait"${BUSYWAIT_TIMEOUT}"test-s"${OUTPUT_FILE}"+# Make sure the message was received in the dst part+# and exit+validate_msg"${OUTPUT_FILE}"++# kill socat in case it is still running+pkill_socat+# Cleanup & unload the module+cleanup"${NETCONS_CONFIGFS}/cmdline0"+rmmodnetconsole+trap-EXIT++echo"${BINDMODE} : Test passed">&2+done++exit"${ksft_pass}"
On Sun, Nov 09, 2025 at 11:05:55AM +0000, Andre Carvalho wrote:
quoted hunk
Attempt to resume a previously deactivated target when the associated
interface comes back (NETDEV_UP event is received) by calling
__netpoll_setup_hold on the device.
Depending on how the target was setup (by mac or interface name), the
corresponding field is compared with the device being brought up.
Targets that are candidates for resuming are removed from the target list
and added to a temporarily list, as __netpoll_setup_hold might allocate.
__netpoll_setup_hold assumes RTNL is held (which is guaranteed to be the
case when handling the event) and holds a reference to the device in case
of success. This reference will be removed upon target (or netconsole)
removal by netpoll_cleanup.
Target transitions to STATE_DISABLED in case of failures resuming it to
avoid retrying the same target indefinitely.
Signed-off-by: Andre Carvalho <redacted>
---
drivers/net/netconsole.c | 62 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 56 insertions(+), 6 deletions(-)
* disabled. Internally, although both STATE_DISABLED and
* STATE_DEACTIVATED correspond to inactive targets, the latter is
* due to automatic interface state changes and will try
* recover automatically, if the interface comes back
* online.
quoted hunk
* Also, other parameters of a target may be modified at
- * runtime only when it is disabled (state == STATE_DISABLED).
+ * runtime only when it is disabled (state != STATE_ENABLED).
* @extended: Denotes whether console is extended or not.
* @release: Denotes whether kernel release version should be prepended
* to the message. Depends on extended console.
@@ -1445,17 +1447,50 @@ static int prepare_extradata(struct netconsole_target *nt) } #endif /* CONFIG_NETCONSOLE_DYNAMIC */+/* Attempts to resume logging to a deactivated target. */+static void maybe_resume_target(struct netconsole_target *nt,+ struct net_device *ndev)+{+ int ret;++ ret = __netpoll_setup_hold(&nt->np, ndev);+ if (ret) {+ /* netpoll fails setup once, do not try again. */+ nt->state = STATE_DISABLED;+ } else {+ nt->state = STATE_ENABLED;+ pr_info("network logging resumed on interface %s\n",+ nt->np.dev_name);+ }+}
I am not sure that helper is useful, I would simplify the last patch
with this one and write something like:
/* Attempts to resume logging to a deactivated target. */
static void maybe_resume_target(struct netconsole_target *nt,
struct net_device *ndev)
{
int ret;
ret = __netpoll_setup_hold(&nt->np, ndev);
if (ret) {
/* netpoll fails setup once, do not try again. */
nt->state = STATE_DISABLED;
return;
}
netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL);
nt->state = STATE_ENABLED;
pr_info("network logging resumed on interface %s\n",
nt->np.dev_name);
}
+
+/* Check if the target was bound by mac address. */
+static bool bound_by_mac(struct netconsole_target *nt)
+{
+ return is_valid_ether_addr(nt->np.dev_mac);
+}
Awesome. I liked this helper. It might be useful it some other places, and
eventually transformed into a specific type in the target (in case we need to
in the future)
Can we use it egress_dev also? If so, please separate this in a separate patch.
I think it would be better to move the nt->state == STATE_DEACTIVATED to target_match and use
the case above. As the following:
if (nt->np.dev == dev) {
switch (event) {
case NETDEV_CHANGENAME:
....
case NETDEV_UP:
if (target_match(nt, dev))
list_move(&nt->list, &resume_list);
Write a comment saying that maybe_resume_target() might be called with IRQ
enabled.
+ list_for_each_entry_safe(nt, tmp, &resume_list, list) {
+ maybe_resume_target(nt, dev);
+
+ /* At this point the target is either enabled or disabled and
+ * was cleaned up before getting deactivated. Either way, add it
+ * back to target list.
+ */
+ spin_lock_irqsave(&target_list_lock, flags);
+ list_move(&nt->list, &target_list);
+ spin_unlock_irqrestore(&target_list_lock, flags);
+ }
+
if (stopped) {
const char *msg = "had an event";
Also, extract the code below in a static function. Similar to
netconsole_process_cleanups_core(), but passing resume_list argument.
Let's try to keep netconsole_netdev_event() simple to read and reason about.
On Sun, Nov 09, 2025 at 11:05:56AM +0000, Andre Carvalho wrote:
quoted hunk
Introduce a new netconsole selftest to validate that netconsole is able
to resume a deactivated target when the low level interface comes back.
The test setups the network using netdevsim, creates a netconsole target
and then remove/add netdevsim in order to bring the same interfaces
back. Afterwards, the test validates that the target works as expected.
Targets are created via cmdline parameters to the module to ensure that
we are able to resume targets that were bound by mac and interface name.
Signed-off-by: Andre Carvalho <redacted>
---
tools/testing/selftests/drivers/net/Makefile | 1 +
.../selftests/drivers/net/lib/sh/lib_netcons.sh | 30 ++++++-
.../selftests/drivers/net/netcons_resume.sh | 92 ++++++++++++++++++++++
3 files changed, 120 insertions(+), 3 deletions(-)
@@ -186,12 +186,13 @@ function do_cleanup() {}functioncleanup(){+localTARGETPATH=${1:-${NETCONS_PATH}}# delete netconsole dynamic reconfiguration-echo0>"${NETCONS_PATH}"/enabled+echo0>"${TARGETPATH}"/enabled# Remove all the keys that got created during the selftest-find"${NETCONS_PATH}/userdata/"-mindepth1-typed-delete+find"${TARGETPATH}/userdata/"-mindepth1-typed-delete# Remove the configfs entry-rmdir"${NETCONS_PATH}"+rmdir"${TARGETPATH}"do_cleanup}
@@ -350,6 +351,29 @@ function check_netconsole_module() {fi}+functionwait_target_state(){+localTARGET=${1}+localSTATE=${2}+localFILE="${NETCONS_CONFIGFS}"/"${TARGET}"/"enabled"
local TARGET_PATH="${NETCONS_CONFIGFS}"/"${TARGET}"
+
+ if [ "${STATE}" == "enabled" ]
+ then
+ ENABLED=1
Shouldn't they be local variables in here ?
+ else
+ ENABLED=0
+ fi
+
+ if [ ! -f "$FILE" ]; then
if [ ! -f "${TARGET_PATH}" ]; then
+ echo "FAIL: Target does not exist." >&2
+ exit "${ksft_fail}"
+ fi
+
+ slowwait 2 sh -c "test -n \"\$(grep \"${ENABLED}\" \"${FILE}\")\"" || {
+ echo "FAIL: ${TARGET} is not ${STATE}." >&2
+ }
+}
+
# A wrapper to translate protocol version to udp version
function wait_for_port() {
local NAMESPACE=${1}
@@ -0,0 +1,92 @@+#!/usr/bin/env bash+# SPDX-License-Identifier: GPL-2.0++# This test validates that netconsole is able to resume a target that was+# deactivated when its interface was removed when the interface is brought+# back up.
Comment above is a bit harder to understand.
+#
+# The test configures a netconsole target and then removes netdevsim module to
+# cause the interface to disappear. Targets are configured via cmdline to ensure
+# targets bound by interface name and mac address can be resumed.
+# The test verifies that the target moved to disabled state before adding
+# netdevsim and the interface back.
+#
+# Finally, the test verifies that the target is re-enabled automatically and
+# the message is received on the destination interface.
+#
+# Author: Andre Carvalho [off-list ref]
+
+set -euo pipefail
+
+SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
+
+source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh
+
+modprobe netdevsim 2> /dev/null || true
+rmmod netconsole 2> /dev/null || true
+
+check_netconsole_module
+
+# Run the test twice, with different cmdline parameters
+for BINDMODE in "ifname" "mac"
+do
+ echo "Running with bind mode: ${BINDMODE}" >&2
+ # Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
+ echo "6 5" > /proc/sys/kernel/printk
+
+ # Create one namespace and two interfaces
+ set_network
+ trap do_cleanup EXIT
can we keep these trap lines outside of the loop?
+
+ # Create the command line for netconsole, with the configuration from
+ # the function above
+ CMDLINE=$(create_cmdline_str "${BINDMODE}")
+
+ # The content of kmsg will be save to the following file
+ OUTPUT_FILE="/tmp/${TARGET}-${BINDMODE}"
+
+ # Load the module, with the cmdline set
+ modprobe netconsole "${CMDLINE}"
+ # Expose cmdline target in configfs
+ mkdir ${NETCONS_CONFIGFS}"/cmdline0"
+ trap 'cleanup "${NETCONS_CONFIGFS}"/cmdline0' EXIT
+
+ # Target should be enabled
+ wait_target_state "cmdline0" "enabled"
+
+ # Remove low level module
+ rmmod netdevsim
+ # Target should be disabled
+ wait_target_state "cmdline0" "disabled"
+
+ # Add back low level module
+ modprobe netdevsim
+ # Recreate namespace and two interfaces
+ set_network
+ # Target should be enabled again
+ wait_target_state "cmdline0" "enabled"
+
+ # Listen for netconsole port inside the namespace and destination
+ # interface
+ listen_port_and_save_to "${OUTPUT_FILE}" &
+ # Wait for socat to start and listen to the port.
+ wait_local_port_listen "${NAMESPACE}" "${PORT}" udp
+ # Send the message
+ echo "${MSG}: ${TARGET}" > /dev/kmsg
+ # Wait until socat saves the file to disk
+ busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}"
+ # Make sure the message was received in the dst part
+ # and exit
+ validate_msg "${OUTPUT_FILE}"
+
+ # kill socat in case it is still running
+ pkill_socat
+ # Cleanup & unload the module
+ cleanup "${NETCONS_CONFIGFS}/cmdline0"
+ rmmod netconsole
Why do we need to remove netconsole module in here?
Thanks for this patch. This is solving a real issue we have right now.
--breno
From: Jakub Kicinski <kuba@kernel.org> Date: 2025-11-11 16:02:33
On Sun, 09 Nov 2025 11:05:50 +0000 Andre Carvalho wrote:
This patchset introduces target resume capability to netconsole allowing
it to recover targets when underlying low-level interface comes back
online.
Hi! FWIW this is not getting applied to our testing branch.
I suppose it's because of Breno's fixes in net/main.
net will be merged into net-next on Thursday afternoon.
Until then please switch to posting as RFC.
Once merge happens you'll need to rebase and post as PATCH.
From: Andre Carvalho <hidden> Date: 2025-11-11 19:18:50
On Tue, Nov 11, 2025 at 02:12:26AM -0800, Breno Leitao wrote:
quoted
+ * disabled. Internally, although both STATE_DISABLED and
+ * STATE_DEACTIVATED correspond to inactive netpoll the latter is>
+ * due to interface state changes and may recover automatically.
* disabled. Internally, although both STATE_DISABLED and
* STATE_DEACTIVATED correspond to inactive targets, the latter is
* due to automatic interface state changes and will try
* recover automatically, if the interface comes back
* online.
This is much clearer, thanks for the suggestion.
quoted
+ ret = __netpoll_setup_hold(&nt->np, ndev);
+ if (ret) {
+ /* netpoll fails setup once, do not try again. */
+ nt->state = STATE_DISABLED;
+ } else {
+ nt->state = STATE_ENABLED;
+ pr_info("network logging resumed on interface %s\n",
+ nt->np.dev_name);
+ }
+}
I am not sure that helper is useful, I would simplify the last patch
with this one and write something like:
The main reason why I opted for a helper in netpoll was to keep reference
tracking for these devices strictly inside netpoll and have simmetry between
setup and cleanup. Having said that, this might be an overkill and I'm fine with
dropping the helper and taking your suggestion.
quoted
+
+/* Check if the target was bound by mac address. */
+static bool bound_by_mac(struct netconsole_target *nt)
+{
+ return is_valid_ether_addr(nt->np.dev_mac);
+}
Awesome. I liked this helper. It might be useful it some other places, and
eventually transformed into a specific type in the target (in case we need to
in the future)
Can we use it egress_dev also? If so, please separate this in a separate patch.
In order to do that, we'd need to move bound_by_mac to netpolland make it available
to be called by netconsole. Let me know if you'd like me to do this in this series,
otherwise I'm also happy to refactor this separately from this series.
I think it would be better to move the nt->state == STATE_DEACTIVATED to target_match and use
the case above. As the following:
if (nt->np.dev == dev) {
switch (event) {
case NETDEV_CHANGENAME:
....
case NETDEV_UP:
if (target_match(nt, dev))
list_move(&nt->list, &resume_list);
We are not able to handle this inside this switch because when target got deactivated,
do_netpoll_cleanup sets nt->np.dev = NULL. Having said that, I can still move nt->state == STATE_DEACTIVATED
to inside target_match (maybe calling it deactivated_target_match) to make this slightly more readable.
Write a comment saying that maybe_resume_target() might be called with IRQ
enabled.
Ack.
Also, extract the code below in a static function. Similar to
netconsole_process_cleanups_core(), but passing resume_list argument.
Let's try to keep netconsole_netdev_event() simple to read and reason about.
+ echo "FAIL: ${TARGET} is not ${STATE}." >&2
+ }
+}
+
# A wrapper to translate protocol version to udp version
function wait_for_port() {
local NAMESPACE=${1}
@@ -0,0 +1,92 @@+#!/usr/bin/env bash+# SPDX-License-Identifier: GPL-2.0++# This test validates that netconsole is able to resume a target that was+# deactivated when its interface was removed when the interface is brought+# back up.
Comment above is a bit harder to understand.
Agreed. What do you think of:
# This test validates that netconsole is able to resume a previously deactivated
# target once its interface is brought back up.
quoted
+for BINDMODE in "ifname" "mac"
+do
+ echo "Running with bind mode: ${BINDMODE}" >&2
+ # Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
+ echo "6 5" > /proc/sys/kernel/printk
+
+ # Create one namespace and two interfaces
+ set_network
+ trap do_cleanup EXIT
Why do we need to remove netconsole module in here?
We are removing the module here so we can load it on the second iteration of the
test with new cmdline. This is following a similar pattern to netcons_cmdline.sh.
Thanks for this patch. This is solving a real issue we have right now.
--breno
On Tue, Nov 11, 2025 at 07:18:46PM +0000, Andre Carvalho wrote:
On Tue, Nov 11, 2025 at 02:12:26AM -0800, Breno Leitao wrote:
quoted
quoted
+ * disabled. Internally, although both STATE_DISABLED and
+ * STATE_DEACTIVATED correspond to inactive netpoll the latter is>
+ * due to interface state changes and may recover automatically.
* disabled. Internally, although both STATE_DISABLED and
* STATE_DEACTIVATED correspond to inactive targets, the latter is
* due to automatic interface state changes and will try
* recover automatically, if the interface comes back
* online.
This is much clearer, thanks for the suggestion.
quoted
quoted
+ ret = __netpoll_setup_hold(&nt->np, ndev);
+ if (ret) {
+ /* netpoll fails setup once, do not try again. */
+ nt->state = STATE_DISABLED;
+ } else {
+ nt->state = STATE_ENABLED;
+ pr_info("network logging resumed on interface %s\n",
+ nt->np.dev_name);
+ }
+}
I am not sure that helper is useful, I would simplify the last patch
with this one and write something like:
The main reason why I opted for a helper in netpoll was to keep reference
tracking for these devices strictly inside netpoll and have simmetry between
setup and cleanup. Having said that, this might be an overkill and I'm fine with
dropping the helper and taking your suggestion.
Right, that makes sense. Would we have other owners for that function?
quoted
quoted
+
+/* Check if the target was bound by mac address. */
+static bool bound_by_mac(struct netconsole_target *nt)
+{
+ return is_valid_ether_addr(nt->np.dev_mac);
+}
Awesome. I liked this helper. It might be useful it some other places, and
eventually transformed into a specific type in the target (in case we need to
in the future)
Can we use it egress_dev also? If so, please separate this in a separate patch.
In order to do that, we'd need to move bound_by_mac to netpolland make it available
to be called by netconsole. Let me know if you'd like me to do this in this series,
otherwise I'm also happy to refactor this separately from this series.
Oh, I see the problem. That egress_dev() should belong to netconsole not
netpoll.
I've sent a patchset to start untangling netconsole and netpoll, and the
patchset was conflicting with the fix in 'net'
https://lore.kernel.org/all/20250902-netpoll_untangle_v3-v1-0-51a03d6411be@debian.org/
Let's keep egress_dev() as it is for now, until we got them untangled.
I think it would be better to move the nt->state == STATE_DEACTIVATED to target_match and use
the case above. As the following:
if (nt->np.dev == dev) {
switch (event) {
case NETDEV_CHANGENAME:
....
case NETDEV_UP:
if (target_match(nt, dev))
list_move(&nt->list, &resume_list);
We are not able to handle this inside this switch because when target got deactivated,
You are right, that is why we are doing the magic here. Please add
a comment in saying that maybe_resume_target() is IRQ usafe, thus,
cannot be called with IRQ disabled.
do_netpoll_cleanup sets nt->np.dev = NULL. Having said that, I can still move nt->state == STATE_DEACTIVATED
to inside target_match (maybe calling it deactivated_target_match) to make this slightly more readable.
From: Andre Carvalho <hidden> Date: 2025-11-12 23:42:12
On Wed, Nov 12, 2025 at 09:52:10AM -0800, Breno Leitao wrote:
quoted
The main reason why I opted for a helper in netpoll was to keep reference
tracking for these devices strictly inside netpoll and have simmetry between
setup and cleanup. Having said that, this might be an overkill and I'm fine with
dropping the helper and taking your suggestion.
Right, that makes sense. Would we have other owners for that function?
I've looked at other drivers using netpoll and from what I could find all of them
are using __netpoll_setup paired with __netpoll_free. They don't seem to
rely on dev_tracker to track references, I'd need to look a bit more to be certain,
but I think other callers are own the devices and track their lifecycle separately.
So I don't think this would be useful for them.
Since we are moving netpoll_cleanup to netconsole in your patch below, I think I should
drop the netpoll helper and keep it in netconsole. I wonder if we should consider
moving do_netpoll_cleanup to netconsole as well, since it seems to be the only caller
and then we would have the same symmetry I mentioned above.
So, to summarize, given your refactor patch I think it makes sense drop the previous
patch and do the netdev_hold in netconsole as you suggested. Does that sound good?
--
Andre Carvalho