Skip to content

Commit

Permalink
Add a case for packet loss when slowly reusing memory buffers
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
heywji committed Jan 22, 2025
1 parent 3f60e13 commit b2fea54
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 0 deletions.
35 changes: 35 additions & 0 deletions qemu/tests/cfg/netkvm_buffer_shortage.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
- netkvm_buffer_shortage:
virt_test_type = qemu
type = netkvm_buffer_shortage
only Windows
only virtio_net
vhost = on
timeout = 360
cdroms += " virtio"
vms += " vm2"
image_snapshot = yes
start_vm = yes
start_vm_vm2 = no
smp = 2
queues = ${smp}
vectors = 1024
copy_dest = "C:\"
dest_location = "pushd ${copy_dest}"
nic_extra_params_nic1 = ",rx_queue_size=1024,tx_queue_size=256"
nic_extra_params_type = "{'rx_queue_size': 'int', 'tx_queue_size': 'int'}"
i386:
psutil_whl = "psutil-6.1.1-cp37-abi3-win32.whl"
x86_64:
psutil_whl = "psutil-6.1.1-cp37-abi3-win_amd64.whl"
s_py = '.\server.py'
c_py = '.\client.py'
port_num = 12345
check_live_python = "tasklist | findstr /i python"
c_pip_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${psutil_whl}" ${copy_dest}'
c_pip_cmd = "py -m pip install ${psutil_whl}"
s_py_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${s_py}" ${copy_dest}'
s_py_cmd = "start cmd /c py ${s_py} ${port_num}"
c_py_copy_cmd = 'xcopy "WIN_UTILS:\packet_loss_scripts\${c_py}" ${copy_dest}'
c_py_cmd = "start cmd /c py ${c_py} 99999 %s ${port_num}"
param_name = "MinRxBufferPercent"
param_values = "0 25 50 75 100"
157 changes: 157 additions & 0 deletions qemu/tests/netkvm_buffer_shortage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import re

from virttest import env_process, error_context, utils_misc, utils_net


@error_context.context_aware
def run(test, params, env):
"""
Simulate high packet rate between host and device by running Python scripts
on both server and client side. This test is executed on two VM guests:
1) Start a VM guest as the server.
2) Start a VM guest as the client.
3) Simulate buffer allocation issues on the server node.
4) Use a Python script to connect the client to the server.
5) Adjust the MinRxBufferPercent parameter to work around the issue.
6) Ensure no BSOD occurs on the client node.
:param test: QEMU test object.
:param params: Dictionary of test parameters.
:param env: Dictionary of test environment details.
"""

def analyze_ping_results(session, count, timeout):
"""
conduct a ping test to check the packet loss on slow memory buffer reallocation
:param session: Local executon hint or session to execute the ping command.
:param count: Count of icmp packet.
:param timeout: Timeout for the ping command.
"""

status, output = utils_net.ping(
s_vm_ip, session=c_session, count=count, timeout=timeout
)
if status != 0:
test.fail("Ping failed, status: %s," " output: %s" % (status, output))
pattern = r"(\d+)% loss"
match = re.search(pattern, output)
if match:
return match.group(1)

def modify_and_analyze_params_result(vm, netkvmco_name, value):
"""
First set netkvm driver parameter 'param_name'
to value 'param_value'. Then read the current and compare
to 'param_value' to check identity Raised exception when
checking netkvmco.exe setup was unsuccessful if something is wrong.
param vm: the selected vm
param netkvmco_name: the netkvm driver parameter to modify
param value: the value to set to
"""

utils_net.set_netkvm_param_value(vm, netkvmco_name, value)
cur_value = utils_net.get_netkvm_param_value(vm, netkvmco_name)
if cur_value != value:
test.fail(f"Current value '{cur_value}' was not equires '{value}'")

def check_and_restart_port(session, port, script_to_run):
"""
Check if a Python process is listening on the specified port.
If not, restart the appropriate Python script (server or client).
param session: session to execute commands on the target machine.
port: the port number to monitor.
script_to_run: the path to the Python script to execute.
"""

check_live_python = params.get("check_live_python")
dest_location = params.get("dest_location")
c_pip_copy_cmd = params.get("c_pip_copy_cmd")
c_pip_cmd = params.get("c_pip_cmd")
c_py_copy_cmd = params.get("c_py_copy_cmd")
s_py_copy_cmd = params.get("s_py_copy_cmd")
status, output = session.cmd_status_output(check_live_python, timeout=1200)
if status == 0:
return
session.cmd(dest_location)
if "server" in script_to_run:
error_context.context(
"Run python3 code runs on the server node", test.log.info
)
s_py_copy_cmd = utils_misc.set_winutils_letter(session, s_py_copy_cmd)
session.cmd(s_py_copy_cmd)
status, output = session.cmd_status_output(s_py_cmd, timeout=1200)
if status != 0:
test.fail("The server node failed to start.")
else:
error_context.context(
"Run python3 code runs on the client node", test.log.info
)
c_pip_copy_cmd = utils_misc.set_winutils_letter(session, c_pip_copy_cmd)
session.cmd(c_pip_copy_cmd)
session.cmd(c_pip_cmd)
c_py_copy_cmd = utils_misc.set_winutils_letter(session, c_py_copy_cmd)
session.cmd(c_py_copy_cmd)
status, output = session.cmd_status_output(c_py_cmd % s_vm_ip, timeout=1200)
if status != 0:
test.fail(
"The client could not connect to the server node.", test.log.info
)

timeout = params.get_numeric("login_timeout", 360)
port_num = params.get("port_num")
s_py_cmd = params.get("s_py_cmd")
c_py_cmd = params.get("c_py_cmd")
param_name = params.get("param_name")
param_values = params.get("param_values")

s_vm_name = params["vms"].split()[0]
s_vm = env.get_vm(s_vm_name)
s_vm.verify_alive()
s_session = s_vm.wait_for_serial_login(
timeout=int(params.get("login_timeout", 360))
)
s_vm_ip = s_vm.get_address()

c_vm_name = params["vms"].split(s_vm_name)[1].strip()
c_vm_params = params.object_params(c_vm_name)
c_vm_params["nic_extra_params_nic1"] = ""
c_vm_params["start_vm"] = "yes"
env_process.preprocess_vm(test, c_vm_params, env, c_vm_name)
c_vm = env.get_vm(c_vm_name)
c_vm.verify_alive()
c_session = c_vm.wait_for_serial_login(
timeout=int(params.get("login_timeout", 360))
)

ping_results = []
error_context.context(
"Open the NIC properties and change the values in the server node",
test.log.info,
)
for value in param_values.split(" "):
modify_and_analyze_params_result(vm=s_vm, netkvmco_name=param_name, value=value)
check_and_restart_port(session=s_session, port=port_num, script_to_run=s_py_cmd)
check_and_restart_port(session=c_session, port=port_num, script_to_run=c_py_cmd)
ping_results.append(
int(analyze_ping_results(session=c_session, count=100, timeout=timeout))
)

error_context.context(
"Get the packet loss percentage of the ping request", test.log.info
)
if sum(ping_results) != 0:
if not all(
ping_results[i] > ping_results[i + 1] for i in range(len(ping_results) - 1)
):
test.fail(
"With the parameter, the number of lost packets should be "
"less than without the parameter."
)

error_context.context("no BSOD will occur on the client side", test.log.info)
for value in param_values.split(" "):
modify_and_analyze_params_result(vm=c_vm, netkvmco_name=param_name, value=value)

0 comments on commit b2fea54

Please sign in to comment.