Skip to content

Commit fbbf935

Browse files
MohanPrasad-JPaolo Abeni
authored andcommitted
selftests: nic_performance: Add selftest for performance of NIC driver
Add selftest case to check the send and receive throughput. Supported link modes between local NIC driver and partner are varied. Then send and receive throughput is captured and verified. Test uses iperf3 tool. Add iperf3 server/client function in GenerateTraffic class. Signed-off-by: Mohan Prasad J <mohan.prasad@microchip.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent c087dc5 commit fbbf935

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

tools/testing/selftests/drivers/net/hw/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ TEST_PROGS = \
1212
hw_stats_l3_gre.sh \
1313
loopback.sh \
1414
nic_link_layer.py \
15+
nic_performance.py \
1516
pp_alloc_fail.py \
1617
rss_ctx.py \
1718
#
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
#Introduction:
5+
#This file has basic performance test for generic NIC drivers.
6+
#The test comprises of throughput check for TCP and UDP streams.
7+
#
8+
#Setup:
9+
#Connect the DUT PC with NIC card to partner pc back via ethernet medium of your choice(RJ45, T1)
10+
#
11+
# DUT PC Partner PC
12+
#┌───────────────────────┐ ┌──────────────────────────┐
13+
#│ │ │ │
14+
#│ │ │ │
15+
#│ ┌───────────┐ │ │
16+
#│ │DUT NIC │ Eth │ │
17+
#│ │Interface ─┼─────────────────────────┼─ any eth Interface │
18+
#│ └───────────┘ │ │
19+
#│ │ │ │
20+
#│ │ │ │
21+
#└───────────────────────┘ └──────────────────────────┘
22+
#
23+
#Configurations:
24+
#To prevent interruptions, Add ethtool, ip to the sudoers list in remote PC and get the ssh key from remote.
25+
#Required minimum ethtool version is 6.10
26+
#Change the below configuration based on your hw needs.
27+
# """Default values"""
28+
#time_delay = 8 #time taken to wait for transitions to happen, in seconds.
29+
#test_duration = 10 #performance test duration for the throughput check, in seconds.
30+
#send_throughput_threshold = 80 #percentage of send throughput required to pass the check
31+
#receive_throughput_threshold = 50 #percentage of receive throughput required to pass the check
32+
33+
import time
34+
import json
35+
import argparse
36+
from lib.py import ksft_run, ksft_exit, ksft_pr, ksft_true
37+
from lib.py import KsftFailEx, KsftSkipEx, GenerateTraffic
38+
from lib.py import NetDrvEpEnv, bkg, wait_port_listen
39+
from lib.py import cmd
40+
from lib.py import LinkConfig
41+
42+
class TestConfig:
43+
def __init__(self, time_delay: int, test_duration: int, send_throughput_threshold: int, receive_throughput_threshold: int) -> None:
44+
self.time_delay = time_delay
45+
self.test_duration = test_duration
46+
self.send_throughput_threshold = send_throughput_threshold
47+
self.receive_throughput_threshold = receive_throughput_threshold
48+
49+
def _pre_test_checks(cfg: object, link_config: LinkConfig) -> None:
50+
if not link_config.verify_link_up():
51+
KsftSkipEx(f"Link state of interface {cfg.ifname} is DOWN")
52+
common_link_modes = link_config.common_link_modes
53+
if common_link_modes is None:
54+
KsftSkipEx("No common link modes found")
55+
if link_config.partner_netif == None:
56+
KsftSkipEx("Partner interface is not available")
57+
if link_config.check_autoneg_supported():
58+
KsftSkipEx("Auto-negotiation not supported by local")
59+
if link_config.check_autoneg_supported(remote=True):
60+
KsftSkipEx("Auto-negotiation not supported by remote")
61+
cfg.require_cmd("iperf3", remote=True)
62+
63+
def check_throughput(cfg: object, link_config: LinkConfig, test_config: TestConfig, protocol: str, traffic: GenerateTraffic) -> None:
64+
common_link_modes = link_config.common_link_modes
65+
speeds, duplex_modes = link_config.get_speed_duplex_values(common_link_modes)
66+
"""Test duration in seconds"""
67+
duration = test_config.test_duration
68+
69+
ksft_pr(f"{protocol} test")
70+
test_type = "-u" if protocol == "UDP" else ""
71+
72+
send_throughput = []
73+
receive_throughput = []
74+
for idx in range(0, len(speeds)):
75+
if link_config.set_speed_and_duplex(speeds[idx], duplex_modes[idx]) == False:
76+
raise KsftFailEx(f"Not able to set speed and duplex parameters for {cfg.ifname}")
77+
time.sleep(test_config.time_delay)
78+
if not link_config.verify_link_up():
79+
raise KsftSkipEx(f"Link state of interface {cfg.ifname} is DOWN")
80+
81+
send_command=f"{test_type} -b 0 -t {duration} --json"
82+
receive_command=f"{test_type} -b 0 -t {duration} --reverse --json"
83+
84+
send_result = traffic.run_remote_test(cfg, command=send_command)
85+
if send_result.ret != 0:
86+
raise KsftSkipEx("Error occurred during data transmit: {send_result.stdout}")
87+
88+
send_output = send_result.stdout
89+
send_data = json.loads(send_output)
90+
91+
"""Convert throughput to Mbps"""
92+
send_throughput.append(round(send_data['end']['sum_sent']['bits_per_second'] / 1e6, 2))
93+
ksft_pr(f"{protocol}: Send throughput: {send_throughput[idx]} Mbps")
94+
95+
receive_result = traffic.run_remote_test(cfg, command=receive_command)
96+
if receive_result.ret != 0:
97+
raise KsftSkipEx("Error occurred during data receive: {receive_result.stdout}")
98+
99+
receive_output = receive_result.stdout
100+
receive_data = json.loads(receive_output)
101+
102+
"""Convert throughput to Mbps"""
103+
receive_throughput.append(round(receive_data['end']['sum_received']['bits_per_second'] / 1e6, 2))
104+
ksft_pr(f"{protocol}: Receive throughput: {receive_throughput[idx]} Mbps")
105+
106+
"""Check whether throughput is not below the threshold (default values set at start)"""
107+
for idx in range(0, len(speeds)):
108+
send_threshold = float(speeds[idx]) * float(test_config.send_throughput_threshold / 100)
109+
receive_threshold = float(speeds[idx]) * float(test_config.receive_throughput_threshold / 100)
110+
ksft_true(send_throughput[idx] >= send_threshold, f"{protocol}: Send throughput is below threshold for {speeds[idx]} Mbps in {duplex_modes[idx]} duplex")
111+
ksft_true(receive_throughput[idx] >= receive_threshold, f"{protocol}: Receive throughput is below threshold for {speeds[idx]} Mbps in {duplex_modes[idx]} duplex")
112+
113+
def test_tcp_throughput(cfg: object, link_config: LinkConfig, test_config: TestConfig, traffic: GenerateTraffic) -> None:
114+
_pre_test_checks(cfg, link_config)
115+
check_throughput(cfg, link_config, test_config, 'TCP', traffic)
116+
117+
def test_udp_throughput(cfg: object, link_config: LinkConfig, test_config: TestConfig, traffic: GenerateTraffic) -> None:
118+
_pre_test_checks(cfg, link_config)
119+
check_throughput(cfg, link_config, test_config, 'UDP', traffic)
120+
121+
def main() -> None:
122+
parser = argparse.ArgumentParser(description="Run basic performance test for NIC driver")
123+
parser.add_argument('--time-delay', type=int, default=8, help='Time taken to wait for transitions to happen(in seconds). Default is 8 seconds.')
124+
parser.add_argument('--test-duration', type=int, default=10, help='Performance test duration for the throughput check, in seconds. Default is 10 seconds.')
125+
parser.add_argument('--stt', type=int, default=80, help='Send throughput Threshold: Percentage of send throughput upon actual throughput required to pass the throughput check (in percentage). Default is 80.')
126+
parser.add_argument('--rtt', type=int, default=50, help='Receive throughput Threshold: Percentage of receive throughput upon actual throughput required to pass the throughput check (in percentage). Default is 50.')
127+
args=parser.parse_args()
128+
test_config = TestConfig(args.time_delay, args.test_duration, args.stt, args.rtt)
129+
with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
130+
traffic = GenerateTraffic(cfg)
131+
link_config = LinkConfig(cfg)
132+
ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, link_config, test_config, traffic, ))
133+
link_config.reset_interface()
134+
ksft_exit()
135+
136+
if __name__ == "__main__":
137+
main()

