Merge branch 'net-netpoll-fix-memory-leak-and-add-comprehensive-selftests'

Breno Leitao says:

====================
net: netpoll: fix memory leak and add comprehensive selftests

Fix a memory leak in netpoll and introduce netconsole selftests that
expose the issue when running with kmemleak detection enabled.

This patchset includes a selftest for netpoll with multiple concurrent
users (netconsole + bonding), which simulates the scenario from test[1]
that originally demonstrated the issue allegedly fixed by commit
efa95b01da ("netpoll: fix use after free") - a commit that is now
being reverted.

Sending this to "net" branch because this is a fix, and the selftest
might help with the backports validation.

Link: https://lore.kernel.org/lkml/96b940137a50e5c387687bb4f57de8b0435a653f.1404857349.git.decot@googlers.com/ [1]
====================

Link: https://patch.msgid.link/20251107-netconsole_torture-v10-0-749227b55f63@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-11-10 18:34:46 -08:00
7 changed files with 566 additions and 17 deletions

View File

@@ -811,6 +811,10 @@ static void __netpoll_cleanup(struct netpoll *np)
if (!npinfo) if (!npinfo)
return; return;
/* At this point, there is a single npinfo instance per netdevice, and
* its refcnt tracks how many netpoll structures are linked to it. We
* only perform npinfo cleanup when the refcnt decrements to zero.
*/
if (refcount_dec_and_test(&npinfo->refcnt)) { if (refcount_dec_and_test(&npinfo->refcnt)) {
const struct net_device_ops *ops; const struct net_device_ops *ops;
@@ -820,8 +824,7 @@ static void __netpoll_cleanup(struct netpoll *np)
RCU_INIT_POINTER(np->dev->npinfo, NULL); RCU_INIT_POINTER(np->dev->npinfo, NULL);
call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info);
} else }
RCU_INIT_POINTER(np->dev->npinfo, NULL);
skb_pool_flush(np); skb_pool_flush(np);
} }

View File

@@ -18,6 +18,7 @@ TEST_PROGS := \
netcons_fragmented_msg.sh \ netcons_fragmented_msg.sh \
netcons_overflow.sh \ netcons_overflow.sh \
netcons_sysdata.sh \ netcons_sysdata.sh \
netcons_torture.sh \
netpoll_basic.py \ netpoll_basic.py \
ping.py \ ping.py \
psp.py \ psp.py \

View File

@@ -14,6 +14,7 @@ TEST_PROGS := \
dev_addr_lists.sh \ dev_addr_lists.sh \
mode-1-recovery-updelay.sh \ mode-1-recovery-updelay.sh \
mode-2-recovery-updelay.sh \ mode-2-recovery-updelay.sh \
netcons_over_bonding.sh \
# end of TEST_PROGS # end of TEST_PROGS
TEST_FILES := \ TEST_FILES := \
@@ -24,6 +25,7 @@ TEST_FILES := \
TEST_INCLUDES := \ TEST_INCLUDES := \
../../../net/lib.sh \ ../../../net/lib.sh \
../lib/sh/lib_netcons.sh \
../../../net/forwarding/lib.sh \ ../../../net/forwarding/lib.sh \
# end of TEST_INCLUDES # end of TEST_INCLUDES

View File

@@ -1,5 +1,6 @@
CONFIG_BONDING=y CONFIG_BONDING=y
CONFIG_BRIDGE=y CONFIG_BRIDGE=y
CONFIG_CONFIGFS_FS=y
CONFIG_DUMMY=y CONFIG_DUMMY=y
CONFIG_INET_ESP=y CONFIG_INET_ESP=y
CONFIG_INET_ESP_OFFLOAD=y CONFIG_INET_ESP_OFFLOAD=y
@@ -9,6 +10,9 @@ CONFIG_MACVLAN=y
CONFIG_NET_ACT_GACT=y CONFIG_NET_ACT_GACT=y
CONFIG_NET_CLS_FLOWER=y CONFIG_NET_CLS_FLOWER=y
CONFIG_NET_CLS_MATCHALL=m CONFIG_NET_CLS_MATCHALL=m
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m CONFIG_NETDEVSIM=m
CONFIG_NET_SCH_INGRESS=y CONFIG_NET_SCH_INGRESS=y
CONFIG_NLMON=y CONFIG_NLMON=y

View File

@@ -0,0 +1,361 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
#
# This selftest exercises trying to have multiple netpoll users at the same
# time.
#
# This selftest has multiple smalls test inside, and the goal is to
# get interfaces with bonding and netconsole in different orders in order
# to catch any possible issue.
#
# The main test composes of four interfaces being created using netdevsim; two
# of them are bonded to serve as the netconsole's transmit interface. The
# remaining two interfaces are similarly bonded and assigned to a separate
# network namespace, which acts as the receive interface, where socat monitors
# for incoming messages.
#
# A netconsole message is then sent to ensure it is properly received across
# this configuration.
#
# Later, run a few other tests, to make sure that bonding and netconsole
# cannot coexist.
#
# The test's objective is to exercise netpoll usage when managed simultaneously
# by multiple subsystems (netconsole and bonding).
#
# Author: Breno Leitao <leitao@debian.org>
set -euo pipefail
SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh
modprobe netdevsim 2> /dev/null || true
modprobe netconsole 2> /dev/null || true
modprobe bonding 2> /dev/null || true
modprobe veth 2> /dev/null || true
# The content of kmsg will be save to the following file
OUTPUT_FILE="/tmp/${TARGET}"
# Check for basic system dependency and exit if not found
check_for_dependencies
# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
echo "6 5" > /proc/sys/kernel/printk
# Remove the namespace, interfaces and netconsole target on exit
trap cleanup_bond EXIT
FORMAT="extended"
IP_VERSION="ipv4"
VETH0="veth"$(( RANDOM % 256))
VETH1="veth"$((256 + RANDOM % 256))
TXNS=""
RXNS=""
# Create "bond_tx_XX" and "bond_rx_XX" interfaces, and set DSTIF and SRCIF with
# the bonding interfaces
function setup_bonding_ifaces() {
local RAND=$(( RANDOM % 100 ))
BOND_TX_MAIN_IF="bond_tx_$RAND"
BOND_RX_MAIN_IF="bond_rx_$RAND"
# Setup TX
if ! ip -n "${TXNS}" link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr
then
echo "Failed to create bond TX interface. Is CONFIG_BONDING set?" >&2
# only clean nsim ifaces and namespace. Nothing else has been
# initialized
cleanup_bond_nsim
trap - EXIT
exit "${ksft_skip}"
fi
# create_netdevsim() got the interface up, but it needs to be down
# before being enslaved.
ip -n "${TXNS}" \
link set "${BOND_TX1_SLAVE_IF}" down
ip -n "${TXNS}" \
link set "${BOND_TX2_SLAVE_IF}" down
ip -n "${TXNS}" \
link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
ip -n "${TXNS}" \
link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
ip -n "${TXNS}" \
link set "${BOND_TX_MAIN_IF}" up
# Setup RX
ip -n "${RXNS}" \
link add "${BOND_RX_MAIN_IF}" type bond mode balance-rr
ip -n "${RXNS}" \
link set "${BOND_RX1_SLAVE_IF}" down
ip -n "${RXNS}" \
link set "${BOND_RX2_SLAVE_IF}" down
ip -n "${RXNS}" \
link set "${BOND_RX1_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
ip -n "${RXNS}" \
link set "${BOND_RX2_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
ip -n "${RXNS}" \
link set "${BOND_RX_MAIN_IF}" up
export DSTIF="${BOND_RX_MAIN_IF}"
export SRCIF="${BOND_TX_MAIN_IF}"
}
# Create 4 netdevsim interfaces. Two of them will be bound to TX bonding iface
# and the other two will be bond to the RX interface (on the other namespace)
function create_ifaces_bond() {
BOND_TX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_1}" "${TXNS}")
BOND_TX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_2}" "${TXNS}")
BOND_RX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_1}" "${RXNS}")
BOND_RX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_2}" "${RXNS}")
}
# netdevsim link BOND_TX to BOND_RX interfaces
function link_ifaces_bond() {
local BOND_TX1_SLAVE_IFIDX
local BOND_TX2_SLAVE_IFIDX
local BOND_RX1_SLAVE_IFIDX
local BOND_RX2_SLAVE_IFIDX
local TXNS_FD
local RXNS_FD
BOND_TX1_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
cat /sys/class/net/"$BOND_TX1_SLAVE_IF"/ifindex)
BOND_TX2_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
cat /sys/class/net/"$BOND_TX2_SLAVE_IF"/ifindex)
BOND_RX1_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
cat /sys/class/net/"$BOND_RX1_SLAVE_IF"/ifindex)
BOND_RX2_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
cat /sys/class/net/"$BOND_RX2_SLAVE_IF"/ifindex)
exec {TXNS_FD}</var/run/netns/"${TXNS}"
exec {RXNS_FD}</var/run/netns/"${RXNS}"
# Linking TX ifaces to the RX ones (on the other namespace)
echo "${TXNS_FD}:$BOND_TX1_SLAVE_IFIDX $RXNS_FD:$BOND_RX1_SLAVE_IFIDX" \
> "$NSIM_DEV_SYS_LINK"
echo "${TXNS_FD}:$BOND_TX2_SLAVE_IFIDX $RXNS_FD:$BOND_RX2_SLAVE_IFIDX" \
> "$NSIM_DEV_SYS_LINK"
exec {TXNS_FD}<&-
exec {RXNS_FD}<&-
}
function create_all_ifaces() {
# setup_ns function is coming from lib.sh
setup_ns TXNS RXNS
export NAMESPACE="${RXNS}"
# Create two interfaces for RX and two for TX
create_ifaces_bond
# Link netlink ifaces
link_ifaces_bond
}
# configure DSTIF and SRCIF IPs
function configure_ifaces_ips() {
local IP_VERSION=${1:-"ipv4"}
select_ipv4_or_ipv6 "${IP_VERSION}"
ip -n "${RXNS}" addr add "${DSTIP}"/24 dev "${DSTIF}"
ip -n "${RXNS}" link set "${DSTIF}" up
ip -n "${TXNS}" addr add "${SRCIP}"/24 dev "${SRCIF}"
ip -n "${TXNS}" link set "${SRCIF}" up
}
function test_enable_netpoll_on_enslaved_iface() {
echo 0 > "${NETCONS_PATH}"/enabled
# At this stage, BOND_TX1_SLAVE_IF is enslaved to BOND_TX_MAIN_IF, and
# linked to BOND_RX1_SLAVE_IF inside the namespace.
echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name
# This should fail with the following message in dmesg:
# netpoll: netconsole: ethX is a slave device, aborting
set +e
enable_netcons_ns 2> /dev/null
set -e
if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
then
echo "test failed: Bonding and netpoll cannot co-exists." >&2
exit "${ksft_fail}"
fi
}
function test_delete_bond_and_reenable_target() {
ip -n "${TXNS}" \
link delete "${BOND_TX_MAIN_IF}" type bond
# BOND_TX1_SLAVE_IF is not attached to a bond interface anymore
# netpoll can be plugged in there
echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name
# this should work, since the interface is not enslaved
enable_netcons_ns
if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
then
echo "test failed: Unable to start netpoll on an unbond iface." >&2
exit "${ksft_fail}"
fi
}
# Send a netconsole message to the netconsole target
function test_send_netcons_msg_through_bond_iface() {
# Listen for netconsole port inside the namespace and
# destination interface
listen_port_and_save_to "${OUTPUT_FILE}" "${IP_VERSION}" &
# Wait for socat to start and listen to the port.
wait_for_port "${RXNS}" "${PORT}" "${IP_VERSION}"
# 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_result "${OUTPUT_FILE}" "${FORMAT}"
# kill socat in case it is still running
pkill_socat
}
# BOND_TX1_SLAVE_IF has netconsole enabled on it, bind it to BOND_TX_MAIN_IF.
# Given BOND_TX_MAIN_IF was deleted, recreate it first
function test_enslave_netcons_enabled_iface {
# netconsole got disabled while the interface was down
if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
then
echo "test failed: netconsole expected to be enabled against BOND_TX1_SLAVE_IF" >&2
exit "${ksft_fail}"
fi
# recreate the bonding iface. it got deleted by previous
# test (test_delete_bond_and_reenable_target)
ip -n "${TXNS}" \
link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr
# sub-interface need to be down before attaching to bonding
# This will also disable netconsole.
ip -n "${TXNS}" \
link set "${BOND_TX1_SLAVE_IF}" down
ip -n "${TXNS}" \
link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
ip -n "${TXNS}" \
link set "${BOND_TX_MAIN_IF}" up
# netconsole got disabled while the interface was down
if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
then
echo "test failed: Device is part of a bond iface, cannot have netcons enabled" >&2
exit "${ksft_fail}"
fi
}
# Get netconsole enabled on a bonding interface and attach a second
# sub-interface.
function test_enslave_iface_to_bond {
# BOND_TX_MAIN_IF has only BOND_TX1_SLAVE_IF right now
echo "${BOND_TX_MAIN_IF}" > "${NETCONS_PATH}"/dev_name
enable_netcons_ns
# netcons is attached to bond0 and BOND_TX1_SLAVE_IF is
# part of BOND_TX_MAIN_IF. Attach BOND_TX2_SLAVE_IF to BOND_TX_MAIN_IF.
ip -n "${TXNS}" \
link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
then
echo "test failed: Netconsole should be enabled on bonding interface. Failed" >&2
exit "${ksft_fail}"
fi
}
function test_enslave_iff_disabled_netpoll_iface {
local ret
# Create two interfaces. veth interfaces it known to have
# IFF_DISABLE_NETPOLL set
if ! ip link add "${VETH0}" type veth peer name "${VETH1}"
then
echo "Failed to create veth TX interface. Is CONFIG_VETH set?" >&2
exit "${ksft_skip}"
fi
set +e
# This will print RTNETLINK answers: Device or resource busy
ip link set "${VETH0}" master "${BOND_TX_MAIN_IF}" 2> /dev/null
ret=$?
set -e
if [[ $ret -eq 0 ]]
then
echo "test failed: veth interface could not be enslaved"
exit "${ksft_fail}"
fi
}
# Given that netconsole picks the current net namespace, we need to enable it
# from inside the TXNS namespace
function enable_netcons_ns() {
ip netns exec "${TXNS}" sh -c \
"mount -t configfs configfs /sys/kernel/config && echo 1 > $NETCONS_PATH/enabled"
}
####################
# Tests start here #
####################
# Create regular interfaces using netdevsim and link them
create_all_ifaces
# Setup the bonding interfaces
# BOND_RX_MAIN_IF has BOND_RX{1,2}_SLAVE_IF
# BOND_TX_MAIN_IF has BOND_TX{1,2}_SLAVE_IF
setup_bonding_ifaces
# Configure the ips as BOND_RX1_SLAVE_IF and BOND_TX1_SLAVE_IF
configure_ifaces_ips "${IP_VERSION}"
_create_dynamic_target "${FORMAT}" "${NETCONS_PATH}"
enable_netcons_ns
set_user_data
# Test #1 : Create an bonding interface and attach netpoll into
# the bonding interface. Netconsole/netpoll should work on
# the bonding interface.
test_send_netcons_msg_through_bond_iface
echo "test #1: netpoll on bonding interface worked. Test passed" >&2
# Test #2: Attach netpoll to an enslaved interface
# Try to attach netpoll to an enslaved sub-interface (while still being part of
# a bonding interface), which shouldn't be allowed
test_enable_netpoll_on_enslaved_iface
echo "test #2: netpoll correctly rejected enslaved interface (expected behavior). Test passed." >&2
# Test #3: Unplug the sub-interface from bond and enable netconsole
# Detach the interface from a bonding interface and attach netpoll again
test_delete_bond_and_reenable_target
echo "test #3: Able to attach to an unbound interface. Test passed." >&2
# Test #4: Enslave a sub-interface that had netconsole enabled
# Try to enslave an interface that has netconsole/netpoll enabled.
# Previous test has netconsole enabled in BOND_TX1_SLAVE_IF, try to enslave it
test_enslave_netcons_enabled_iface
echo "test #4: Enslaving an interface with netpoll attached. Test passed." >&2
# Test #5: Enslave a sub-interface to a bonding interface
# Enslave an interface to a bond interface that has netpoll attached
# At this stage, BOND_TX_MAIN_IF is created and BOND_TX1_SLAVE_IF is part of
# it. Netconsole is currently disabled
test_enslave_iface_to_bond
echo "test #5: Enslaving an interface to bond+netpoll. Test passed." >&2
# Test #6: Enslave a IFF_DISABLE_NETPOLL sub-interface to a bonding interface
# At this stage, BOND_TX_MAIN_IF has both sub interface and netconsole is
# enabled. This test will try to enslave an a veth (IFF_DISABLE_NETPOLL) interface
# and it should fail, with netpoll: veth0 doesn't support polling
test_enslave_iff_disabled_netpoll_iface
echo "test #6: Enslaving IFF_DISABLE_NETPOLL ifaces to bond iface is not supported. Test passed." >&2
cleanup_bond
trap - EXIT
exit "${EXIT_STATUS}"

