Path: blob/master/tools/testing/selftests/drivers/net/hw/tso.py
29271 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023"""Run the tools/testing/selftests/net/csum testsuite."""45import fcntl6import socket7import struct8import termios9import time1011from lib.py import ksft_pr, ksft_run, ksft_exit, KsftSkipEx, KsftXfailEx12from lib.py import ksft_eq, ksft_ge, ksft_lt13from lib.py import EthtoolFamily, NetdevFamily, NetDrvEpEnv14from lib.py import bkg, cmd, defer, ethtool, ip, rand_port, wait_port_listen151617def sock_wait_drain(sock, max_wait=1000):18"""Wait for all pending write data on the socket to get ACKed."""19for _ in range(max_wait):20one = b'\0' * 421outq = fcntl.ioctl(sock.fileno(), termios.TIOCOUTQ, one)22outq = struct.unpack("I", outq)[0]23if outq == 0:24break25time.sleep(0.01)26ksft_eq(outq, 0)272829def tcp_sock_get_retrans(sock):30"""Get the number of retransmissions for the TCP socket."""31info = sock.getsockopt(socket.SOL_TCP, socket.TCP_INFO, 512)32return struct.unpack("I", info[100:104])[0]333435def run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso):36cfg.require_cmd("socat", local=False, remote=True)3738port = rand_port()39listen_cmd = f"socat -{ipver} -t 2 -u TCP-LISTEN:{port},reuseport /dev/null,ignoreeof"4041with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as nc:42wait_port_listen(port, host=cfg.remote)4344if ipver == "4":45sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)46sock.connect((remote_v4, port))47else:48sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)49sock.connect((remote_v6, port))5051# Small send to make sure the connection is working.52sock.send("ping".encode())53sock_wait_drain(sock)5455# Send 4MB of data, record the LSO packet count.56qstat_old = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0]57buf = b"0" * 1024 * 1024 * 458sock.send(buf)59sock_wait_drain(sock)60qstat_new = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0]6162# Check that at least 90% of the data was sent as LSO packets.63# System noise may cause false negatives. Also header overheads64# will add up to 5% of extra packes... The check is best effort.65total_lso_wire = len(buf) * 0.90 // cfg.dev["mtu"]66total_lso_super = len(buf) * 0.90 // cfg.dev["tso_max_size"]6768# Make sure we have order of magnitude more LSO packets than69# retransmits, in case TCP retransmitted all the LSO packets.70ksft_lt(tcp_sock_get_retrans(sock), total_lso_wire / 4)71sock.close()7273if should_lso:74if cfg.have_stat_super_count:75ksft_ge(qstat_new['tx-hw-gso-packets'] -76qstat_old['tx-hw-gso-packets'],77total_lso_super,78comment="Number of LSO super-packets with LSO enabled")79if cfg.have_stat_wire_count:80ksft_ge(qstat_new['tx-hw-gso-wire-packets'] -81qstat_old['tx-hw-gso-wire-packets'],82total_lso_wire,83comment="Number of LSO wire-packets with LSO enabled")84else:85if cfg.have_stat_super_count:86ksft_lt(qstat_new['tx-hw-gso-packets'] -87qstat_old['tx-hw-gso-packets'],8815, comment="Number of LSO super-packets with LSO disabled")89if cfg.have_stat_wire_count:90ksft_lt(qstat_new['tx-hw-gso-wire-packets'] -91qstat_old['tx-hw-gso-wire-packets'],92500, comment="Number of LSO wire-packets with LSO disabled")939495def build_tunnel(cfg, outer_ipver, tun_info):96local_v4 = NetDrvEpEnv.nsim_v4_pfx + "1"97local_v6 = NetDrvEpEnv.nsim_v6_pfx + "1"98remote_v4 = NetDrvEpEnv.nsim_v4_pfx + "2"99remote_v6 = NetDrvEpEnv.nsim_v6_pfx + "2"100101local_addr = cfg.addr_v[outer_ipver]102remote_addr = cfg.remote_addr_v[outer_ipver]103104tun_type = tun_info[0]105tun_arg = tun_info[1]106ip(f"link add {tun_type}-ksft type {tun_type} {tun_arg} local {local_addr} remote {remote_addr} dev {cfg.ifname}")107defer(ip, f"link del {tun_type}-ksft")108ip(f"link set dev {tun_type}-ksft up")109ip(f"addr add {local_v4}/24 dev {tun_type}-ksft")110ip(f"addr add {local_v6}/64 dev {tun_type}-ksft")111112ip(f"link add {tun_type}-ksft type {tun_type} {tun_arg} local {remote_addr} remote {local_addr} dev {cfg.remote_ifname}",113host=cfg.remote)114defer(ip, f"link del {tun_type}-ksft", host=cfg.remote)115ip(f"link set dev {tun_type}-ksft up", host=cfg.remote)116ip(f"addr add {remote_v4}/24 dev {tun_type}-ksft", host=cfg.remote)117ip(f"addr add {remote_v6}/64 dev {tun_type}-ksft", host=cfg.remote)118119return remote_v4, remote_v6120121122def restore_wanted_features(cfg):123features_cmd = ""124for feature in cfg.hw_features:125setting = "on" if feature in cfg.wanted_features else "off"126features_cmd += f" {feature} {setting}"127try:128ethtool(f"-K {cfg.ifname} {features_cmd}")129except Exception as e:130ksft_pr(f"WARNING: failure restoring wanted features: {e}")131132133def test_builder(name, cfg, outer_ipver, feature, tun=None, inner_ipver=None):134"""Construct specific tests from the common template."""135def f(cfg):136cfg.require_ipver(outer_ipver)137defer(restore_wanted_features, cfg)138139if not cfg.have_stat_super_count and \140not cfg.have_stat_wire_count:141raise KsftSkipEx(f"Device does not support LSO queue stats")142143if feature not in cfg.hw_features:144raise KsftSkipEx(f"Device does not support {feature}")145146ipver = outer_ipver147if tun:148remote_v4, remote_v6 = build_tunnel(cfg, ipver, tun)149ipver = inner_ipver150else:151remote_v4 = cfg.remote_addr_v["4"]152remote_v6 = cfg.remote_addr_v["6"]153154# First test without the feature enabled.155ethtool(f"-K {cfg.ifname} {feature} off")156run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=False)157158ethtool(f"-K {cfg.ifname} tx-gso-partial off")159ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation off")160if feature in cfg.partial_features:161ethtool(f"-K {cfg.ifname} tx-gso-partial on")162if ipver == "4":163ksft_pr("Testing with mangleid enabled")164ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation on")165166# Full feature enabled.167ethtool(f"-K {cfg.ifname} {feature} on")168run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=True)169170f.__name__ = name + ((outer_ipver + "_") if tun else "") + "ipv" + inner_ipver171return f172173174def query_nic_features(cfg) -> None:175"""Query and cache the NIC features."""176cfg.have_stat_super_count = False177cfg.have_stat_wire_count = False178179features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}})180181cfg.wanted_features = set()182for f in features["wanted"]["bits"]["bit"]:183cfg.wanted_features.add(f["name"])184185cfg.hw_features = set()186hw_all_features_cmd = ""187for f in features["hw"]["bits"]["bit"]:188if f.get("value", False):189feature = f["name"]190cfg.hw_features.add(feature)191hw_all_features_cmd += f" {feature} on"192try:193ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}")194except Exception as e:195ksft_pr(f"WARNING: failure enabling all hw features: {e}")196ksft_pr("partial gso feature detection may be impacted")197198# Check which features are supported via GSO partial199cfg.partial_features = set()200if 'tx-gso-partial' in cfg.hw_features:201ethtool(f"-K {cfg.ifname} tx-gso-partial off")202203no_partial = set()204features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}})205for f in features["active"]["bits"]["bit"]:206no_partial.add(f["name"])207cfg.partial_features = cfg.hw_features - no_partial208ethtool(f"-K {cfg.ifname} tx-gso-partial on")209210restore_wanted_features(cfg)211212stats = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)213if stats:214if 'tx-hw-gso-packets' in stats[0]:215ksft_pr("Detected qstat for LSO super-packets")216cfg.have_stat_super_count = True217if 'tx-hw-gso-wire-packets' in stats[0]:218ksft_pr("Detected qstat for LSO wire-packets")219cfg.have_stat_wire_count = True220221222def main() -> None:223with NetDrvEpEnv(__file__, nsim_test=False) as cfg:224cfg.ethnl = EthtoolFamily()225cfg.netnl = NetdevFamily()226227query_nic_features(cfg)228229test_info = (230# name, v4/v6 ethtool_feature tun:(type, args, inner ip versions)231("", "4", "tx-tcp-segmentation", None),232("", "6", "tx-tcp6-segmentation", None),233("vxlan", "4", "tx-udp_tnl-segmentation", ("vxlan", "id 100 dstport 4789 noudpcsum", ("4", "6"))),234("vxlan", "6", "tx-udp_tnl-segmentation", ("vxlan", "id 100 dstport 4789 udp6zerocsumtx udp6zerocsumrx", ("4", "6"))),235("vxlan_csum", "", "tx-udp_tnl-csum-segmentation", ("vxlan", "id 100 dstport 4789 udpcsum", ("4", "6"))),236("gre", "4", "tx-gre-segmentation", ("gre", "", ("4", "6"))),237("gre", "6", "tx-gre-segmentation", ("ip6gre","", ("4", "6"))),238)239240cases = []241for outer_ipver in ["4", "6"]:242for info in test_info:243# Skip if test which only works for a specific IP version244if info[1] and outer_ipver != info[1]:245continue246247if info[3]:248cases += [249test_builder(info[0], cfg, outer_ipver, info[2], info[3], inner_ipver)250for inner_ipver in info[3][2]251]252else:253cases.append(test_builder(info[0], cfg, outer_ipver, info[2], None, outer_ipver))254255ksft_run(cases=cases, args=(cfg, ))256ksft_exit()257258259if __name__ == "__main__":260main()261262263