Commit 583024cf authored by Paolo Abeni's avatar Paolo Abeni

Merge branch 'netdevsim-support-for-l3-hw-stats'

Petr Machata says:

====================
netdevsim: Support for L3 HW stats

"L3 stats" is a suite of interface statistics aimed at reflecting traffic
taking place in a HW device, on an object corresponding to some software
netdevice. Support for this stats suite has been added recently, in commit
ca0a53dc ("Merge branch 'net-hw-counters-for-soft-devices'").

In this patch set:

- Patch #1 adds support for L3 stats to netdevsim.

  Real devices can have various conditions for when an L3 counter is
  available. To simulate this, netdevsim maintains a list of devices
  suitable for HW stats collection. Only when l3_stats is enabled on both a
  netdevice itself, and in netdevsim, will netdevsim contribute values to
  L3 stats.

  This enablement and disablement is done via debugfs:

    # echo $ifindex > /sys/kernel/debug/netdevsim/$DEV/hwstats/l3/enable_ifindex
    # echo $ifindex > /sys/kernel/debug/netdevsim/$DEV/hwstats/l3/disable_ifindex

  Besides this, there is a third toggle to mark a device for future failure:

    # echo $ifindex > /sys/kernel/debug/netdevsim/$DEV/hwstats/l3/fail_next_enable

- This allows HW-independent testing of stats reporting and in-kernel APIs,
  as well as a test for enablement rollback, which is difficult to do
  otherwise. This netdevsim-specific selftest is added in patch #2.

- Patch #3 adds another driver-specific selftest, namely a test aimed at
  checking mlxsw-induced stats monitoring events.

====================

