From d93608b0a20e7e6ce51bfb5671c561fa07a60e81 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:09 -0700 Subject: [PATCH 1/9] netpoll: export carrier_timeout via netpoll_get_carrier_timeout() netpoll_wait_carrier() is only used in netconsole, and it will move to netconsole. The carrier_timeout module parameter has to stay in netpoll so the existing netpoll.carrier_timeout kernel parameter keeps working for current users, and we don't break user compatibility. Add a netpoll_get_carrier_timeout() accessor and export it so netconsole can read the value once the carrier wait lives there. Drop the now redundant timeout argument from netpoll_wait_carrier() (its only caller passed carrier_timeout) and read the parameter directly while the helper still lives here. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-1-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- include/linux/netpoll.h | 1 + net/core/netpoll.c | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 79315461a7b1..d426d1844cac 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -72,6 +72,7 @@ void netpoll_cleanup(struct netpoll *np); void do_netpoll_cleanup(struct netpoll *np); netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); void netpoll_zap_completion_queue(void); +unsigned int netpoll_get_carrier_timeout(void); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index f8da1048ea3a..9b9fcc307958 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -38,9 +38,20 @@ #define USEC_PER_POLL 50 +/* + * carrier_timeout is netconsole-specific and only kept here to preserve the + * netpoll.carrier_timeout module-parameter ABI. Its value is exposed to + * netconsole through netpoll_get_carrier_timeout(). + */ static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); +unsigned int netpoll_get_carrier_timeout(void) +{ + return carrier_timeout; +} +EXPORT_SYMBOL_GPL(netpoll_get_carrier_timeout); + static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq) @@ -397,12 +408,11 @@ static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) return buf; } -static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev, - unsigned int timeout) +static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) { unsigned long atmost; - atmost = jiffies + timeout * HZ; + atmost = jiffies + carrier_timeout * HZ; while (!netif_carrier_ok(ndev)) { if (time_after(jiffies, atmost)) { np_notice(np, "timeout waiting for carrier\n"); @@ -539,7 +549,7 @@ int netpoll_setup(struct netpoll *np) } rtnl_unlock(); - netpoll_wait_carrier(np, ndev, carrier_timeout); + netpoll_wait_carrier(np, ndev); rtnl_lock(); } From bb996303efae1240b364ca7658b11473b1ce8a2d Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:10 -0700 Subject: [PATCH 2/9] netpoll: export the netpoll_setup() helpers for netconsole Temporarily export leaf functions that will be moved to netconsole. The upcoming patch will move the setup function to netconsole, and continue to call these leaf functions here in netpoll, then other patches will move these exports functions to netconsole (and make them statics). In summary, these exports are temporary in order to make the patchset digestible. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-2-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- include/linux/netpoll.h | 5 +++++ net/core/netpoll.c | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index d426d1844cac..0877515fa744 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -73,6 +73,11 @@ void do_netpoll_cleanup(struct netpoll *np); netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); void netpoll_zap_completion_queue(void); unsigned int netpoll_get_carrier_timeout(void); +void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); +char *egress_dev(struct netpoll *np, char *buf, size_t bufsz); +int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev); +int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev); +bool netpoll_local_ip_unset(const struct netpoll *np); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 9b9fcc307958..6a545063223b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(__netpoll_setup); * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address * and its NUL terminator. */ -static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) +char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) { if (np->dev_name[0]) return np->dev_name; @@ -407,8 +407,9 @@ static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) snprintf(buf, bufsz, "%pM", np->dev_mac); return buf; } +EXPORT_SYMBOL_GPL(egress_dev); -static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) +void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) { unsigned long atmost; @@ -421,11 +422,12 @@ static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) msleep(1); } } +EXPORT_SYMBOL_GPL(netpoll_wait_carrier); /* * Take the IPv6 from ndev and populate local_ip structure in netpoll */ -static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) +int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) { char buf[MAC_ADDR_STR_LEN + 1]; int err = -EDESTADDRREQ; @@ -462,11 +464,12 @@ static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); return 0; } +EXPORT_SYMBOL_GPL(netpoll_take_ipv6); /* * Take the IPv4 from ndev and populate local_ip structure in netpoll */ -static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) +int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) { char buf[MAC_ADDR_STR_LEN + 1]; const struct in_ifaddr *ifa; @@ -491,6 +494,7 @@ static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) return 0; } +EXPORT_SYMBOL_GPL(netpoll_take_ipv4); /* * Test whether the caller left np->local_ip unset, so that @@ -502,12 +506,13 @@ static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) * doing so would misclassify a caller-supplied address as unset and * silently overwrite it with whatever address the device exposes. */ -static bool netpoll_local_ip_unset(const struct netpoll *np) +bool netpoll_local_ip_unset(const struct netpoll *np) { if (np->ipv6) return ipv6_addr_any(&np->local_ip.in6); return !np->local_ip.ip; } +EXPORT_SYMBOL_GPL(netpoll_local_ip_unset); int netpoll_setup(struct netpoll *np) { From 672ecd3bb145ac3b6e050e7fbdcaff0b46115c96 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:11 -0700 Subject: [PATCH 3/9] netconsole: take over netpoll_setup() from netpoll netpoll_setup() is only used by netconsole. All the other users use __netpoll_setup(). Move netpoll_setup() to netconsole, and rename it to netcons_netpoll_setup(). Pure code motion: the body is unchanged. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-3-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 86 ++++++++++++++++++++++++++++++++++++++-- include/linux/netpoll.h | 1 - net/core/netpoll.c | 81 ------------------------------------- 3 files changed, 83 insertions(+), 85 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 7f8851a734fc..3a7a2cafc627 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -351,6 +351,86 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +static int netcons_netpoll_setup(struct netpoll *np) +{ + struct net *net = current->nsproxy->net_ns; + char buf[MAC_ADDR_STR_LEN + 1]; + struct net_device *ndev = NULL; + bool ip_overwritten = false; + int err; + + rtnl_lock(); + if (np->dev_name[0]) + ndev = __dev_get_by_name(net, np->dev_name); + else if (is_valid_ether_addr(np->dev_mac)) + ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac); + + if (!ndev) { + np_err(np, "%s doesn't exist, aborting\n", + egress_dev(np, buf, sizeof(buf))); + err = -ENODEV; + goto unlock; + } + netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL); + + if (netdev_master_upper_dev_get(ndev)) { + np_err(np, "%s is a slave device, aborting\n", + egress_dev(np, buf, sizeof(buf))); + err = -EBUSY; + goto put; + } + + if (!netif_running(ndev)) { + np_info(np, "device %s not up yet, forcing it\n", + egress_dev(np, buf, sizeof(buf))); + + err = dev_open(ndev, NULL); + if (err) { + np_err(np, "failed to open %s\n", ndev->name); + goto put; + } + + rtnl_unlock(); + netpoll_wait_carrier(np, ndev); + rtnl_lock(); + } + + if (netpoll_local_ip_unset(np)) { + if (!np->ipv6) { + err = netpoll_take_ipv4(np, ndev); + if (err) + goto put; + } else { + err = netpoll_take_ipv6(np, ndev); + if (err) + goto put; + } + ip_overwritten = true; + } + + err = __netpoll_setup(np, ndev); + if (err) + goto put; + rtnl_unlock(); + + /* Make sure all NAPI polls which started before dev->npinfo + * was visible have exited before we start calling NAPI poll. + * NAPI skips locking if dev->npinfo is NULL. + */ + synchronize_rcu(); + + return 0; + +put: + DEBUG_NET_WARN_ON_ONCE(np->dev); + if (ip_overwritten) + memset(&np->local_ip, 0, sizeof(np->local_ip)); + netdev_put(ndev, &np->dev_tracker); +unlock: + rtnl_unlock(); + return err; +} + /* Attempts to resume logging to a deactivated target. */ static void resume_target(struct netconsole_target *nt) { @@ -361,7 +441,7 @@ static void resume_target(struct netconsole_target *nt) */ netconsole_skb_pool_init(nt); - if (netpoll_setup(&nt->np)) { + if (netcons_netpoll_setup(&nt->np)) { /* netpoll fails setup once, do not try again. */ netconsole_skb_pool_flush(nt); nt->state = STATE_DISABLED; @@ -840,7 +920,7 @@ static ssize_t enabled_store(struct config_item *item, */ netconsole_skb_pool_init(nt); - ret = netpoll_setup(&nt->np); + ret = netcons_netpoll_setup(&nt->np); if (ret) { netconsole_skb_pool_flush(nt); goto out_unlock; @@ -2430,7 +2510,7 @@ static struct netconsole_target *alloc_param_target(char *target_config, */ netconsole_skb_pool_init(nt); - err = netpoll_setup(&nt->np); + err = netcons_netpoll_setup(&nt->np); if (err) { pr_err("Not enabling netconsole for %s%d. Netpoll setup failed\n", NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count); diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 0877515fa744..cd455a5a013d 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -66,7 +66,6 @@ static inline void netpoll_poll_enable(struct net_device *dev) { return; } #endif int __netpoll_setup(struct netpoll *np, struct net_device *ndev); -int netpoll_setup(struct netpoll *np); void __netpoll_free(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); void do_netpoll_cleanup(struct netpoll *np); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 6a545063223b..d50d48a82def 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -514,87 +514,6 @@ bool netpoll_local_ip_unset(const struct netpoll *np) } EXPORT_SYMBOL_GPL(netpoll_local_ip_unset); -int netpoll_setup(struct netpoll *np) -{ - struct net *net = current->nsproxy->net_ns; - char buf[MAC_ADDR_STR_LEN + 1]; - struct net_device *ndev = NULL; - bool ip_overwritten = false; - int err; - - rtnl_lock(); - if (np->dev_name[0]) - ndev = __dev_get_by_name(net, np->dev_name); - else if (is_valid_ether_addr(np->dev_mac)) - ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac); - - if (!ndev) { - np_err(np, "%s doesn't exist, aborting\n", - egress_dev(np, buf, sizeof(buf))); - err = -ENODEV; - goto unlock; - } - netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL); - - if (netdev_master_upper_dev_get(ndev)) { - np_err(np, "%s is a slave device, aborting\n", - egress_dev(np, buf, sizeof(buf))); - err = -EBUSY; - goto put; - } - - if (!netif_running(ndev)) { - np_info(np, "device %s not up yet, forcing it\n", - egress_dev(np, buf, sizeof(buf))); - - err = dev_open(ndev, NULL); - if (err) { - np_err(np, "failed to open %s\n", ndev->name); - goto put; - } - - rtnl_unlock(); - netpoll_wait_carrier(np, ndev); - rtnl_lock(); - } - - if (netpoll_local_ip_unset(np)) { - if (!np->ipv6) { - err = netpoll_take_ipv4(np, ndev); - if (err) - goto put; - } else { - err = netpoll_take_ipv6(np, ndev); - if (err) - goto put; - } - ip_overwritten = true; - } - - err = __netpoll_setup(np, ndev); - if (err) - goto put; - rtnl_unlock(); - - /* Make sure all NAPI polls which started before dev->npinfo - * was visible have exited before we start calling NAPI poll. - * NAPI skips locking if dev->npinfo is NULL. - */ - synchronize_rcu(); - - return 0; - -put: - DEBUG_NET_WARN_ON_ONCE(np->dev); - if (ip_overwritten) - memset(&np->local_ip, 0, sizeof(np->local_ip)); - netdev_put(ndev, &np->dev_tracker); -unlock: - rtnl_unlock(); - return err; -} -EXPORT_SYMBOL(netpoll_setup); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo = From 6a55e81d4159e6745e4b77daac8362d85c30696e Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:12 -0700 Subject: [PATCH 4/9] netconsole: move netpoll_local_ip_unset() as netcons_local_ip_unset() Move netpoll_local_ip_unset() from netpoll to netconsole and rename it to netcons_local_ip_unset(); The body is otherwise unchanged, only the comment's setup-function reference is updated. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-4-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 19 ++++++++++++++++++- include/linux/netpoll.h | 1 - net/core/netpoll.c | 18 ------------------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 3a7a2cafc627..70f9c5bfb372 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -351,6 +351,23 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +/* + * Test whether the caller left np->local_ip unset, so that + * netcons_netpoll_setup() should auto-populate it from the egress device. + * + * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), + * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, + * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — + * doing so would misclassify a caller-supplied address as unset and + * silently overwrite it with whatever address the device exposes. + */ +static bool netcons_local_ip_unset(const struct netpoll *np) +{ + if (np->ipv6) + return ipv6_addr_any(&np->local_ip.in6); + return !np->local_ip.ip; +} + static int netcons_netpoll_setup(struct netpoll *np) { struct net *net = current->nsproxy->net_ns; @@ -395,7 +412,7 @@ static int netcons_netpoll_setup(struct netpoll *np) rtnl_lock(); } - if (netpoll_local_ip_unset(np)) { + if (netcons_local_ip_unset(np)) { if (!np->ipv6) { err = netpoll_take_ipv4(np, ndev); if (err) diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index cd455a5a013d..da05884f876e 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -76,7 +76,6 @@ void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); char *egress_dev(struct netpoll *np, char *buf, size_t bufsz); int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev); int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev); -bool netpoll_local_ip_unset(const struct netpoll *np); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d50d48a82def..f4b110cb0416 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -496,24 +496,6 @@ int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) } EXPORT_SYMBOL_GPL(netpoll_take_ipv4); -/* - * Test whether the caller left np->local_ip unset, so that - * netpoll_setup() should auto-populate it from the egress device. - * - * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), - * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, - * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — - * doing so would misclassify a caller-supplied address as unset and - * silently overwrite it with whatever address the device exposes. - */ -bool netpoll_local_ip_unset(const struct netpoll *np) -{ - if (np->ipv6) - return ipv6_addr_any(&np->local_ip.in6); - return !np->local_ip.ip; -} -EXPORT_SYMBOL_GPL(netpoll_local_ip_unset); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo = From 3aa9ff037ba3ef12c75a924140955242e750fcbb Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:13 -0700 Subject: [PATCH 5/9] netconsole: move netpoll_take_ipv4() as netcons_take_ipv4() Move netpoll_take_ipv4() to netconsole, which is the only user. Rename it to netcons_take_ipv4() for the netcons_ prefix. The body is unchanged. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-5-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 32 +++++++++++++++++++++++++++++++- include/linux/netpoll.h | 1 - net/core/netpoll.c | 30 ------------------------------ 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 70f9c5bfb372..9506b1e1193f 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -351,6 +352,35 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +/* + * Take the IPv4 from ndev and populate local_ip structure in netpoll + */ +static int netcons_take_ipv4(struct netpoll *np, struct net_device *ndev) +{ + char buf[MAC_ADDR_STR_LEN + 1]; + const struct in_ifaddr *ifa; + struct in_device *in_dev; + + in_dev = __in_dev_get_rtnl(ndev); + if (!in_dev) { + np_err(np, "no IP address for %s, aborting\n", + egress_dev(np, buf, sizeof(buf))); + return -EDESTADDRREQ; + } + + ifa = rtnl_dereference(in_dev->ifa_list); + if (!ifa) { + np_err(np, "no IP address for %s, aborting\n", + egress_dev(np, buf, sizeof(buf))); + return -EDESTADDRREQ; + } + + np->local_ip.ip = ifa->ifa_local; + np_info(np, "local IP %pI4\n", &np->local_ip.ip); + + return 0; +} + /* * Test whether the caller left np->local_ip unset, so that * netcons_netpoll_setup() should auto-populate it from the egress device. @@ -414,7 +444,7 @@ static int netcons_netpoll_setup(struct netpoll *np) if (netcons_local_ip_unset(np)) { if (!np->ipv6) { - err = netpoll_take_ipv4(np, ndev); + err = netcons_take_ipv4(np, ndev); if (err) goto put; } else { diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index da05884f876e..e212cb86a942 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -74,7 +74,6 @@ void netpoll_zap_completion_queue(void); unsigned int netpoll_get_carrier_timeout(void); void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); char *egress_dev(struct netpoll *np, char *buf, size_t bufsz); -int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev); int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev); #ifdef CONFIG_NETPOLL diff --git a/net/core/netpoll.c b/net/core/netpoll.c index f4b110cb0416..60c3faf9ea94 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -466,36 +466,6 @@ int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) } EXPORT_SYMBOL_GPL(netpoll_take_ipv6); -/* - * Take the IPv4 from ndev and populate local_ip structure in netpoll - */ -int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) -{ - char buf[MAC_ADDR_STR_LEN + 1]; - const struct in_ifaddr *ifa; - struct in_device *in_dev; - - in_dev = __in_dev_get_rtnl(ndev); - if (!in_dev) { - np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EDESTADDRREQ; - } - - ifa = rtnl_dereference(in_dev->ifa_list); - if (!ifa) { - np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EDESTADDRREQ; - } - - np->local_ip.ip = ifa->ifa_local; - np_info(np, "local IP %pI4\n", &np->local_ip.ip); - - return 0; -} -EXPORT_SYMBOL_GPL(netpoll_take_ipv4); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo = From 80fcfc3538f3176c823eaf52fcf658d7f210d116 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:14 -0700 Subject: [PATCH 6/9] netconsole: move netpoll_take_ipv6() as netcons_take_ipv6() Move netpoll_take_ipv6() to netconsole, and add netcons_ prefix. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-6-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 44 +++++++++++++++++++++++++++++++++++++++- include/linux/netpoll.h | 1 - net/core/netpoll.c | 42 -------------------------------------- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 9506b1e1193f..b6cbda4722d5 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -352,6 +353,47 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +/* + * Take the IPv6 from ndev and populate local_ip structure in netpoll + */ +static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) +{ + char buf[MAC_ADDR_STR_LEN + 1]; + int err = -EDESTADDRREQ; + struct inet6_dev *idev; + + if (!IS_ENABLED(CONFIG_IPV6)) { + np_err(np, "IPv6 is not supported %s, aborting\n", + egress_dev(np, buf, sizeof(buf))); + return -EINVAL; + } + + idev = __in6_dev_get(ndev); + if (idev) { + struct inet6_ifaddr *ifp; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifp, &idev->addr_list, if_list) { + if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) != + !!(ipv6_addr_type(&np->remote_ip.in6) & IPV6_ADDR_LINKLOCAL)) + continue; + /* Got the IP, let's return */ + np->local_ip.in6 = ifp->addr; + err = 0; + break; + } + read_unlock_bh(&idev->lock); + } + if (err) { + np_err(np, "no IPv6 address for %s, aborting\n", + egress_dev(np, buf, sizeof(buf))); + return err; + } + + np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); + return 0; +} + /* * Take the IPv4 from ndev and populate local_ip structure in netpoll */ @@ -448,7 +490,7 @@ static int netcons_netpoll_setup(struct netpoll *np) if (err) goto put; } else { - err = netpoll_take_ipv6(np, ndev); + err = netcons_take_ipv6(np, ndev); if (err) goto put; } diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index e212cb86a942..7736339f9880 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -74,7 +74,6 @@ void netpoll_zap_completion_queue(void); unsigned int netpoll_get_carrier_timeout(void); void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); char *egress_dev(struct netpoll *np, char *buf, size_t bufsz); -int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 60c3faf9ea94..246aa2bfd780 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -424,48 +424,6 @@ void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) } EXPORT_SYMBOL_GPL(netpoll_wait_carrier); -/* - * Take the IPv6 from ndev and populate local_ip structure in netpoll - */ -int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) -{ - char buf[MAC_ADDR_STR_LEN + 1]; - int err = -EDESTADDRREQ; - struct inet6_dev *idev; - - if (!IS_ENABLED(CONFIG_IPV6)) { - np_err(np, "IPv6 is not supported %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EINVAL; - } - - idev = __in6_dev_get(ndev); - if (idev) { - struct inet6_ifaddr *ifp; - - read_lock_bh(&idev->lock); - list_for_each_entry(ifp, &idev->addr_list, if_list) { - if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) != - !!(ipv6_addr_type(&np->remote_ip.in6) & IPV6_ADDR_LINKLOCAL)) - continue; - /* Got the IP, let's return */ - np->local_ip.in6 = ifp->addr; - err = 0; - break; - } - read_unlock_bh(&idev->lock); - } - if (err) { - np_err(np, "no IPv6 address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return err; - } - - np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); - return 0; -} -EXPORT_SYMBOL_GPL(netpoll_take_ipv6); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo = From cf6de67ba41c2b361cdab4d649f757d817bc6975 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:15 -0700 Subject: [PATCH 7/9] netconsole: move egress_dev() as netcons_egress_dev() move egress_dev() from netpoll to netconsole, and append netcons_ prefix. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-7-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 30 +++++++++++++++++++++++------- include/linux/netpoll.h | 1 - net/core/netpoll.c | 17 ----------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index b6cbda4722d5..fc575adc29bd 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -353,6 +353,22 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +/* + * Returns a pointer to a string representation of the identifier used + * to select the egress interface for the given netpoll instance. buf + * is used to format np->dev_mac when np->dev_name is empty; bufsz must + * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address + * and its NUL terminator. + */ +static char *netcons_egress_dev(struct netpoll *np, char *buf, size_t bufsz) +{ + if (np->dev_name[0]) + return np->dev_name; + + snprintf(buf, bufsz, "%pM", np->dev_mac); + return buf; +} + /* * Take the IPv6 from ndev and populate local_ip structure in netpoll */ @@ -364,7 +380,7 @@ static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) if (!IS_ENABLED(CONFIG_IPV6)) { np_err(np, "IPv6 is not supported %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); return -EINVAL; } @@ -386,7 +402,7 @@ static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) } if (err) { np_err(np, "no IPv6 address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); return err; } @@ -406,14 +422,14 @@ static int netcons_take_ipv4(struct netpoll *np, struct net_device *ndev) in_dev = __in_dev_get_rtnl(ndev); if (!in_dev) { np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); return -EDESTADDRREQ; } ifa = rtnl_dereference(in_dev->ifa_list); if (!ifa) { np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); return -EDESTADDRREQ; } @@ -456,7 +472,7 @@ static int netcons_netpoll_setup(struct netpoll *np) if (!ndev) { np_err(np, "%s doesn't exist, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); err = -ENODEV; goto unlock; } @@ -464,14 +480,14 @@ static int netcons_netpoll_setup(struct netpoll *np) if (netdev_master_upper_dev_get(ndev)) { np_err(np, "%s is a slave device, aborting\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); err = -EBUSY; goto put; } if (!netif_running(ndev)) { np_info(np, "device %s not up yet, forcing it\n", - egress_dev(np, buf, sizeof(buf))); + netcons_egress_dev(np, buf, sizeof(buf))); err = dev_open(ndev, NULL); if (err) { diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 7736339f9880..dac8dc2529de 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -73,7 +73,6 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); void netpoll_zap_completion_queue(void); unsigned int netpoll_get_carrier_timeout(void); void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); -char *egress_dev(struct netpoll *np, char *buf, size_t bufsz); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 246aa2bfd780..f385ae4b70f5 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -392,23 +392,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) } EXPORT_SYMBOL_GPL(__netpoll_setup); -/* - * Returns a pointer to a string representation of the identifier used - * to select the egress interface for the given netpoll instance. buf - * is used to format np->dev_mac when np->dev_name is empty; bufsz must - * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address - * and its NUL terminator. - */ -char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) -{ - if (np->dev_name[0]) - return np->dev_name; - - snprintf(buf, bufsz, "%pM", np->dev_mac); - return buf; -} -EXPORT_SYMBOL_GPL(egress_dev); - void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) { unsigned long atmost; From a1116396476f643b748a1686184c48b44ac1c2e5 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:16 -0700 Subject: [PATCH 8/9] netconsole: move local_ip/remote_ip/ipv6 to netconsole_target With netpoll_setup() and the packet-building path now living in netconsole, local_ip, remote_ip and ipv6 in struct netpoll are read and written only by netconsole. No other netpoll user touches them. Move the three fields into netconsole_target and switch the packet builders and setup helpers to take the target instead of the netpoll handle. struct netpoll is left holding only the device-binding state that the shared netpoll transport needs. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-8-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 143 +++++++++++++++++++++------------------ include/linux/netpoll.h | 3 - 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index fc575adc29bd..199d0e1ac17b 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -177,9 +177,10 @@ enum target_state { * @np: The netpoll structure for this target. * Contains the other userspace visible parameters: * dev_name (read-write) - * local_ip (read-write) - * remote_ip (read-write) * local_mac (read-only) + * @local_ip: Source IP address of the target (read-write). + * @remote_ip: Destination IP address of the target (read-write). + * @ipv6: Whether the target addresses are IPv6 (read-write). * @local_port: Source UDP port of the target (read-write). * @remote_port: Destination UDP port of the target (read-write). * @remote_mac: Destination ethernet address of the target (read-write). @@ -210,6 +211,8 @@ struct netconsole_target { bool extended; bool release; struct netpoll np; + union inet_addr local_ip, remote_ip; + bool ipv6; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; /* protected by target_list_lock; +1 gives scnprintf() room for its @@ -370,11 +373,13 @@ static char *netcons_egress_dev(struct netpoll *np, char *buf, size_t bufsz) } /* - * Take the IPv6 from ndev and populate local_ip structure in netpoll + * Populate the target's local_ip with the IPv6 address from ndev. */ -static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) +static int netcons_take_ipv6(struct netconsole_target *nt, + struct net_device *ndev) { char buf[MAC_ADDR_STR_LEN + 1]; + struct netpoll *np = &nt->np; int err = -EDESTADDRREQ; struct inet6_dev *idev; @@ -391,10 +396,10 @@ static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) read_lock_bh(&idev->lock); list_for_each_entry(ifp, &idev->addr_list, if_list) { if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) != - !!(ipv6_addr_type(&np->remote_ip.in6) & IPV6_ADDR_LINKLOCAL)) + !!(ipv6_addr_type(&nt->remote_ip.in6) & IPV6_ADDR_LINKLOCAL)) continue; /* Got the IP, let's return */ - np->local_ip.in6 = ifp->addr; + nt->local_ip.in6 = ifp->addr; err = 0; break; } @@ -406,16 +411,18 @@ static int netcons_take_ipv6(struct netpoll *np, struct net_device *ndev) return err; } - np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); + np_info(np, "local IPv6 %pI6c\n", &nt->local_ip.in6); return 0; } /* - * Take the IPv4 from ndev and populate local_ip structure in netpoll + * Populate the target's local_ip with the IPv4 address from ndev. */ -static int netcons_take_ipv4(struct netpoll *np, struct net_device *ndev) +static int netcons_take_ipv4(struct netconsole_target *nt, + struct net_device *ndev) { char buf[MAC_ADDR_STR_LEN + 1]; + struct netpoll *np = &nt->np; const struct in_ifaddr *ifa; struct in_device *in_dev; @@ -433,34 +440,35 @@ static int netcons_take_ipv4(struct netpoll *np, struct net_device *ndev) return -EDESTADDRREQ; } - np->local_ip.ip = ifa->ifa_local; - np_info(np, "local IP %pI4\n", &np->local_ip.ip); + nt->local_ip.ip = ifa->ifa_local; + np_info(np, "local IP %pI4\n", &nt->local_ip.ip); return 0; } /* - * Test whether the caller left np->local_ip unset, so that + * Test whether the caller left nt->local_ip unset, so that * netcons_netpoll_setup() should auto-populate it from the egress device. * - * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), + * nt->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — * doing so would misclassify a caller-supplied address as unset and * silently overwrite it with whatever address the device exposes. */ -static bool netcons_local_ip_unset(const struct netpoll *np) +static bool netcons_local_ip_unset(const struct netconsole_target *nt) { - if (np->ipv6) - return ipv6_addr_any(&np->local_ip.in6); - return !np->local_ip.ip; + if (nt->ipv6) + return ipv6_addr_any(&nt->local_ip.in6); + return !nt->local_ip.ip; } -static int netcons_netpoll_setup(struct netpoll *np) +static int netcons_netpoll_setup(struct netconsole_target *nt) { struct net *net = current->nsproxy->net_ns; char buf[MAC_ADDR_STR_LEN + 1]; struct net_device *ndev = NULL; + struct netpoll *np = &nt->np; bool ip_overwritten = false; int err; @@ -500,13 +508,13 @@ static int netcons_netpoll_setup(struct netpoll *np) rtnl_lock(); } - if (netcons_local_ip_unset(np)) { - if (!np->ipv6) { - err = netcons_take_ipv4(np, ndev); + if (netcons_local_ip_unset(nt)) { + if (!nt->ipv6) { + err = netcons_take_ipv4(nt, ndev); if (err) goto put; } else { - err = netcons_take_ipv6(np, ndev); + err = netcons_take_ipv6(nt, ndev); if (err) goto put; } @@ -529,7 +537,7 @@ static int netcons_netpoll_setup(struct netpoll *np) put: DEBUG_NET_WARN_ON_ONCE(np->dev); if (ip_overwritten) - memset(&np->local_ip, 0, sizeof(np->local_ip)); + memset(&nt->local_ip, 0, sizeof(nt->local_ip)); netdev_put(ndev, &np->dev_tracker); unlock: rtnl_unlock(); @@ -546,7 +554,7 @@ static void resume_target(struct netconsole_target *nt) */ netconsole_skb_pool_init(nt); - if (netcons_netpoll_setup(&nt->np)) { + if (netcons_netpoll_setup(nt)) { /* netpoll fails setup once, do not try again. */ netconsole_skb_pool_flush(nt); nt->state = STATE_DISABLED; @@ -691,17 +699,17 @@ static void netconsole_print_banner(struct netconsole_target *nt) struct netpoll *np = &nt->np; np_info(np, "local port %d\n", nt->local_port); - if (np->ipv6) - np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6); + if (nt->ipv6) + np_info(np, "local IPv6 address %pI6c\n", &nt->local_ip.in6); else - np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip); + np_info(np, "local IPv4 address %pI4\n", &nt->local_ip.ip); np_info(np, "interface name '%s'\n", np->dev_name); np_info(np, "local ethernet address '%pM'\n", np->dev_mac); np_info(np, "remote port %d\n", nt->remote_port); - if (np->ipv6) - np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6); + if (nt->ipv6) + np_info(np, "remote IPv6 address %pI6c\n", &nt->remote_ip.in6); else - np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip); + np_info(np, "remote IPv4 address %pI4\n", &nt->remote_ip.ip); np_info(np, "remote ethernet address %pM\n", nt->remote_mac); } @@ -832,20 +840,20 @@ static ssize_t local_ip_show(struct config_item *item, char *buf) { struct netconsole_target *nt = to_target(item); - if (nt->np.ipv6) - return sysfs_emit(buf, "%pI6c\n", &nt->np.local_ip.in6); + if (nt->ipv6) + return sysfs_emit(buf, "%pI6c\n", &nt->local_ip.in6); else - return sysfs_emit(buf, "%pI4\n", &nt->np.local_ip); + return sysfs_emit(buf, "%pI4\n", &nt->local_ip); } static ssize_t remote_ip_show(struct config_item *item, char *buf) { struct netconsole_target *nt = to_target(item); - if (nt->np.ipv6) - return sysfs_emit(buf, "%pI6c\n", &nt->np.remote_ip.in6); + if (nt->ipv6) + return sysfs_emit(buf, "%pI6c\n", &nt->remote_ip.in6); else - return sysfs_emit(buf, "%pI4\n", &nt->np.remote_ip); + return sysfs_emit(buf, "%pI4\n", &nt->remote_ip); } static ssize_t local_mac_show(struct config_item *item, char *buf) @@ -1025,7 +1033,7 @@ static ssize_t enabled_store(struct config_item *item, */ netconsole_skb_pool_init(nt); - ret = netcons_netpoll_setup(&nt->np); + ret = netcons_netpoll_setup(nt); if (ret) { netconsole_skb_pool_flush(nt); goto out_unlock; @@ -1199,10 +1207,10 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf, goto out_unlock; } - ipv6 = netpoll_parse_ip_addr(buf, &nt->np.local_ip); + ipv6 = netpoll_parse_ip_addr(buf, &nt->local_ip); if (ipv6 == -1) goto out_unlock; - nt->np.ipv6 = !!ipv6; + nt->ipv6 = !!ipv6; ret = count; out_unlock: @@ -1224,10 +1232,10 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf, goto out_unlock; } - ipv6 = netpoll_parse_ip_addr(buf, &nt->np.remote_ip); + ipv6 = netpoll_parse_ip_addr(buf, &nt->remote_ip); if (ipv6 == -1) goto out_unlock; - nt->np.ipv6 = !!ipv6; + nt->ipv6 = !!ipv6; ret = count; out_unlock: @@ -2027,8 +2035,8 @@ static struct sk_buff *find_skb(struct netconsole_target *nt, int len, return skb; } -static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb, - int len) +static void netpoll_udp_checksum(struct netconsole_target *nt, + struct sk_buff *skb, int len) { struct udphdr *udph; int udp_len; @@ -2038,14 +2046,14 @@ static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb, /* check needs to be set, since it will be consumed in csum_partial */ udph->check = 0; - if (np->ipv6) - udph->check = csum_ipv6_magic(&np->local_ip.in6, - &np->remote_ip.in6, + if (nt->ipv6) + udph->check = csum_ipv6_magic(&nt->local_ip.in6, + &nt->remote_ip.in6, udp_len, IPPROTO_UDP, csum_partial(udph, udp_len, 0)); else - udph->check = csum_tcpudp_magic(np->local_ip.ip, - np->remote_ip.ip, + udph->check = csum_tcpudp_magic(nt->local_ip.ip, + nt->remote_ip.ip, udp_len, IPPROTO_UDP, csum_partial(udph, udp_len, 0)); if (udph->check == 0) @@ -2054,7 +2062,6 @@ static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb, static void push_udp(struct netconsole_target *nt, struct sk_buff *skb, int len) { - struct netpoll *np = &nt->np; struct udphdr *udph; int udp_len; @@ -2068,7 +2075,7 @@ static void push_udp(struct netconsole_target *nt, struct sk_buff *skb, int len) udph->dest = htons(nt->remote_port); udp_set_len_short(udph, udp_len); - netpoll_udp_checksum(np, skb, len); + netpoll_udp_checksum(nt, skb, len); } static void push_eth(struct netconsole_target *nt, struct sk_buff *skb) @@ -2080,13 +2087,14 @@ static void push_eth(struct netconsole_target *nt, struct sk_buff *skb) skb_reset_mac_header(skb); ether_addr_copy(eth->h_source, np->dev->dev_addr); ether_addr_copy(eth->h_dest, nt->remote_mac); - if (np->ipv6) + if (nt->ipv6) eth->h_proto = htons(ETH_P_IPV6); else eth->h_proto = htons(ETH_P_IP); } -static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len) +static void push_ipv4(struct netconsole_target *nt, struct sk_buff *skb, + int len) { static atomic_t ip_ident; struct iphdr *iph; @@ -2107,13 +2115,14 @@ static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len) iph->ttl = 64; iph->protocol = IPPROTO_UDP; iph->check = 0; - put_unaligned(np->local_ip.ip, &iph->saddr); - put_unaligned(np->remote_ip.ip, &iph->daddr); + put_unaligned(nt->local_ip.ip, &iph->saddr); + put_unaligned(nt->remote_ip.ip, &iph->daddr); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); skb->protocol = htons(ETH_P_IP); } -static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len) +static void push_ipv6(struct netconsole_target *nt, struct sk_buff *skb, + int len) { struct ipv6hdr *ip6h; @@ -2130,8 +2139,8 @@ static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len) ip6h->payload_len = htons(sizeof(struct udphdr) + len); ip6h->nexthdr = IPPROTO_UDP; ip6h->hop_limit = 32; - ip6h->saddr = np->local_ip.in6; - ip6h->daddr = np->remote_ip.in6; + ip6h->saddr = nt->local_ip.in6; + ip6h->daddr = nt->remote_ip.in6; skb->protocol = htons(ETH_P_IPV6); } @@ -2147,7 +2156,7 @@ static int netpoll_send_udp(struct netconsole_target *nt, const char *msg, WARN_ON_ONCE(!irqs_disabled()); udp_len = len + sizeof(struct udphdr); - if (np->ipv6) + if (nt->ipv6) ip_len = udp_len + sizeof(struct ipv6hdr); else ip_len = udp_len + sizeof(struct iphdr); @@ -2163,10 +2172,10 @@ static int netpoll_send_udp(struct netconsole_target *nt, const char *msg, skb_put(skb, len); push_udp(nt, skb, len); - if (np->ipv6) - push_ipv6(np, skb, len); + if (nt->ipv6) + push_ipv6(nt, skb, len); else - push_ipv4(np, skb, len); + push_ipv4(nt, skb, len); push_eth(nt, skb); skb->dev = np->dev; @@ -2505,11 +2514,11 @@ static int netconsole_parser_cmdline(struct netconsole_target *nt, char *opt) if (!delim) goto parse_failed; *delim = 0; - ipv6 = netpoll_parse_ip_addr(cur, &np->local_ip); + ipv6 = netpoll_parse_ip_addr(cur, &nt->local_ip); if (ipv6 < 0) goto parse_failed; else - np->ipv6 = (bool)ipv6; + nt->ipv6 = (bool)ipv6; cur = delim; } cur++; @@ -2551,13 +2560,13 @@ static int netconsole_parser_cmdline(struct netconsole_target *nt, char *opt) if (!delim) goto parse_failed; *delim = 0; - ipv6 = netpoll_parse_ip_addr(cur, &np->remote_ip); + ipv6 = netpoll_parse_ip_addr(cur, &nt->remote_ip); if (ipv6 < 0) goto parse_failed; - else if (ipversion_set && np->ipv6 != (bool)ipv6) + else if (ipversion_set && nt->ipv6 != (bool)ipv6) goto parse_failed; else - np->ipv6 = (bool)ipv6; + nt->ipv6 = (bool)ipv6; cur = delim + 1; if (*cur != 0) { @@ -2615,7 +2624,7 @@ static struct netconsole_target *alloc_param_target(char *target_config, */ netconsole_skb_pool_init(nt); - err = netcons_netpoll_setup(&nt->np); + err = netcons_netpoll_setup(nt); if (err) { pr_err("Not enabling netconsole for %s%d. Netpoll setup failed\n", NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count); diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index dac8dc2529de..ef88a30b11f4 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -32,9 +32,6 @@ struct netpoll { char dev_name[IFNAMSIZ]; u8 dev_mac[ETH_ALEN]; const char *name; - - union inet_addr local_ip, remote_ip; - bool ipv6; }; #define np_info(np, fmt, ...) \ From dc50a5c9cdeae6b71754446fae84b4d831957f85 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 24 Jul 2026 08:04:17 -0700 Subject: [PATCH 9/9] netconsole: move netpoll_wait_carrier() as netcons_wait_carrier() netpoll_wait_carrier() waits for the egress device carrier during netconsole setup. Its only caller, netcons_netpoll_setup(), already lives in netconsole. Move the function into drivers/net/netconsole.c, drop EXPORT_SYMBOL_GPL() and remove the prototype from . Rename it to netcons_wait_carrier() for the netcons_ prefix. It now reads the timeout through netpoll_get_carrier_timeout(), since carrier_timeout stays in netpoll to keep the netpoll.carrier_timeout parameter. Signed-off-by: Breno Leitao Reviewed-by: Gustavo Luiz Duarte Link: https://patch.msgid.link/20260724-netconsole_move_more_final-v1-9-a5f7691db81c@debian.org Signed-off-by: Paolo Abeni --- drivers/net/netconsole.c | 17 ++++++++++++++++- include/linux/netpoll.h | 1 - net/core/netpoll.c | 15 --------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 199d0e1ac17b..03913302328c 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -48,6 +48,7 @@ #include #include #include +#include MODULE_AUTHOR("Matt Mackall "); MODULE_DESCRIPTION("Console driver for network interfaces"); @@ -356,6 +357,20 @@ static void netconsole_skb_pool_flush(struct netconsole_target *nt) skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED); } +static void netcons_wait_carrier(struct netpoll *np, struct net_device *ndev) +{ + unsigned long atmost; + + atmost = jiffies + netpoll_get_carrier_timeout() * HZ; + while (!netif_carrier_ok(ndev)) { + if (time_after(jiffies, atmost)) { + np_notice(np, "timeout waiting for carrier\n"); + break; + } + msleep(1); + } +} + /* * Returns a pointer to a string representation of the identifier used * to select the egress interface for the given netpoll instance. buf @@ -504,7 +519,7 @@ static int netcons_netpoll_setup(struct netconsole_target *nt) } rtnl_unlock(); - netpoll_wait_carrier(np, ndev); + netcons_wait_carrier(np, ndev); rtnl_lock(); } diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index ef88a30b11f4..1c6b1eec5efd 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -69,7 +69,6 @@ void do_netpoll_cleanup(struct netpoll *np); netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); void netpoll_zap_completion_queue(void); unsigned int netpoll_get_carrier_timeout(void); -void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index f385ae4b70f5..fe1e0cda5d6b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -392,21 +392,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) } EXPORT_SYMBOL_GPL(__netpoll_setup); -void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev) -{ - unsigned long atmost; - - atmost = jiffies + carrier_timeout * HZ; - while (!netif_carrier_ok(ndev)) { - if (time_after(jiffies, atmost)) { - np_notice(np, "timeout waiting for carrier\n"); - break; - } - msleep(1); - } -} -EXPORT_SYMBOL_GPL(netpoll_wait_carrier); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo =