diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 8aebdc6feb17..18ce235a77f8 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -50,6 +50,7 @@ TEST_PROGS = \ tso.py \ userns_devmem.py \ uso.py \ + vlan.py \ xdp_metadata.py \ xsk_reconfig.py \ # diff --git a/tools/testing/selftests/drivers/net/hw/config b/tools/testing/selftests/drivers/net/hw/config index d89a9ba17655..c6c2b64bb712 100644 --- a/tools/testing/selftests/drivers/net/hw/config +++ b/tools/testing/selftests/drivers/net/hw/config @@ -24,5 +24,6 @@ CONFIG_NET_SCH_INGRESS=y CONFIG_SYNC_FILE=y CONFIG_UDMABUF=y CONFIG_USER_NS=y +CONFIG_VLAN_8021Q=m CONFIG_VXLAN=y CONFIG_XFRM_USER=y diff --git a/tools/testing/selftests/drivers/net/hw/vlan.py b/tools/testing/selftests/drivers/net/hw/vlan.py new file mode 100755 index 000000000000..00c90b930f67 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/vlan.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +VLAN tests. + +Validates that ping traffic is sent and received correctly over 802.1q +and 802.1ad VLAN interfaces, with hardware RX VLAN stripping enabled and +disabled on the local interface. + +The "hw" test variants enable the RX VLAN stripping features supported by +the device, the "sw" test variants disable all of them. A test is xfailed +if the requested configuration is not possible. + +Test cases: + - 8021q_hw: Traffic over a single 802.1q VLAN, HW stripping on + - 8021q_sw: Traffic over a single 802.1q VLAN, HW stripping off + - 8021ad_hw: Traffic over a single 802.1ad VLAN, HW stripping on + - 8021ad_sw: Traffic over a single 802.1ad VLAN, HW stripping off + - qinq_hw: Traffic over an 802.1q VLAN stacked on an 802.1ad VLAN, + HW stripping on + - qinq_sw: Traffic over an 802.1q VLAN stacked on an 802.1ad VLAN, + HW stripping off +""" + +import os + +from lib.py import ( + KsftNamedVariant, + KsftXfailEx, + NetDrvEpEnv, + cmd, + defer, + ethtool, + ip, + ksft_exit, + ksft_run, + ksft_variants, + set_ethtool_feat, +) + +OUTER_DEV = f"vlout{os.getpid()}" +INNER_DEV = f"vlin{os.getpid()}" + +OUTER_VID = 100 +INNER_VID = 200 + +LOCAL_IP = os.environ.get("VLAN_LOCAL_V4", "198.51.100.1") +REMOTE_IP = os.environ.get("VLAN_REMOTE_V4", "198.51.100.2") + + +def _vlan_add(base, name, proto, vid, host=None): + """Create a VLAN device on top of base and bring it up.""" + + ip(f"link add link {base} name {name} type vlan proto {proto} id {vid}", + host=host) + defer(ip, f"link del {name}", host=host) + ip(f"link set {name} up", host=host) + + +def _vlan_setup(base, addr, outer_proto, inner_proto, host=None): + """Create VLAN interfaces on base and set an IP on the innermost one.""" + + _vlan_add(base, OUTER_DEV, outer_proto, OUTER_VID, host=host) + if inner_proto: + _vlan_add(OUTER_DEV, INNER_DEV, inner_proto, INNER_VID, host=host) + + dev = INNER_DEV if inner_proto else OUTER_DEV + ip(f"addr add {addr}/24 dev {dev}", host=host) + + +def _setup(cfg, outer_proto, inner_proto, hw_strip): + """Configure VLAN stripping and create the VLAN interfaces.""" + + names = ["rx-vlan-offload"] + if outer_proto == "802.1ad": + names.append("rx-vlan-stag-hw-parse") + + feat = ethtool(f"-k {cfg.ifname}", json=True)[0] + feats = {} + for name in names: + # hw: skip the features the device does not support + # sw: request all of them off + if hw_strip and feat[name]["fixed"] and not feat[name]["active"]: + continue + feats[name] = hw_strip + + if hw_strip and not feats: + raise KsftXfailEx("Device does not support RX VLAN stripping") + + set_ethtool_feat(cfg.ifname, feat, feats) + + _vlan_setup(cfg.ifname, LOCAL_IP, outer_proto, inner_proto) + _vlan_setup(cfg.remote_ifname, REMOTE_IP, outer_proto, inner_proto, + host=cfg.remote) + + +def _vlan_variants(): + """Generator that yields the VLAN protocols and the stripping mode.""" + + yield KsftNamedVariant("8021q_hw", "802.1q", None, True) + yield KsftNamedVariant("8021q_sw", "802.1q", None, False) + yield KsftNamedVariant("8021ad_hw", "802.1ad", None, True) + yield KsftNamedVariant("8021ad_sw", "802.1ad", None, False) + yield KsftNamedVariant("qinq_hw", "802.1ad", "802.1q", True) + yield KsftNamedVariant("qinq_sw", "802.1ad", "802.1q", False) + + +@ksft_variants(_vlan_variants()) +def test(cfg, outer_proto, inner_proto, hw_strip): + """Run a single VLAN test""" + + _setup(cfg, outer_proto, inner_proto, hw_strip) + + cmd(f"ping -c 1 -W 5 {REMOTE_IP}") + + +def main() -> None: + """ Ksft boiler plate main """ + + with NetDrvEpEnv(__file__) as cfg: + ksft_run(cases=[test], args=(cfg, )) + ksft_exit() + + +if __name__ == "__main__": + main()