Link: https://lore.kernel.org/r/cover.1647265833.git.petrm@nvidia.comSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 231fdac3 ed2ae69c
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
obj-$(CONFIG_NETDEVSIM) += netdevsim.o obj-$(CONFIG_NETDEVSIM) += netdevsim.o
netdevsim-objs := \ netdevsim-objs := \
netdev.o dev.o ethtool.o fib.o bus.o health.o udp_tunnels.o netdev.o dev.o ethtool.o fib.o bus.o health.o hwstats.o udp_tunnels.o
ifeq ($(CONFIG_BPF_SYSCALL),y) ifeq ($(CONFIG_BPF_SYSCALL),y)
netdevsim-objs += \ netdevsim-objs += \
......
...@@ -1498,10 +1498,14 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, ...@@ -1498,10 +1498,14 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
if (err) if (err)
goto err_health_exit; goto err_health_exit;
err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); err = nsim_dev_hwstats_init(nsim_dev);
if (err) if (err)
goto err_psample_exit; goto err_psample_exit;
err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
if (err)
goto err_hwstats_exit;
nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
0200, 0200,
nsim_dev->ddir, nsim_dev->ddir,
...@@ -1509,6 +1513,8 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, ...@@ -1509,6 +1513,8 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
&nsim_dev_take_snapshot_fops); &nsim_dev_take_snapshot_fops);
return 0; return 0;
err_hwstats_exit:
nsim_dev_hwstats_exit(nsim_dev);
err_psample_exit: err_psample_exit:
nsim_dev_psample_exit(nsim_dev); nsim_dev_psample_exit(nsim_dev);
err_health_exit: err_health_exit:
...@@ -1595,15 +1601,21 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev) ...@@ -1595,15 +1601,21 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
if (err) if (err)
goto err_bpf_dev_exit; goto err_bpf_dev_exit;
err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); err = nsim_dev_hwstats_init(nsim_dev);
if (err) if (err)
goto err_psample_exit; goto err_psample_exit;
err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
if (err)
goto err_hwstats_exit;
nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY;
devlink_set_features(devlink, DEVLINK_F_RELOAD); devlink_set_features(devlink, DEVLINK_F_RELOAD);
devlink_register(devlink); devlink_register(devlink);
return 0; return 0;
err_hwstats_exit:
nsim_dev_hwstats_exit(nsim_dev);
err_psample_exit: err_psample_exit:
nsim_dev_psample_exit(nsim_dev); nsim_dev_psample_exit(nsim_dev);
err_bpf_dev_exit: err_bpf_dev_exit:
...@@ -1648,6 +1660,7 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) ...@@ -1648,6 +1660,7 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
mutex_unlock(&nsim_dev->vfs_lock); mutex_unlock(&nsim_dev->vfs_lock);
nsim_dev_port_del_all(nsim_dev); nsim_dev_port_del_all(nsim_dev);
nsim_dev_hwstats_exit(nsim_dev);
nsim_dev_psample_exit(nsim_dev); nsim_dev_psample_exit(nsim_dev);
nsim_dev_health_exit(nsim_dev); nsim_dev_health_exit(nsim_dev);
nsim_fib_destroy(devlink, nsim_dev->fib_data); nsim_fib_destroy(devlink, nsim_dev->fib_data);
......
This diff is collapsed.
...@@ -184,6 +184,28 @@ struct nsim_dev_health { ...@@ -184,6 +184,28 @@ struct nsim_dev_health {
int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink); int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink);
void nsim_dev_health_exit(struct nsim_dev *nsim_dev); void nsim_dev_health_exit(struct nsim_dev *nsim_dev);
struct nsim_dev_hwstats_netdev {
struct list_head list;
struct net_device *netdev;
struct rtnl_hw_stats64 stats;
bool enabled;
bool fail_enable;
};
struct nsim_dev_hwstats {
struct dentry *ddir;
struct dentry *l3_ddir;
struct mutex hwsdev_list_lock; /* protects hwsdev list(s) */
struct list_head l3_list;
struct notifier_block netdevice_nb;
struct delayed_work traffic_dw;
};
int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev);
void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev);
#if IS_ENABLED(CONFIG_PSAMPLE) #if IS_ENABLED(CONFIG_PSAMPLE)
int nsim_dev_psample_init(struct nsim_dev *nsim_dev); int nsim_dev_psample_init(struct nsim_dev *nsim_dev);
void nsim_dev_psample_exit(struct nsim_dev *nsim_dev); void nsim_dev_psample_exit(struct nsim_dev *nsim_dev);
...@@ -261,6 +283,7 @@ struct nsim_dev { ...@@ -261,6 +283,7 @@ struct nsim_dev {
bool fail_reload; bool fail_reload;
struct devlink_region *dummy_region; struct devlink_region *dummy_region;
struct nsim_dev_health health; struct nsim_dev_health health;
struct nsim_dev_hwstats hwstats;
struct flow_action_cookie *fa_cookie; struct flow_action_cookie *fa_cookie;
spinlock_t fa_cookie_lock; /* protects fa_cookie */ spinlock_t fa_cookie_lock; /* protects fa_cookie */
bool fail_trap_group_set; bool fail_trap_group_set;
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
l3_monitor_test
"
NUM_NETIFS=0
source $lib_dir/lib.sh
swp=$NETIF_NO_CABLE
cleanup()
{
pre_cleanup
}
l3_monitor_test()
{
hw_stats_monitor_test $swp l3 \
"ip addr add dev $swp 192.0.2.1/28" \
"ip addr del dev $swp 192.0.2.1/28"
}
trap cleanup EXIT
setup_wait
tests_run
exit $EXIT_STATUS
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
l3_reporting_test
l3_fail_next_test
l3_counter_test
l3_rollback_test
l3_monitor_test
"
NETDEVSIM_PATH=/sys/bus/netdevsim/
DEV_ADDR_1=1337
DEV_ADDR_2=1057
DEV_ADDR_3=5417
NUM_NETIFS=0
source $lib_dir/lib.sh
DUMMY_IFINDEX=
DEV_ADDR()
{
local n=$1; shift
local var=DEV_ADDR_$n
echo ${!var}
}
DEV()
{
echo netdevsim$(DEV_ADDR $1)
}
DEVLINK_DEV()
{
echo netdevsim/$(DEV $1)
}
SYSFS_NET_DIR()
{
echo /sys/bus/netdevsim/devices/$(DEV $1)/net/
}
DEBUGFS_DIR()
{
echo /sys/kernel/debug/netdevsim/$(DEV $1)/
}
nsim_add()
{
local n=$1; shift
echo "$(DEV_ADDR $n) 1" > ${NETDEVSIM_PATH}/new_device
while [ ! -d $(SYSFS_NET_DIR $n) ] ; do :; done
}
nsim_reload()
{
local n=$1; shift
local ns=$1; shift
devlink dev reload $(DEVLINK_DEV $n) netns $ns
if [ $? -ne 0 ]; then
echo "Failed to reload $(DEV $n) into netns \"testns1\""
exit 1
fi
}
nsim_del()
{
local n=$1; shift
echo "$(DEV_ADDR $n)" > ${NETDEVSIM_PATH}/del_device
}
nsim_hwstats_toggle()
{
local action=$1; shift
local instance=$1; shift
local netdev=$1; shift
local type=$1; shift
local ifindex=$($IP -j link show dev $netdev | jq '.[].ifindex')
echo $ifindex > $(DEBUGFS_DIR $instance)/hwstats/$type/$action
}
nsim_hwstats_enable()
{
nsim_hwstats_toggle enable_ifindex "$@"
}
nsim_hwstats_disable()
{
nsim_hwstats_toggle disable_ifindex "$@"
}
nsim_hwstats_fail_next_enable()
{
nsim_hwstats_toggle fail_next_enable "$@"
}
setup_prepare()
{
modprobe netdevsim &> /dev/null
nsim_add 1
nsim_add 2
nsim_add 3
ip netns add testns1
if [ $? -ne 0 ]; then
echo "Failed to add netns \"testns1\""
exit 1
fi
nsim_reload 1 testns1
nsim_reload 2 testns1
nsim_reload 3 testns1
IP="ip -n testns1"
$IP link add name dummy1 type dummy
$IP link set dev dummy1 up
DUMMY_IFINDEX=$($IP -j link show dev dummy1 | jq '.[].ifindex')
}
cleanup()
{
pre_cleanup
$IP link del name dummy1
ip netns del testns1
nsim_del 3
nsim_del 2
nsim_del 1
modprobe -r netdevsim &> /dev/null
}
netdev_hwstats_used()
{
local netdev=$1; shift
local type=$1; shift
$IP -j stats show dev "$netdev" group offload subgroup hw_stats_info |
jq '.[].info.l3_stats.used'
}
netdev_check_used()
{
local netdev=$1; shift
local type=$1; shift
[[ $(netdev_hwstats_used $netdev $type) == "true" ]]
}
netdev_check_unused()
{
local netdev=$1; shift
local type=$1; shift
[[ $(netdev_hwstats_used $netdev $type) == "false" ]]
}
netdev_hwstats_request()
{
local netdev=$1; shift
local type=$1; shift
$IP -j stats show dev "$netdev" group offload subgroup hw_stats_info |
jq ".[].info.${type}_stats.request"
}
netdev_check_requested()
{
local netdev=$1; shift
local type=$1; shift
[[ $(netdev_hwstats_request $netdev $type) == "true" ]]
}
netdev_check_unrequested()
{
local netdev=$1; shift
local type=$1; shift
[[ $(netdev_hwstats_request $netdev $type) == "false" ]]
}
reporting_test()
{
local type=$1; shift
local instance=1
RET=0
[[ -n $(netdev_hwstats_used dummy1 $type) ]]
check_err $? "$type stats not reported"
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used before either device or netdevsim request"
nsim_hwstats_enable $instance dummy1 $type
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used before device request"
netdev_check_unrequested dummy1 $type
check_err $? "$type stats reported as requested before device request"
$IP stats set dev dummy1 ${type}_stats on
netdev_check_used dummy1 $type
check_err $? "$type stats reported as not used after both device and netdevsim request"
netdev_check_requested dummy1 $type
check_err $? "$type stats reported as not requested after device request"
nsim_hwstats_disable $instance dummy1 $type
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used after netdevsim request withdrawn"
nsim_hwstats_enable $instance dummy1 $type
netdev_check_used dummy1 $type
check_err $? "$type stats reported as not used after netdevsim request reenabled"
$IP stats set dev dummy1 ${type}_stats off
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used after device request withdrawn"
netdev_check_unrequested dummy1 $type
check_err $? "$type stats reported as requested after device request withdrawn"
nsim_hwstats_disable $instance dummy1 $type
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used after both requests withdrawn"
log_test "Reporting of $type stats usage"
}
l3_reporting_test()
{
reporting_test l3
}
__fail_next_test()
{
local instance=$1; shift
local type=$1; shift
RET=0
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used before either device or netdevsim request"
nsim_hwstats_enable $instance dummy1 $type
nsim_hwstats_fail_next_enable $instance dummy1 $type
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used before device request"
netdev_check_unrequested dummy1 $type
check_err $? "$type stats reported as requested before device request"
$IP stats set dev dummy1 ${type}_stats on 2>/dev/null
check_fail $? "$type stats request not bounced as it should have been"
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used after bounce"
netdev_check_unrequested dummy1 $type
check_err $? "$type stats reported as requested after bounce"
$IP stats set dev dummy1 ${type}_stats on
check_err $? "$type stats request failed when it shouldn't have"
netdev_check_used dummy1 $type
check_err $? "$type stats reported as not used after both device and netdevsim request"
netdev_check_requested dummy1 $type
check_err $? "$type stats reported as not requested after device request"
$IP stats set dev dummy1 ${type}_stats off
nsim_hwstats_disable $instance dummy1 $type
log_test "Injected failure of $type stats enablement (netdevsim #$instance)"
}
fail_next_test()
{
__fail_next_test 1 "$@"
__fail_next_test 2 "$@"
__fail_next_test 3 "$@"
}
l3_fail_next_test()
{
fail_next_test l3
}
get_hwstat()
{
local netdev=$1; shift
local type=$1; shift
local selector=$1; shift
$IP -j stats show dev $netdev group offload subgroup ${type}_stats |
jq ".[0].stats64.${selector}"
}
counter_test()
{
local type=$1; shift
local instance=1
RET=0
nsim_hwstats_enable $instance dummy1 $type
$IP stats set dev dummy1 ${type}_stats on
netdev_check_used dummy1 $type
check_err $? "$type stats reported as not used after both device and netdevsim request"
# Netdevsim counts 10pps on ingress. We should see maybe a couple
# packets, unless things take a reeealy long time.
local pkts=$(get_hwstat dummy1 l3 rx.packets)
((pkts < 10))
check_err $? "$type stats show >= 10 packets after first enablement"
sleep 2
local pkts=$(get_hwstat dummy1 l3 rx.packets)
((pkts >= 20))
check_err $? "$type stats show < 20 packets after 2s passed"
$IP stats set dev dummy1 ${type}_stats off
sleep 2
$IP stats set dev dummy1 ${type}_stats on
local pkts=$(get_hwstat dummy1 l3 rx.packets)
((pkts < 10))
check_err $? "$type stats show >= 10 packets after second enablement"
$IP stats set dev dummy1 ${type}_stats off
nsim_hwstats_fail_next_enable $instance dummy1 $type
$IP stats set dev dummy1 ${type}_stats on 2>/dev/null
check_fail $? "$type stats request not bounced as it should have been"
sleep 2
$IP stats set dev dummy1 ${type}_stats on
local pkts=$(get_hwstat dummy1 l3 rx.packets)
((pkts < 10))
check_err $? "$type stats show >= 10 packets after post-fail enablement"
$IP stats set dev dummy1 ${type}_stats off
log_test "Counter values in $type stats"
}
l3_counter_test()
{
counter_test l3
}
rollback_test()
{
local type=$1; shift
RET=0
nsim_hwstats_enable 1 dummy1 l3
nsim_hwstats_enable 2 dummy1 l3
nsim_hwstats_enable 3 dummy1 l3
# The three netdevsim instances are registered in order of their number
# one after another. It is reasonable to expect that whatever
# notifications take place hit no. 2 in between hitting nos. 1 and 3,
# whatever the actual order. This allows us to test that a fail caused
# by no. 2 does not leave the system in a partial state, and rolls
# everything back.
nsim_hwstats_fail_next_enable 2 dummy1 l3
$IP stats set dev dummy1 ${type}_stats on 2>/dev/null
check_fail $? "$type stats request not bounced as it should have been"
netdev_check_unused dummy1 $type
check_err $? "$type stats reported as used after bounce"
netdev_check_unrequested dummy1 $type
check_err $? "$type stats reported as requested after bounce"
sleep 2
$IP stats set dev dummy1 ${type}_stats on
check_err $? "$type stats request not upheld as it should have been"
local pkts=$(get_hwstat dummy1 l3 rx.packets)
((pkts < 10))
check_err $? "$type stats show $pkts packets after post-fail enablement"
$IP stats set dev dummy1 ${type}_stats off
nsim_hwstats_disable 3 dummy1 l3
nsim_hwstats_disable 2 dummy1 l3
nsim_hwstats_disable 1 dummy1 l3
log_test "Failure in $type stats enablement rolled back"
}
l3_rollback_test()
{
rollback_test l3
}
l3_monitor_test()
{
hw_stats_monitor_test dummy1 l3 \
"nsim_hwstats_enable 1 dummy1 l3" \
"nsim_hwstats_disable 1 dummy1 l3" \
"$IP"
}
trap cleanup EXIT
setup_prepare
tests_run
exit $EXIT_STATUS
...@@ -1498,3 +1498,63 @@ brmcast_check_sg_state() ...@@ -1498,3 +1498,63 @@ brmcast_check_sg_state()
check_err_fail $should_fail $? "Entry $src has blocked flag" check_err_fail $should_fail $? "Entry $src has blocked flag"
done done
} }
start_ip_monitor()
{
local mtype=$1; shift
local ip=${1-ip}; shift
# start the monitor in the background
tmpfile=`mktemp /var/run/nexthoptestXXX`
mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
sleep 0.2
echo "$mpid $tmpfile"
}
stop_ip_monitor()
{
local mpid=$1; shift
local tmpfile=$1; shift
local el=$1; shift
local what=$1; shift
sleep 0.2
kill $mpid
local lines=`grep '^\w' $tmpfile | wc -l`
test $lines -eq $el
check_err $? "$what: $lines lines of events, expected $el"
rm -rf $tmpfile
}
hw_stats_monitor_test()
{
local dev=$1; shift
local type=$1; shift
local make_suitable=$1; shift
local make_unsuitable=$1; shift
local ip=${1-ip}; shift
RET=0
# Expect a notification about enablement.
local ipmout=$(start_ip_monitor stats "$ip")
$ip stats set dev $dev ${type}_stats on
stop_ip_monitor $ipmout 1 "${type}_stats enablement"
# Expect a notification about offload.
local ipmout=$(start_ip_monitor stats "$ip")
$make_suitable
stop_ip_monitor $ipmout 1 "${type}_stats installation"
# Expect a notification about loss of offload.
local ipmout=$(start_ip_monitor stats "$ip")
$make_unsuitable
stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
# Expect a notification about disablement
local ipmout=$(start_ip_monitor stats "$ip")
$ip stats set dev $dev ${type}_stats off
stop_ip_monitor $ipmout 1 "${type}_stats disablement"
log_test "${type}_stats notifications"
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment