Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'netpoll-untangle-netconsole-and-netpoll'

Breno Leitao says:

====================
netpoll: Untangle netconsole and netpoll

Initially netpoll and netconsole were created together, and some
functions are in the wrong file. Seperate netconsole-only functions
in netconsole, avoiding exports.

1. Expose netpoll logging macros in the public header to enable consistent
log formatting across netpoll consumers.

2. Relocate netconsole-specific functions from netpoll to the netconsole
module where they are actually used, reducing unnecessary coupling.

3. Remove unnecessary function exports

4. Rename netpoll parsing functions in netconsole to better reflect their
specific usage.

5. Create a test to check that cmdline works fine. This was in my todo
list since [1], this was a good time to add it here to make sure this
patchset doesn't regress.

PS: The code was split in a way that it is easy to review. When copying
the functions from netpoll to netconsole, I do not change than other
than adding `static`. This will make checkpatch unhappy, but, further
patches will address the issues. It is done this way to make it easy for
reviewers.

Link: https://lore.kernel.org/netdev/Z36TlACdNMwFD7wv@dev-ushankar.dev.purestorage.com/ [1]

v2: https://lore.kernel.org/20250611-rework-v2-0-ab1d92b458ca@debian.org
v1: https://lore.kernel.org/20250610-rework-v1-0-7cfde283f246@debian.org
====================

