Skip to content

Commit f52c96a

Browse files
committed
Add a case for packet loss when slowly reusing memory buffers
Under various conditions, when the host-to-device packet rate is high, we lose packets in QEMU due to a lack of guest-allocated buffers. Look also at virtio-win/kvm-guest-drivers-windows#1012 Signed-off-by: wji <[email protected]>
1 parent 3f60e13 commit f52c96a

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
- netkvm_buffer_shortage:
2+
virt_test_type = qemu
3+
type = netkvm_buffer_shortage
4+
only Windows
5+
only virtio_net
6+
vhost = on
7+
timeout = 360
8+
cdroms += " virtio"
9+
vms += " vm2"
10+
image_snapshot = yes
11+
start_vm = yes
12+
start_vm_vm2 = no
13+
smp = 2
14+
queues = ${smp}
15+
vectors = 1024
16+
copy_dest = "C:\"
17+
dest_location = "pushd ${copy_dest}"
18+
nic_extra_params_nic1 = ",rx_queue_size=1024,tx_queue_size=256"
19+
i386:
20+
psutil_whl = "psutil-6.1.1-cp37-abi3-win32.whl"
21+
x86_64:
22+
psutil_whl = "psutil-6.1.1-cp37-abi3-win_amd64.whl"
23+
s_py = '.\server.py'
24+
c_py = '.\client.py'
25+
port_num = 12345
26+
check_live_python = "tasklist | findstr /i python"
27+
c_pip_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${psutil_whl}" ${copy_dest}'
28+
c_pip_cmd = "py -m pip install ${psutil_whl}"
29+
s_py_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${s_py}" ${copy_dest}'
30+
s_py_cmd = "start cmd /c py ${s_py} ${port_num}"
31+
c_py_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${c_py}" ${copy_dest}'
32+
c_py_cmd = "start cmd /c py ${c_py} 99999 %s ${port_num}"
33+
param_name = "MinRxBufferPercent"
34+
param_values = "0 25 50 75 100"

