Attempt to resume a previously deactivated target when the associated interface comes back (NETDEV_UP event is received).
Target transitions to STATE_DISABLED in case of failures resuming it to avoid retrying the same target indefinitely.
Signed-off-by: Andre Carvalho asantostc@gmail.com --- drivers/net/netconsole.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 59d770bb4baa5f9616b10c0dfb39ed45a4eb7710..397e6543b3d9aeda6da450823adee09cb3e9ae70 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -135,10 +135,12 @@ enum target_state { * @stats: Packet send stats for the target. Used for debugging. * @state: State of the target. * Visible from userspace (read-write). - * We maintain a strict 1:1 correspondence between this and - * whether the corresponding netpoll is active or inactive. + * From a userspace perspective, the target is either enabled or + * 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. * 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. @@ -172,6 +174,7 @@ struct netconsole_target { struct netpoll np; /* protected by target_list_lock */ char buf[MAX_PRINT_CHUNK]; + struct work_struct resume_wq; };
#ifdef CONFIG_NETCONSOLE_DYNAMIC @@ -237,6 +240,33 @@ static void populate_configfs_item(struct netconsole_target *nt, } #endif /* CONFIG_NETCONSOLE_DYNAMIC */
+/* Attempts to resume logging to a deactivated target. */ +static void resume_target(struct netconsole_target *nt) +{ + int ret; + + if (nt->state != STATE_DEACTIVATED) + return; + + ret = netpoll_setup(&nt->np); + if (ret) { + /* netpoll fails to register once, do not try again. */ + nt->state = STATE_DISABLED; + } else { + pr_info("network logging resumed on interface %s\n", + nt->np.dev_name); + nt->state = STATE_ENABLED; + } +} + +static void resume_work_handler(struct work_struct *work) +{ + struct netconsole_target *nt = + container_of(work, struct netconsole_target, resume_wq); + + resume_target(nt); +} + /* Allocate and initialize with defaults. * Note that these targets get their config_item fields zeroed-out. */ @@ -260,6 +290,8 @@ static struct netconsole_target *alloc_and_init(void) eth_broadcast_addr(nt->np.remote_mac); nt->state = STATE_DISABLED;
+ INIT_WORK(&nt->resume_wq, resume_work_handler); + return nt; }
@@ -1440,7 +1472,8 @@ static int netconsole_netdev_event(struct notifier_block *this, bool stopped = false;
if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || - event == NETDEV_RELEASE || event == NETDEV_JOIN)) + event == NETDEV_RELEASE || event == NETDEV_JOIN || + event == NETDEV_UP)) goto done;
mutex_lock(&target_cleanup_list_lock); @@ -1460,6 +1493,10 @@ static int netconsole_netdev_event(struct notifier_block *this, stopped = true; } } + if (nt->state == STATE_DEACTIVATED && event == NETDEV_UP) { + if (!strncmp(nt->np.dev_name, dev->name, IFNAMSIZ)) + schedule_work(&nt->resume_wq); + } netconsole_target_put(nt); } spin_unlock_irqrestore(&target_list_lock, flags); @@ -1915,6 +1952,7 @@ static struct netconsole_target *alloc_param_target(char *target_config, static void free_param_target(struct netconsole_target *nt) { netpoll_cleanup(&nt->np); + cancel_work_sync(&nt->resume_wq); kfree(nt); }