Link: https://patch.msgid.link/20250613-rework-v3-0-0752bf2e6912@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+240 -155
+134 -3
drivers/net/netconsole.c
··· 278 278 mutex_unlock(&target_cleanup_list_lock); 279 279 } 280 280 281 + static void netconsole_print_banner(struct netpoll *np) 282 + { 283 + np_info(np, "local port %d\n", np->local_port); 284 + if (np->ipv6) 285 + np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6); 286 + else 287 + np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip); 288 + np_info(np, "interface name '%s'\n", np->dev_name); 289 + np_info(np, "local ethernet address '%pM'\n", np->dev_mac); 290 + np_info(np, "remote port %d\n", np->remote_port); 291 + if (np->ipv6) 292 + np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6); 293 + else 294 + np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip); 295 + np_info(np, "remote ethernet address %pM\n", np->remote_mac); 296 + } 297 + 281 298 #ifdef CONFIG_NETCONSOLE_DYNAMIC 282 299 283 300 /* ··· 551 534 } 552 535 553 536 /* 554 - * Skip netpoll_parse_options() -- all the attributes are 537 + * Skip netconsole_parser_cmdline() -- all the attributes are 555 538 * already configured via configfs. Just print them out. 556 539 */ 557 - netpoll_print_options(&nt->np); 540 + netconsole_print_banner(&nt->np); 558 541 559 542 ret = netpoll_setup(&nt->np); 560 543 if (ret) ··· 1676 1659 spin_unlock_irqrestore(&target_list_lock, flags); 1677 1660 } 1678 1661 1662 + static int netpoll_parse_ip_addr(const char *str, union inet_addr *addr) 1663 + { 1664 + const char *end; 1665 + 1666 + if (!strchr(str, ':') && 1667 + in4_pton(str, -1, (void *)addr, -1, &end) > 0) { 1668 + if (!*end) 1669 + return 0; 1670 + } 1671 + if (in6_pton(str, -1, addr->in6.s6_addr, -1, &end) > 0) { 1672 + #if IS_ENABLED(CONFIG_IPV6) 1673 + if (!*end) 1674 + return 1; 1675 + #else 1676 + return -1; 1677 + #endif 1678 + } 1679 + return -1; 1680 + } 1681 + 1682 + static int netconsole_parser_cmdline(struct netpoll *np, char *opt) 1683 + { 1684 + bool ipversion_set = false; 1685 + char *cur = opt; 1686 + char *delim; 1687 + int ipv6; 1688 + 1689 + if (*cur != '@') { 1690 + delim = strchr(cur, '@'); 1691 + if (!delim) 1692 + goto parse_failed; 1693 + *delim = 0; 1694 + if (kstrtou16(cur, 10, &np->local_port)) 1695 + goto parse_failed; 1696 + cur = delim; 1697 + } 1698 + cur++; 1699 + 1700 + if (*cur != '/') { 1701 + ipversion_set = true; 1702 + delim = strchr(cur, '/'); 1703 + if (!delim) 1704 + goto parse_failed; 1705 + *delim = 0; 1706 + ipv6 = netpoll_parse_ip_addr(cur, &np->local_ip); 1707 + if (ipv6 < 0) 1708 + goto parse_failed; 1709 + else 1710 + np->ipv6 = (bool)ipv6; 1711 + cur = delim; 1712 + } 1713 + cur++; 1714 + 1715 + if (*cur != ',') { 1716 + /* parse out dev_name or dev_mac */ 1717 + delim = strchr(cur, ','); 1718 + if (!delim) 1719 + goto parse_failed; 1720 + *delim = 0; 1721 + 1722 + np->dev_name[0] = '\0'; 1723 + eth_broadcast_addr(np->dev_mac); 1724 + if (!strchr(cur, ':')) 1725 + strscpy(np->dev_name, cur, sizeof(np->dev_name)); 1726 + else if (!mac_pton(cur, np->dev_mac)) 1727 + goto parse_failed; 1728 + 1729 + cur = delim; 1730 + } 1731 + cur++; 1732 + 1733 + if (*cur != '@') { 1734 + /* dst port */ 1735 + delim = strchr(cur, '@'); 1736 + if (!delim) 1737 + goto parse_failed; 1738 + *delim = 0; 1739 + if (*cur == ' ' || *cur == '\t') 1740 + np_info(np, "warning: whitespace is not allowed\n"); 1741 + if (kstrtou16(cur, 10, &np->remote_port)) 1742 + goto parse_failed; 1743 + cur = delim; 1744 + } 1745 + cur++; 1746 + 1747 + /* dst ip */ 1748 + delim = strchr(cur, '/'); 1749 + if (!delim) 1750 + goto parse_failed; 1751 + *delim = 0; 1752 + ipv6 = netpoll_parse_ip_addr(cur, &np->remote_ip); 1753 + if (ipv6 < 0) 1754 + goto parse_failed; 1755 + else if (ipversion_set && np->ipv6 != (bool)ipv6) 1756 + goto parse_failed; 1757 + else 1758 + np->ipv6 = (bool)ipv6; 1759 + cur = delim + 1; 1760 + 1761 + if (*cur != 0) { 1762 + /* MAC address */ 1763 + if (!mac_pton(cur, np->remote_mac)) 1764 + goto parse_failed; 1765 + } 1766 + 1767 + netconsole_print_banner(np); 1768 + 1769 + return 0; 1770 + 1771 + parse_failed: 1772 + np_info(np, "couldn't parse config at '%s'!\n", cur); 1773 + return -1; 1774 + } 1775 + 1679 1776 /* Allocate new target (from boot/module param) and setup netpoll for it */ 1680 1777 static struct netconsole_target *alloc_param_target(char *target_config, 1681 1778 int cmdline_count) ··· 1819 1688 } 1820 1689 1821 1690 /* Parse parameters and setup netpoll */ 1822 - err = netpoll_parse_options(&nt->np, target_config); 1691 + err = netconsole_parser_cmdline(&nt->np, target_config); 1823 1692 if (err) 1824 1693 goto fail; 1825 1694
+7 -3
include/linux/netpoll.h
··· 42 42 struct work_struct refill_wq; 43 43 }; 44 44 45 + #define np_info(np, fmt, ...) \ 46 + pr_info("%s: " fmt, np->name, ##__VA_ARGS__) 47 + #define np_err(np, fmt, ...) \ 48 + pr_err("%s: " fmt, np->name, ##__VA_ARGS__) 49 + #define np_notice(np, fmt, ...) \ 50 + pr_notice("%s: " fmt, np->name, ##__VA_ARGS__) 51 + 45 52 struct netpoll_info { 46 53 refcount_t refcnt; 47 54 ··· 72 65 #endif 73 66 74 67 int netpoll_send_udp(struct netpoll *np, const char *msg, int len); 75 - void netpoll_print_options(struct netpoll *np); 76 - int netpoll_parse_options(struct netpoll *np, char *opt); 77 68 int __netpoll_setup(struct netpoll *np, struct net_device *ndev); 78 69 int netpoll_setup(struct netpoll *np); 79 - void __netpoll_cleanup(struct netpoll *np); 80 70 void __netpoll_free(struct netpoll *np); 81 71 void netpoll_cleanup(struct netpoll *np); 82 72 void do_netpoll_cleanup(struct netpoll *np);
+1 -135
net/core/netpoll.c
··· 58 58 static unsigned int carrier_timeout = 4; 59 59 module_param(carrier_timeout, uint, 0644); 60 60 61 - #define np_info(np, fmt, ...) \ 62 - pr_info("%s: " fmt, np->name, ##__VA_ARGS__) 63 - #define np_err(np, fmt, ...) \ 64 - pr_err("%s: " fmt, np->name, ##__VA_ARGS__) 65 - #define np_notice(np, fmt, ...) \ 66 - pr_notice("%s: " fmt, np->name, ##__VA_ARGS__) 67 - 68 61 static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb, 69 62 struct net_device *dev, 70 63 struct netdev_queue *txq) ··· 492 499 } 493 500 EXPORT_SYMBOL(netpoll_send_udp); 494 501 495 - void netpoll_print_options(struct netpoll *np) 496 - { 497 - np_info(np, "local port %d\n", np->local_port); 498 - if (np->ipv6) 499 - np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6); 500 - else 501 - np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip); 502 - np_info(np, "interface name '%s'\n", np->dev_name); 503 - np_info(np, "local ethernet address '%pM'\n", np->dev_mac); 504 - np_info(np, "remote port %d\n", np->remote_port); 505 - if (np->ipv6) 506 - np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6); 507 - else 508 - np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip); 509 - np_info(np, "remote ethernet address %pM\n", np->remote_mac); 510 - } 511 - EXPORT_SYMBOL(netpoll_print_options); 512 - 513 - static int netpoll_parse_ip_addr(const char *str, union inet_addr *addr) 514 - { 515 - const char *end; 516 - 517 - if (!strchr(str, ':') && 518 - in4_pton(str, -1, (void *)addr, -1, &end) > 0) { 519 - if (!*end) 520 - return 0; 521 - } 522 - if (in6_pton(str, -1, addr->in6.s6_addr, -1, &end) > 0) { 523 - #if IS_ENABLED(CONFIG_IPV6) 524 - if (!*end) 525 - return 1; 526 - #else 527 - return -1; 528 - #endif 529 - } 530 - return -1; 531 - } 532 502 533 503 static void skb_pool_flush(struct netpoll *np) 534 504 { ··· 501 545 skb_pool = &np->skb_pool; 502 546 skb_queue_purge_reason(skb_pool, SKB_CONSUMED); 503 547 } 504 - 505 - int netpoll_parse_options(struct netpoll *np, char *opt) 506 - { 507 - char *cur=opt, *delim; 508 - int ipv6; 509 - bool ipversion_set = false; 510 - 511 - if (*cur != '@') { 512 - if ((delim = strchr(cur, '@')) == NULL) 513 - goto parse_failed; 514 - *delim = 0; 515 - if (kstrtou16(cur, 10, &np->local_port)) 516 - goto parse_failed; 517 - cur = delim; 518 - } 519 - cur++; 520 - 521 - if (*cur != '/') { 522 - ipversion_set = true; 523 - if ((delim = strchr(cur, '/')) == NULL) 524 - goto parse_failed; 525 - *delim = 0; 526 - ipv6 = netpoll_parse_ip_addr(cur, &np->local_ip); 527 - if (ipv6 < 0) 528 - goto parse_failed; 529 - else 530 - np->ipv6 = (bool)ipv6; 531 - cur = delim; 532 - } 533 - cur++; 534 - 535 - if (*cur != ',') { 536 - /* parse out dev_name or dev_mac */ 537 - if ((delim = strchr(cur, ',')) == NULL) 538 - goto parse_failed; 539 - *delim = 0; 540 - 541 - np->dev_name[0] = '\0'; 542 - eth_broadcast_addr(np->dev_mac); 543 - if (!strchr(cur, ':')) 544 - strscpy(np->dev_name, cur, sizeof(np->dev_name)); 545 - else if (!mac_pton(cur, np->dev_mac)) 546 - goto parse_failed; 547 - 548 - cur = delim; 549 - } 550 - cur++; 551 - 552 - if (*cur != '@') { 553 - /* dst port */ 554 - if ((delim = strchr(cur, '@')) == NULL) 555 - goto parse_failed; 556 - *delim = 0; 557 - if (*cur == ' ' || *cur == '\t') 558 - np_info(np, "warning: whitespace is not allowed\n"); 559 - if (kstrtou16(cur, 10, &np->remote_port)) 560 - goto parse_failed; 561 - cur = delim; 562 - } 563 - cur++; 564 - 565 - /* dst ip */ 566 - if ((delim = strchr(cur, '/')) == NULL) 567 - goto parse_failed; 568 - *delim = 0; 569 - ipv6 = netpoll_parse_ip_addr(cur, &np->remote_ip); 570 - if (ipv6 < 0) 571 - goto parse_failed; 572 - else if (ipversion_set && np->ipv6 != (bool)ipv6) 573 - goto parse_failed; 574 - else 575 - np->ipv6 = (bool)ipv6; 576 - cur = delim + 1; 577 - 578 - if (*cur != 0) { 579 - /* MAC address */ 580 - if (!mac_pton(cur, np->remote_mac)) 581 - goto parse_failed; 582 - } 583 - 584 - netpoll_print_options(np); 585 - 586 - return 0; 587 - 588 - parse_failed: 589 - np_info(np, "couldn't parse config at '%s'!\n", cur); 590 - return -1; 591 - } 592 - EXPORT_SYMBOL(netpoll_parse_options); 593 548 594 549 static void refill_skbs_work_handler(struct work_struct *work) 595 550 { ··· 730 863 kfree(npinfo); 731 864 } 732 865 733 - void __netpoll_cleanup(struct netpoll *np) 866 + static void __netpoll_cleanup(struct netpoll *np) 734 867 { 735 868 struct netpoll_info *npinfo; 736 869 ··· 752 885 753 886 skb_pool_flush(np); 754 887 } 755 - EXPORT_SYMBOL_GPL(__netpoll_cleanup); 756 888 757 889 void __netpoll_free(struct netpoll *np) 758 890 {
+1
tools/testing/selftests/drivers/net/Makefile
··· 12 12 TEST_PROGS := \ 13 13 napi_id.py \ 14 14 netcons_basic.sh \ 15 + netcons_cmdline.sh \ 15 16 netcons_fragmented_msg.sh \ 16 17 netcons_overflow.sh \ 17 18 netcons_sysdata.sh \
+45 -14
tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh
··· 121 121 echo 1 > "${NETCONS_PATH}"/enabled 122 122 } 123 123 124 + # Generate the command line argument for netconsole following: 125 + # netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr] 126 + function create_cmdline_str() { 127 + DSTMAC=$(ip netns exec "${NAMESPACE}" \ 128 + ip link show "${DSTIF}" | awk '/ether/ {print $2}') 129 + SRCPORT="1514" 130 + TGTPORT="6666" 131 + 132 + echo "netconsole=\"+${SRCPORT}@${SRCIP}/${SRCIF},${TGTPORT}@${DSTIP}/${DSTMAC}\"" 133 + } 134 + 124 135 # Do not append the release to the header of the message 125 136 function disable_release_append() { 126 137 echo 0 > "${NETCONS_PATH}"/enabled ··· 139 128 echo 1 > "${NETCONS_PATH}"/enabled 140 129 } 141 130 142 - function cleanup() { 131 + function do_cleanup() { 143 132 local NSIM_DEV_SYS_DEL="/sys/bus/netdevsim/del_device" 144 - 145 - # delete netconsole dynamic reconfiguration 146 - echo 0 > "${NETCONS_PATH}"/enabled 147 - # Remove all the keys that got created during the selftest 148 - find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete 149 - # Remove the configfs entry 150 - rmdir "${NETCONS_PATH}" 151 133 152 134 # Delete netdevsim devices 153 135 echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_DEL" ··· 151 147 152 148 # Restoring printk configurations 153 149 echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk 150 + } 151 + 152 + function cleanup() { 153 + # delete netconsole dynamic reconfiguration 154 + echo 0 > "${NETCONS_PATH}"/enabled 155 + # Remove all the keys that got created during the selftest 156 + find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete 157 + # Remove the configfs entry 158 + rmdir "${NETCONS_PATH}" 159 + 160 + do_cleanup 154 161 } 155 162 156 163 function set_user_data() { ··· 184 169 socat UDP-LISTEN:"${PORT}",fork "${OUTPUT}" 185 170 } 186 171 187 - function validate_result() { 172 + # Only validate that the message arrived properly 173 + function validate_msg() { 188 174 local TMPFILENAME="$1" 189 - local FORMAT=${2:-"extended"} 190 - 191 - # TMPFILENAME will contain something like: 192 - # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM 193 - # key=value 194 175 195 176 # Check if the file exists 196 177 if [ ! -f "$TMPFILENAME" ]; then ··· 199 188 cat "${TMPFILENAME}" >&2 200 189 exit "${ksft_fail}" 201 190 fi 191 + } 192 + 193 + # Validate the message and userdata 194 + function validate_result() { 195 + local TMPFILENAME="$1" 196 + 197 + # TMPFILENAME will contain something like: 198 + # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM 199 + # key=value 200 + 201 + validate_msg "${TMPFILENAME}" 202 202 203 203 # userdata is not supported on basic format target, 204 204 # thus, do not validate it. ··· 284 262 set +e 285 263 pkill -f "${PROCESS_NAME}" 286 264 set -e 265 + } 266 + 267 + # Check if netconsole was compiled as a module, otherwise exit 268 + function check_netconsole_module() { 269 + if modinfo netconsole | grep filename: | grep -q builtin 270 + then 271 + echo "SKIP: netconsole should be compiled as a module" >&2 272 + exit "${ksft_skip}" 273 + fi 287 274 }
+52
tools/testing/selftests/drivers/net/netcons_cmdline.sh
··· 1 + #!/usr/bin/env bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + # This is a selftest to test cmdline arguments on netconsole. 5 + # It exercises loading of netconsole from cmdline instead of the dynamic 6 + # reconfiguration. This includes parsing the long netconsole= line and all the 7 + # flow through init_netconsole(). 8 + # 9 + # Author: Breno Leitao <leitao@debian.org> 10 + 11 + set -euo pipefail 12 + 13 + SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") 14 + 15 + source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh 16 + 17 + check_netconsole_module 18 + 19 + modprobe netdevsim 2> /dev/null || true 20 + rmmod netconsole 2> /dev/null || true 21 + 22 + # The content of kmsg will be save to the following file 23 + OUTPUT_FILE="/tmp/${TARGET}" 24 + 25 + # Check for basic system dependency and exit if not found 26 + # check_for_dependencies 27 + # Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) 28 + echo "6 5" > /proc/sys/kernel/printk 29 + # Remove the namespace and network interfaces 30 + trap do_cleanup EXIT 31 + # Create one namespace and two interfaces 32 + set_network 33 + # Create the command line for netconsole, with the configuration from the 34 + # function above 35 + CMDLINE="$(create_cmdline_str)" 36 + 37 + # Load the module, with the cmdline set 38 + modprobe netconsole "${CMDLINE}" 39 + 40 + # Listed for netconsole port inside the namespace and destination interface 41 + listen_port_and_save_to "${OUTPUT_FILE}" & 42 + # Wait for socat to start and listen to the port. 43 + wait_local_port_listen "${NAMESPACE}" "${PORT}" udp 44 + # Send the message 45 + echo "${MSG}: ${TARGET}" > /dev/kmsg 46 + # Wait until socat saves the file to disk 47 + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" 48 + # Make sure the message was received in the dst part 49 + # and exit 50 + validate_msg "${OUTPUT_FILE}" 51 + 52 + exit "${ksft_pass}"