tools/testing/selftests/drivers/net/lib/py/load.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import time
44

5-
from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen
5+
from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen, bkg
66

77
class GenerateTraffic:
88
def __init__(self, env, port=None):
@@ -23,6 +23,24 @@ def __init__(self, env, port=None):
2323
self.stop(verbose=True)
2424
raise Exception("iperf3 traffic did not ramp up")
2525

26+
def run_remote_test(self, env: object, port=None, command=None):
27+
if port is None:
28+
port = rand_port()
29+
try:
30+
server_cmd = f"iperf3 -s 1 -p {port} --one-off"
31+
with bkg(server_cmd, host=env.remote):
32+
#iperf3 opens TCP connection as default in server
33+
#-u to be specified in client command for UDP
34+
wait_port_listen(port, host=env.remote)
35+
except Exception as e:
36+
raise Exception(f"Unexpected error occurred while running server command: {e}")
37+
try:
38+
client_cmd = f"iperf3 -c {env.remote_addr} -p {port} {command}"
39+
proc = cmd(client_cmd)
40+
return proc
41+
except Exception as e:
42+
raise Exception(f"Unexpected error occurred while running client command: {e}")
43+
2644
def _wait_pkts(self, pkt_cnt=None, pps=None):
2745
"""
2846
Wait until we've seen pkt_cnt or until traffic ramps up to pps.

0 commit comments

Comments
 (0)