Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/drivers/net/lib/py/load.py
50258 views
1
# SPDX-License-Identifier: GPL-2.0
2
3
import re
4
import time
5
import json
6
7
from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen
8
9
10
class Iperf3Runner:
11
"""
12
Sets up and runs iperf3 traffic.
13
"""
14
def __init__(self, env, port=None, server_ip=None, client_ip=None):
15
env.require_cmd("iperf3", local=True, remote=True)
16
self.env = env
17
self.port = rand_port() if port is None else port
18
self.server_ip = server_ip
19
self.client_ip = client_ip
20
21
def _build_server(self):
22
cmdline = f"iperf3 -s -1 -p {self.port}"
23
if self.server_ip:
24
cmdline += f" -B {self.server_ip}"
25
return cmdline
26
27
def _build_client(self, streams, duration, reverse):
28
host = self.env.addr if self.server_ip is None else self.server_ip
29
cmdline = f"iperf3 -c {host} -p {self.port} -P {streams} -t {duration} -J"
30
if self.client_ip:
31
cmdline += f" -B {self.client_ip}"
32
if reverse:
33
cmdline += " --reverse"
34
return cmdline
35
36
def start_server(self):
37
"""
38
Starts an iperf3 server with optional bind IP.
39
"""
40
cmdline = self._build_server()
41
proc = cmd(cmdline, background=True)
42
wait_port_listen(self.port)
43
time.sleep(0.1)
44
return proc
45
46
def start_client(self, background=False, streams=1, duration=10, reverse=False):
47
"""
48
Starts the iperf3 client with the configured options.
49
"""
50
cmdline = self._build_client(streams, duration, reverse)
51
return cmd(cmdline, background=background, host=self.env.remote)
52
53
def measure_bandwidth(self, reverse=False):
54
"""
55
Runs an iperf3 measurement and returns the average bandwidth (Gbps).
56
Discards the first and last few reporting intervals and uses only the
57
middle part of the run where throughput is typically stable.
58
"""
59
self.start_server()
60
result = self.start_client(duration=10, reverse=reverse)
61
62
if result.ret != 0:
63
raise RuntimeError("iperf3 failed to run successfully")
64
try:
65
out = json.loads(result.stdout)
66
except json.JSONDecodeError as exc:
67
raise ValueError("Failed to parse iperf3 JSON output") from exc
68
69
intervals = out.get("intervals", [])
70
samples = [i["sum"]["bits_per_second"] / 1e9 for i in intervals]
71
if len(samples) < 10:
72
raise ValueError(f"iperf3 returned too few intervals: {len(samples)}")
73
# Discard potentially unstable first and last 3 seconds.
74
stable = samples[3:-3]
75
76
avg = sum(stable) / len(stable)
77
78
return avg
79
80
81
class GenerateTraffic:
82
def __init__(self, env, port=None):
83
self.env = env
84
self.runner = Iperf3Runner(env, port)
85
86
self._iperf_server = self.runner.start_server()
87
self._iperf_client = self.runner.start_client(background=True, streams=16, duration=86400)
88
89
# Wait for traffic to ramp up
90
if not self._wait_pkts(pps=1000):
91
self.stop(verbose=True)
92
raise Exception("iperf3 traffic did not ramp up")
93
94
def _wait_pkts(self, pkt_cnt=None, pps=None):
95
"""
96
Wait until we've seen pkt_cnt or until traffic ramps up to pps.
97
Only one of pkt_cnt or pss can be specified.
98
"""
99
pkt_start = ip("-s link show dev " + self.env.ifname, json=True)[0]["stats64"]["rx"]["packets"]
100
for _ in range(50):
101
time.sleep(0.1)
102
pkt_now = ip("-s link show dev " + self.env.ifname, json=True)[0]["stats64"]["rx"]["packets"]
103
if pps:
104
if pkt_now - pkt_start > pps / 10:
105
return True
106
pkt_start = pkt_now
107
elif pkt_cnt:
108
if pkt_now - pkt_start > pkt_cnt:
109
return True
110
return False
111
112
def wait_pkts_and_stop(self, pkt_cnt):
113
failed = not self._wait_pkts(pkt_cnt=pkt_cnt)
114
self.stop(verbose=failed)
115
116
def stop(self, verbose=None):
117
self._iperf_client.process(terminate=True)
118
if verbose:
119
ksft_pr(">> Client:")
120
ksft_pr(self._iperf_client.stdout)
121
ksft_pr(self._iperf_client.stderr)
122
self._iperf_server.process(terminate=True)
123
if verbose:
124
ksft_pr(">> Server:")
125
ksft_pr(self._iperf_server.stdout)
126
ksft_pr(self._iperf_server.stderr)
127
self._wait_client_stopped()
128
129
def _wait_client_stopped(self, sleep=0.005, timeout=5):
130
end = time.monotonic() + timeout
131
132
live_port_pattern = re.compile(fr":{self.runner.port:04X} 0[^6] ")
133
134
while time.monotonic() < end:
135
data = cmd("cat /proc/net/tcp*", host=self.env.remote).stdout
136
if not live_port_pattern.search(data):
137
return
138
time.sleep(sleep)
139
raise Exception(f"Waiting for client to stop timed out after {timeout}s")
140
141