qemu/tests/netkvm_buffer_shortage.py

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import re
2+
3+
from virttest import env_process, error_context, utils_misc, utils_net
4+
5+
6+
@error_context.context_aware
7+
def run(test, params, env):
8+
"""
9+
Simulate high packet rate between host and device by running Python scripts
10+
on both server and client side. This test is executed on two VM guests:
11+
12+
1) Start a VM guest as the server.
13+
2) Start a VM guest as the client.
14+
3) Simulate buffer allocation issues on the server node.
15+
4) Use a Python script to connect the client to the server.
16+
5) Adjust the MinRxBufferPercent parameter to work around the issue.
17+
6) Ensure no BSOD occurs on the client node.
18+
19+
:param test: QEMU test object.
20+
:param params: Dictionary of test parameters.
21+
:param env: Dictionary of test environment details.
22+
"""
23+
24+
def analyze_ping_results(session, dest, count, timeout):
25+
"""
26+
conduct a ping test to check the packet loss on slow memory buffer reallocation
27+
28+
:param session: Local execution hint or session to execute the ping command.
29+
:param count: Count of icmp packet.
30+
:param timeout: Timeout for the ping command.
31+
"""
32+
33+
status, output = utils_net.ping(
34+
dest=dest, session=session, count=count, timeout=timeout
35+
)
36+
if status != 0:
37+
test.fail("Ping failed, status: %s," " output: %s" % (status, output))
38+
if match := re.search(r"(\d+)% loss", output):
39+
return match.group(1)
40+
41+
def modify_and_analyze_params_result(vm, param_name, value):
42+
"""
43+
First set netkvm driver parameter 'param_name'
44+
to value 'param_value'. Then read the current and compare
45+
to 'param_value' to check identity Raised exception when
46+
checking netkvmco.exe setup was unsuccessful if something is wrong.
47+
48+
:param vm: the selected vm
49+
:param param_name: the netkvm driver parameter to modify
50+
:param value: the value to set to
51+
"""
52+
53+
utils_net.set_netkvm_param_value(vm, param_name, value)
54+
cur_value = utils_net.get_netkvm_param_value(vm, param_name)
55+
if cur_value != value:
56+
test.fail(f"Failed to set '{param_name}' to '{value}'")
57+
58+
def check_and_restart_port(session, port, script_to_run):
59+
"""
60+
Check if a Python process is listening on the specified port.
61+
If not, restart the appropriate Python script (server or client).
62+
63+
:param session: session to execute commands on the target machine.
64+
:param port: the port number to monitor.
65+
:param script_to_run: the path to the Python script to execute.
66+
"""
67+
68+
check_live_python = params.get("check_live_python")
69+
status, output = session.cmd_status_output(check_live_python, timeout=1200)
70+
if status == 0:
71+
return
72+
if "server" in script_to_run:
73+
s_session.cmd(dest_location)
74+
error_context.context(
75+
"Run python3 code runs on the server node", test.log.info
76+
)
77+
status, output = session.cmd_status_output(s_py_cmd, timeout=1200)
78+
if status != 0:
79+
test.fail("The server node failed to start.")
80+
else:
81+
c_session.cmd(dest_location)
82+
error_context.context(
83+
"Run python3 code runs on the client node", test.log.info
84+
)
85+
status, output = session.cmd_status_output(c_py_cmd % s_vm_ip, timeout=1200)
86+
if status != 0:
87+
test.fail(
88+
"The client could not connect to the server node.", test.log.info
89+
)
90+
91+
timeout = params.get_numeric("login_timeout", 360)
92+
port_num = params.get("port_num")
93+
s_py_cmd = params.get("s_py_cmd")
94+
c_py_cmd = params.get("c_py_cmd")
95+
param_name = params.get("param_name")
96+
param_values = params.get("param_values")
97+
dest_location = params.get("dest_location")
98+
c_pip_copy_cmd = params.get("c_pip_copy_cmd")
99+
c_pip_cmd = params.get("c_pip_cmd")
100+
c_py_copy_cmd = params.get("c_py_copy_cmd")
101+
s_py_copy_cmd = params.get("s_py_copy_cmd")
102+
103+
s_vm_name = params["vms"].split()[0]
104+
s_vm = env.get_vm(s_vm_name)
105+
s_vm.verify_alive()
106+
s_session = s_vm.wait_for_serial_login(
107+
timeout=int(params.get("login_timeout", 360))
108+
)
109+
s_vm_ip = s_vm.get_address()
110+
111+
c_vm_name = params["vms"].split(s_vm_name)[1].strip()
112+
c_vm_params = params.object_params(c_vm_name)
113+
c_vm_params["nic_extra_params_nic1"] = ""
114+
c_vm_params["start_vm"] = "yes"
115+
env_process.preprocess_vm(test, c_vm_params, env, c_vm_name)
116+
c_vm = env.get_vm(c_vm_name)
117+
c_vm.verify_alive()
118+
c_session = c_vm.wait_for_serial_login(
119+
timeout=int(params.get("login_timeout", 360))
120+
)
121+
c_vm_ip = c_vm.get_address()
122+
123+
# Here, we want to install some dependent packages
124+
s_session.cmd(dest_location)
125+
s_py_copy_cmd = utils_misc.set_winutils_letter(s_session, s_py_copy_cmd)
126+
s_session.cmd(s_py_copy_cmd)
127+
c_session.cmd(dest_location)
128+
c_pip_copy_cmd = utils_misc.set_winutils_letter(c_session, c_pip_copy_cmd)
129+
c_session.cmd(c_pip_copy_cmd)
130+
c_session.cmd(c_pip_cmd)
131+
c_py_copy_cmd = utils_misc.set_winutils_letter(c_session, c_py_copy_cmd)
132+
c_session.cmd(c_py_copy_cmd)
133+
134+
ping_results = []
135+
error_context.context(
136+
"Open the NIC properties and change the values in the server node",
137+
test.log.info,
138+
)
139+
for value in param_values.split(" "):
140+
modify_and_analyze_params_result(vm=s_vm, param_name=param_name, value=value)
141+
check_and_restart_port(session=s_session, port=port_num, script_to_run=s_py_cmd)
142+
check_and_restart_port(session=c_session, port=port_num, script_to_run=c_py_cmd)
143+
ping_results.append(
144+
int(
145+
analyze_ping_results(
146+
session=c_session, dest=s_vm_ip, count=100, timeout=timeout
147+
)
148+
)
149+
)
150+
151+
error_context.context(
152+
"Get the packet loss percentage of the ping request", test.log.info
153+
)
154+
if sum(ping_results) != 0:
155+
if not all(
156+
ping_results[i] > ping_results[i + 1] for i in range(len(ping_results) - 1)
157+
):
158+
test.fail(
159+
"With the parameter, the number of lost packets should be "
160+
"less than without the parameter."
161+
)
162+
163+
error_context.context("no BSOD will occur on the client side", test.log.info)
164+
for value in param_values.split(" "):
165+
modify_and_analyze_params_result(vm=c_vm, param_name=param_name, value=value)
166+
status, output = utils_net.ping(
167+
dest=c_vm_ip, session=c_session, count=10, timeout=60
168+
)
169+
if status != 0:
170+
test.fail("Ping failed, status: %s," " output: %s" % (status, output))

0 commit comments

Comments
 (0)