View File

@@ -11,9 +11,11 @@ set -euo pipefail
LIBDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") LIBDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
SRCIF="" # to be populated later SRCIF="" # to be populated later
SRCIP="" # to be populated later
SRCIP4="192.0.2.1" SRCIP4="192.0.2.1"
SRCIP6="fc00::1" SRCIP6="fc00::1"
DSTIF="" # to be populated later DSTIF="" # to be populated later
DSTIP="" # to be populated later
DSTIP4="192.0.2.2" DSTIP4="192.0.2.2"
DSTIP6="fc00::2" DSTIP6="fc00::2"
@@ -28,17 +30,23 @@ NETCONS_PATH="${NETCONS_CONFIGFS}"/"${TARGET}"
# NAMESPACE will be populated by setup_ns with a random value # NAMESPACE will be populated by setup_ns with a random value
NAMESPACE="" NAMESPACE=""
# IDs for netdevsim # IDs for netdevsim. We either use NSIM_DEV_{1,2}_ID for standard test
# or NSIM_BOND_{T,R}X_{1,2} for the bonding tests. Not both at the
# same time.
NSIM_DEV_1_ID=$((256 + RANDOM % 256)) NSIM_DEV_1_ID=$((256 + RANDOM % 256))
NSIM_DEV_2_ID=$((512 + RANDOM % 256)) NSIM_DEV_2_ID=$((512 + RANDOM % 256))
NSIM_BOND_TX_1=$((768 + RANDOM % 256))
NSIM_BOND_TX_2=$((1024 + RANDOM % 256))
NSIM_BOND_RX_1=$((1280 + RANDOM % 256))
NSIM_BOND_RX_2=$((1536 + RANDOM % 256))
NSIM_DEV_SYS_NEW="/sys/bus/netdevsim/new_device" NSIM_DEV_SYS_NEW="/sys/bus/netdevsim/new_device"
NSIM_DEV_SYS_LINK="/sys/bus/netdevsim/link_device"
# Used to create and delete namespaces # Used to create and delete namespaces
source "${LIBDIR}"/../../../../net/lib.sh source "${LIBDIR}"/../../../../net/lib.sh
# Create netdevsim interfaces # Create netdevsim interfaces
create_ifaces() { create_ifaces() {
echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_NEW" echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_NEW"
echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_NEW" echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_NEW"
udevadm settle 2> /dev/null || true udevadm settle 2> /dev/null || true
@@ -113,31 +121,38 @@ function set_network() {
configure_ip configure_ip
} }
function create_dynamic_target() { function _create_dynamic_target() {
local FORMAT=${1:-"extended"} local FORMAT="${1:?FORMAT parameter required}"
local NCPATH="${2:?NCPATH parameter required}"
DSTMAC=$(ip netns exec "${NAMESPACE}" \ DSTMAC=$(ip netns exec "${NAMESPACE}" \
ip link show "${DSTIF}" | awk '/ether/ {print $2}') ip link show "${DSTIF}" | awk '/ether/ {print $2}')
# Create a dynamic target # Create a dynamic target
mkdir "${NETCONS_PATH}" mkdir "${NCPATH}"
echo "${DSTIP}" > "${NETCONS_PATH}"/remote_ip echo "${DSTIP}" > "${NCPATH}"/remote_ip
echo "${SRCIP}" > "${NETCONS_PATH}"/local_ip echo "${SRCIP}" > "${NCPATH}"/local_ip
echo "${DSTMAC}" > "${NETCONS_PATH}"/remote_mac echo "${DSTMAC}" > "${NCPATH}"/remote_mac
echo "${SRCIF}" > "${NETCONS_PATH}"/dev_name echo "${SRCIF}" > "${NCPATH}"/dev_name
if [ "${FORMAT}" == "basic" ] if [ "${FORMAT}" == "basic" ]
then then
# Basic target does not support release # Basic target does not support release
echo 0 > "${NETCONS_PATH}"/release echo 0 > "${NCPATH}"/release
echo 0 > "${NETCONS_PATH}"/extended echo 0 > "${NCPATH}"/extended
elif [ "${FORMAT}" == "extended" ] elif [ "${FORMAT}" == "extended" ]
then then
echo 1 > "${NETCONS_PATH}"/extended echo 1 > "${NCPATH}"/extended
fi fi
}
echo 1 > "${NETCONS_PATH}"/enabled function create_dynamic_target() {
local FORMAT=${1:-"extended"}
local NCPATH=${2:-"$NETCONS_PATH"}
_create_dynamic_target "${FORMAT}" "${NCPATH}"
echo 1 > "${NCPATH}"/enabled
# This will make sure that the kernel was able to # This will make sure that the kernel was able to
# load the netconsole driver configuration. The console message # load the netconsole driver configuration. The console message
@@ -185,14 +200,26 @@ function do_cleanup() {
echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk
} }
function cleanup() { function cleanup_netcons() {
# delete netconsole dynamic reconfiguration # delete netconsole dynamic reconfiguration
echo 0 > "${NETCONS_PATH}"/enabled # do not fail if the target is already disabled
if [[ ! -d "${NETCONS_PATH}" ]]
then
# in some cases this is called before netcons path is created
return
fi
if [[ $(cat "${NETCONS_PATH}"/enabled) != 0 ]]
then
echo 0 > "${NETCONS_PATH}"/enabled || true
fi
# Remove all the keys that got created during the selftest # Remove all the keys that got created during the selftest
find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete
# Remove the configfs entry # Remove the configfs entry
rmdir "${NETCONS_PATH}" rmdir "${NETCONS_PATH}"
}
function cleanup() {
cleanup_netcons
do_cleanup do_cleanup
} }
@@ -369,3 +396,24 @@ function wait_for_port() {
# more frequently on IPv6 # more frequently on IPv6
sleep 1 sleep 1
} }
# Clean up netdevsim ifaces created for bonding test
function cleanup_bond_nsim() {
ip -n "${TXNS}" \
link delete "${BOND_TX_MAIN_IF}" type bond || true
ip -n "${RXNS}" \
link delete "${BOND_RX_MAIN_IF}" type bond || true
cleanup_netdevsim "$NSIM_BOND_TX_1"
cleanup_netdevsim "$NSIM_BOND_TX_2"
cleanup_netdevsim "$NSIM_BOND_RX_1"
cleanup_netdevsim "$NSIM_BOND_RX_2"
}
# cleanup tests that use bonding interfaces
function cleanup_bond() {
cleanup_netcons
cleanup_bond_nsim
cleanup_all_ns
ip link delete "${VETH0}" || true
}

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
# Repeatedly send kernel messages, toggles netconsole targets on and off,
# creates and deletes targets in parallel, and toggles the source interface to
# simulate stress conditions.
#
# This test aims to verify the robustness of netconsole under dynamic
# configurations and concurrent operations.
#
# The major goal is to run this test with LOCKDEP, Kmemleak and KASAN to make
# sure no issues is reported.
#
# Author: Breno Leitao <leitao@debian.org>
set -euo pipefail
SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh
# Number of times the main loop run
ITERATIONS=${1:-150}
# Only test extended format
FORMAT="extended"
# And ipv6 only
IP_VERSION="ipv6"
# Create, enable and delete some targets.
create_and_delete_random_target() {
COUNT=2
RND_PREFIX=$(mktemp -u netcons_rnd_XXXX_)
if [ -d "${NETCONS_CONFIGFS}/${RND_PREFIX}${COUNT}" ] || \
[ -d "${NETCONS_CONFIGFS}/${RND_PREFIX}0" ]; then
echo "Function didn't finish yet, skipping it." >&2
return
fi
# enable COUNT targets
for i in $(seq ${COUNT})
do
RND_TARGET="${RND_PREFIX}"${i}
RND_TARGET_PATH="${NETCONS_CONFIGFS}"/"${RND_TARGET}"
# Basic population so the target can come up
_create_dynamic_target "${FORMAT}" "${RND_TARGET_PATH}"
done
echo "netconsole selftest: ${COUNT} additional targets were created" > /dev/kmsg
# disable them all
for i in $(seq ${COUNT})
do
RND_TARGET="${RND_PREFIX}"${i}
RND_TARGET_PATH="${NETCONS_CONFIGFS}"/"${RND_TARGET}"
if [[ $(cat "${RND_TARGET_PATH}/enabled") -eq 1 ]]
then
echo 0 > "${RND_TARGET_PATH}"/enabled
fi
rmdir "${RND_TARGET_PATH}"
done
}
# Disable and enable the target mid-air, while messages
# are being transmitted.
toggle_netcons_target() {
for i in $(seq 2)
do
if [ ! -d "${NETCONS_PATH}" ]
then
break
fi
echo 0 > "${NETCONS_PATH}"/enabled 2> /dev/null || true
# Try to enable a bit harder, given it might fail to enable
# Write to `enabled` might fail depending on the lock, which is
# highly contentious here
for _ in $(seq 5)
do
echo 1 > "${NETCONS_PATH}"/enabled 2> /dev/null || true
done
done
}
toggle_iface(){
ip link set "${SRCIF}" down
ip link set "${SRCIF}" up
}
# Start here
modprobe netdevsim 2> /dev/null || true
modprobe netconsole 2> /dev/null || true
# Check for basic system dependency and exit if not found
check_for_dependencies
# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
echo "6 5" > /proc/sys/kernel/printk
# Remove the namespace, interfaces and netconsole target on exit
trap cleanup EXIT
# Create one namespace and two interfaces
set_network "${IP_VERSION}"
# Create a dynamic target for netconsole
create_dynamic_target "${FORMAT}"
for i in $(seq "$ITERATIONS")
do
for _ in $(seq 10)
do
echo "${MSG}: ${TARGET} ${i}" > /dev/kmsg
done
wait
if (( i % 30 == 0 )); then
toggle_netcons_target &
fi
if (( i % 50 == 0 )); then
# create some targets, enable them, send msg and disable
# all in a parallel thread
create_and_delete_random_target &
fi
if (( i % 70 == 0 )); then
toggle_iface &
fi
done
wait
exit "${EXIT_STATUS}"