xref: /linux/tools/testing/selftests/drivers/net/hw/devmem_lib.py (revision 6cac32fc3f1f8e5c6698b8eb88ae541d3c332584)
1*6cac32fcSBobby Eshleman# SPDX-License-Identifier: GPL-2.0
2*6cac32fcSBobby Eshleman"""Shared helpers for devmem TCP selftests."""
3*6cac32fcSBobby Eshleman
4*6cac32fcSBobby Eshlemanimport re
5*6cac32fcSBobby Eshleman
6*6cac32fcSBobby Eshlemanfrom lib.py import (bkg, cmd, defer, ethtool, rand_port, wait_port_listen,
7*6cac32fcSBobby Eshleman                    ksft_eq, KsftSkipEx, NetNSEnter, EthtoolFamily,
8*6cac32fcSBobby Eshleman                    NetdevFamily)
9*6cac32fcSBobby Eshleman
10*6cac32fcSBobby Eshleman
11*6cac32fcSBobby Eshlemandef require_devmem(cfg):
12*6cac32fcSBobby Eshleman    """Probe ncdevmem on cfg.ifname and SKIP the test if devmem isn't supported."""
13*6cac32fcSBobby Eshleman    if not hasattr(cfg, "devmem_probed"):
14*6cac32fcSBobby Eshleman        probe_command = f"{cfg.bin_local} -f {cfg.ifname}"
15*6cac32fcSBobby Eshleman        cfg.devmem_supported = cmd(probe_command, fail=False, shell=True).ret == 0
16*6cac32fcSBobby Eshleman        cfg.devmem_probed = True
17*6cac32fcSBobby Eshleman
18*6cac32fcSBobby Eshleman    if not cfg.devmem_supported:
19*6cac32fcSBobby Eshleman        raise KsftSkipEx("Test requires devmem support")
20*6cac32fcSBobby Eshleman
21*6cac32fcSBobby Eshleman
22*6cac32fcSBobby Eshlemandef configure_nic(cfg):
23*6cac32fcSBobby Eshleman    """Channels, rings, RSS, queue lease for netkit devmem."""
24*6cac32fcSBobby Eshleman    if not hasattr(cfg, 'netns'):
25*6cac32fcSBobby Eshleman        return
26*6cac32fcSBobby Eshleman
27*6cac32fcSBobby Eshleman    cfg.require_ipver('6')
28*6cac32fcSBobby Eshleman    ethnl = EthtoolFamily()
29*6cac32fcSBobby Eshleman
30*6cac32fcSBobby Eshleman    channels = ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
31*6cac32fcSBobby Eshleman    channels = channels['combined-count']
32*6cac32fcSBobby Eshleman    if channels < 2:
33*6cac32fcSBobby Eshleman        raise KsftSkipEx(
34*6cac32fcSBobby Eshleman            'Test requires NETIF with at least 2 combined channels'
35*6cac32fcSBobby Eshleman        )
36*6cac32fcSBobby Eshleman
37*6cac32fcSBobby Eshleman    rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}})
38*6cac32fcSBobby Eshleman    orig_rx_rings = rings['rx']
39*6cac32fcSBobby Eshleman    orig_hds_thresh = rings.get('hds-thresh', 0)
40*6cac32fcSBobby Eshleman    orig_data_split = rings.get('tcp-data-split', 'unknown')
41*6cac32fcSBobby Eshleman
42*6cac32fcSBobby Eshleman    ethnl.rings_set({'header': {'dev-index': cfg.ifindex},
43*6cac32fcSBobby Eshleman                     'tcp-data-split': 'enabled',
44*6cac32fcSBobby Eshleman                     'hds-thresh': 0,
45*6cac32fcSBobby Eshleman                     'rx': min(64, orig_rx_rings)})
46*6cac32fcSBobby Eshleman    defer(ethnl.rings_set, {'header': {'dev-index': cfg.ifindex},
47*6cac32fcSBobby Eshleman                            'tcp-data-split': orig_data_split,
48*6cac32fcSBobby Eshleman                            'hds-thresh': orig_hds_thresh,
49*6cac32fcSBobby Eshleman                            'rx': orig_rx_rings})
50*6cac32fcSBobby Eshleman
51*6cac32fcSBobby Eshleman    cfg.src_queue = channels - 1
52*6cac32fcSBobby Eshleman    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
53*6cac32fcSBobby Eshleman    defer(ethtool, f"-X {cfg.ifname} default")
54*6cac32fcSBobby Eshleman
55*6cac32fcSBobby Eshleman    if not hasattr(cfg, 'nk_queue'):
56*6cac32fcSBobby Eshleman        with NetNSEnter(str(cfg.netns)):
57*6cac32fcSBobby Eshleman            netdevnl = NetdevFamily()
58*6cac32fcSBobby Eshleman            lease_result = netdevnl.queue_create({
59*6cac32fcSBobby Eshleman                "ifindex": cfg.nk_guest_ifindex,
60*6cac32fcSBobby Eshleman                "type": "rx",
61*6cac32fcSBobby Eshleman                "lease": {
62*6cac32fcSBobby Eshleman                    "ifindex": cfg.ifindex,
63*6cac32fcSBobby Eshleman                    "queue": {"id": cfg.src_queue, "type": "rx"},
64*6cac32fcSBobby Eshleman                    "netns-id": 0,
65*6cac32fcSBobby Eshleman                },
66*6cac32fcSBobby Eshleman            })
67*6cac32fcSBobby Eshleman            cfg.nk_queue = lease_result['id']
68*6cac32fcSBobby Eshleman
69*6cac32fcSBobby Eshleman
70*6cac32fcSBobby Eshlemandef set_flow_rule(cfg, port):
71*6cac32fcSBobby Eshleman    """Install a flow rule steering to src_queue and return the flow rule ID."""
72*6cac32fcSBobby Eshleman    output = ethtool(
73*6cac32fcSBobby Eshleman        f"-N {cfg.ifname} flow-type tcp6 dst-port {port}"
74*6cac32fcSBobby Eshleman        f" action {cfg.src_queue}"
75*6cac32fcSBobby Eshleman    ).stdout
76*6cac32fcSBobby Eshleman    return int(re.search(r'ID (\d+)', output).group(1))
77*6cac32fcSBobby Eshleman
78*6cac32fcSBobby Eshleman
79*6cac32fcSBobby Eshlemandef ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False):
80*6cac32fcSBobby Eshleman    """Build the ncdevmem RX listener command."""
81*6cac32fcSBobby Eshleman    if hasattr(cfg, 'netns'):
82*6cac32fcSBobby Eshleman        flow_rule_id = set_flow_rule(cfg, port)
83*6cac32fcSBobby Eshleman        defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
84*6cac32fcSBobby Eshleman
85*6cac32fcSBobby Eshleman        ifname = cfg.nk_guest_ifname
86*6cac32fcSBobby Eshleman        addr = cfg.nk_guest_ipv6
87*6cac32fcSBobby Eshleman        extras = [f"-t {cfg.nk_queue}", "-q 1", "-n"]
88*6cac32fcSBobby Eshleman    else:
89*6cac32fcSBobby Eshleman        ifname = cfg.ifname
90*6cac32fcSBobby Eshleman        addr = cfg.addr
91*6cac32fcSBobby Eshleman        extras = []
92*6cac32fcSBobby Eshleman        if flow_steer:
93*6cac32fcSBobby Eshleman            extras.append(f"-c {cfg.remote_addr}")
94*6cac32fcSBobby Eshleman
95*6cac32fcSBobby Eshleman    if verify:
96*6cac32fcSBobby Eshleman        extras.append("-v 7")
97*6cac32fcSBobby Eshleman    if fail_on_linear:
98*6cac32fcSBobby Eshleman        extras.append("-L")
99*6cac32fcSBobby Eshleman
100*6cac32fcSBobby Eshleman    parts = [cfg.bin_local, "-l", f"-f {ifname}", f"-s {addr}",
101*6cac32fcSBobby Eshleman             f"-p {port}", *extras]
102*6cac32fcSBobby Eshleman    return " ".join(parts)
103*6cac32fcSBobby Eshleman
104*6cac32fcSBobby Eshleman
105*6cac32fcSBobby Eshlemandef ncdevmem_tx(cfg, port, chunk_size=0):
106*6cac32fcSBobby Eshleman    """Build the ncdevmem TX send command."""
107*6cac32fcSBobby Eshleman    if hasattr(cfg, 'netns'):
108*6cac32fcSBobby Eshleman        ifname = cfg.nk_guest_ifname
109*6cac32fcSBobby Eshleman        addr = cfg.remote_addr_v['6']
110*6cac32fcSBobby Eshleman        extras = ["-t 0", "-q 1", "-n"]
111*6cac32fcSBobby Eshleman    else:
112*6cac32fcSBobby Eshleman        ifname = cfg.ifname
113*6cac32fcSBobby Eshleman        addr = cfg.remote_addr
114*6cac32fcSBobby Eshleman        extras = []
115*6cac32fcSBobby Eshleman
116*6cac32fcSBobby Eshleman    if chunk_size:
117*6cac32fcSBobby Eshleman        extras.append(f"-z {chunk_size}")
118*6cac32fcSBobby Eshleman
119*6cac32fcSBobby Eshleman    parts = [cfg.bin_local, f"-f {ifname}", f"-s {addr}",
120*6cac32fcSBobby Eshleman             f"-p {port}", *extras]
121*6cac32fcSBobby Eshleman    return " ".join(parts)
122*6cac32fcSBobby Eshleman
123*6cac32fcSBobby Eshleman
124*6cac32fcSBobby Eshlemandef socat_send(cfg, port, buf_size=0):
125*6cac32fcSBobby Eshleman    """Socat command for sending to the devmem listener.
126*6cac32fcSBobby Eshleman
127*6cac32fcSBobby Eshleman    When buf_size > 0, force one TCP segment per write of exactly that size by
128*6cac32fcSBobby Eshleman    setting socat's buffer (-b) and disabling Nagle (TCP_NODELAY).
129*6cac32fcSBobby Eshleman    """
130*6cac32fcSBobby Eshleman    proto = f"TCP{cfg.addr_ipver}"
131*6cac32fcSBobby Eshleman
132*6cac32fcSBobby Eshleman    if hasattr(cfg, 'netns'):
133*6cac32fcSBobby Eshleman        addr = f"[{cfg.nk_guest_ipv6}]"
134*6cac32fcSBobby Eshleman    else:
135*6cac32fcSBobby Eshleman        addr = cfg.baddr
136*6cac32fcSBobby Eshleman
137*6cac32fcSBobby Eshleman    suffix = f",bind={cfg.remote_baddr}:{port}"
138*6cac32fcSBobby Eshleman
139*6cac32fcSBobby Eshleman    buf = ""
140*6cac32fcSBobby Eshleman    if buf_size:
141*6cac32fcSBobby Eshleman        buf = f"-b {buf_size}"
142*6cac32fcSBobby Eshleman        suffix += ",nodelay"
143*6cac32fcSBobby Eshleman
144*6cac32fcSBobby Eshleman    return f"socat {buf} -u - {proto}:{addr}:{port}{suffix}"
145*6cac32fcSBobby Eshleman
146*6cac32fcSBobby Eshleman
147*6cac32fcSBobby Eshlemandef socat_listen(cfg, port):
148*6cac32fcSBobby Eshleman    """Socat listen command for TX tests."""
149*6cac32fcSBobby Eshleman    return f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}"
150*6cac32fcSBobby Eshleman
151*6cac32fcSBobby Eshleman
152*6cac32fcSBobby Eshlemandef setup_test(cfg, bin_local):
153*6cac32fcSBobby Eshleman    """Stash the local ncdevmem path on cfg and deploy it to the remote."""
154*6cac32fcSBobby Eshleman    cfg.bin_local = bin_local
155*6cac32fcSBobby Eshleman    cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
156*6cac32fcSBobby Eshleman
157*6cac32fcSBobby Eshleman
158*6cac32fcSBobby Eshlemandef run_rx(cfg):
159*6cac32fcSBobby Eshleman    """Run the devmem RX test."""
160*6cac32fcSBobby Eshleman    require_devmem(cfg)
161*6cac32fcSBobby Eshleman    configure_nic(cfg)
162*6cac32fcSBobby Eshleman    port = rand_port()
163*6cac32fcSBobby Eshleman    socat = socat_send(cfg, port)
164*6cac32fcSBobby Eshleman    data_pipe = (f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | head -c 1K"
165*6cac32fcSBobby Eshleman                 f" | {socat}")
166*6cac32fcSBobby Eshleman    netns = getattr(cfg, "netns", None)
167*6cac32fcSBobby Eshleman
168*6cac32fcSBobby Eshleman    listen_cmd = ncdevmem_rx(cfg, port, flow_steer=not hasattr(cfg, 'netns'))
169*6cac32fcSBobby Eshleman    with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem:
170*6cac32fcSBobby Eshleman        wait_port_listen(port, proto="tcp", ns=netns)
171*6cac32fcSBobby Eshleman        cmd(data_pipe, host=cfg.remote, shell=True)
172*6cac32fcSBobby Eshleman    ksft_eq(ncdevmem.ret, 0)
173*6cac32fcSBobby Eshleman
174*6cac32fcSBobby Eshleman
175*6cac32fcSBobby Eshlemandef run_tx(cfg):
176*6cac32fcSBobby Eshleman    """Run the devmem TX test."""
177*6cac32fcSBobby Eshleman    require_devmem(cfg)
178*6cac32fcSBobby Eshleman    configure_nic(cfg)
179*6cac32fcSBobby Eshleman    netns = getattr(cfg, "netns", None)
180*6cac32fcSBobby Eshleman    port = rand_port()
181*6cac32fcSBobby Eshleman    tx_cmd = ncdevmem_tx(cfg, port)
182*6cac32fcSBobby Eshleman    listen_cmd = socat_listen(cfg, port)
183*6cac32fcSBobby Eshleman
184*6cac32fcSBobby Eshleman    with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
185*6cac32fcSBobby Eshleman        wait_port_listen(port, host=cfg.remote)
186*6cac32fcSBobby Eshleman        cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True)
187*6cac32fcSBobby Eshleman    ksft_eq(socat.stdout.strip(), "hello\nworld")
188*6cac32fcSBobby Eshleman
189*6cac32fcSBobby Eshleman
190*6cac32fcSBobby Eshlemandef run_tx_chunks(cfg):
191*6cac32fcSBobby Eshleman    """Run the devmem TX chunking test."""
192*6cac32fcSBobby Eshleman    require_devmem(cfg)
193*6cac32fcSBobby Eshleman    configure_nic(cfg)
194*6cac32fcSBobby Eshleman    netns = getattr(cfg, "netns", None)
195*6cac32fcSBobby Eshleman    port = rand_port()
196*6cac32fcSBobby Eshleman    tx_cmd = ncdevmem_tx(cfg, port, chunk_size=3)
197*6cac32fcSBobby Eshleman    listen_cmd = socat_listen(cfg, port)
198*6cac32fcSBobby Eshleman
199*6cac32fcSBobby Eshleman    with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
200*6cac32fcSBobby Eshleman        wait_port_listen(port, host=cfg.remote)
201*6cac32fcSBobby Eshleman        cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True)
202*6cac32fcSBobby Eshleman    ksft_eq(socat.stdout.strip(), "hello\nworld")
203*6cac32fcSBobby Eshleman
204*6cac32fcSBobby Eshleman
205*6cac32fcSBobby Eshlemandef run_rx_hds(cfg):
206*6cac32fcSBobby Eshleman    """Run the HDS test by running devmem RX across a segment size sweep."""
207*6cac32fcSBobby Eshleman    require_devmem(cfg)
208*6cac32fcSBobby Eshleman    configure_nic(cfg)
209*6cac32fcSBobby Eshleman    netns = getattr(cfg, "netns", None)
210*6cac32fcSBobby Eshleman
211*6cac32fcSBobby Eshleman    for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]:
212*6cac32fcSBobby Eshleman        port = rand_port()
213*6cac32fcSBobby Eshleman
214*6cac32fcSBobby Eshleman        listen_cmd = ncdevmem_rx(cfg, port, verify=False,
215*6cac32fcSBobby Eshleman                                 fail_on_linear=True)
216*6cac32fcSBobby Eshleman        socat = socat_send(cfg, port, buf_size=size)
217*6cac32fcSBobby Eshleman
218*6cac32fcSBobby Eshleman        with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem:
219*6cac32fcSBobby Eshleman            wait_port_listen(port, proto="tcp", ns=netns)
220*6cac32fcSBobby Eshleman            cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | "
221*6cac32fcSBobby Eshleman                f"{socat}", host=cfg.remote, shell=True)
222*6cac32fcSBobby Eshleman        ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}")
223