mirror of
https://github.com/qemu/qemu.git
synced 2026-02-04 05:35:39 +00:00
Add test_change_interval_timer to verify that modifying the 'interval' property of filter-buffer at runtime takes effect immediately. The test uses socket backend and filter-redirector to verify timer behavior: - Creates filter-buffer with a very long interval (1000 seconds) - Sends a packet which gets buffered - Advances virtual clock by 1 second, verifies packet is still buffered - Changes interval to 1ms via qom-set (timer should be rescheduled) - Advances virtual clock by 2ms, verifies packet is now released - This proves the timer was rescheduled immediately when interval changed The test uses filter-redirector to observe when packets are released by filter-buffer, providing end-to-end verification of the timer rescheduling behavior. Reviewed-by: Zhang Chen <zhangckid@gmail.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Jason Wang <jasowang@redhat.com>
170 lines
5.7 KiB
C
170 lines
5.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* QTest testcase for filter-buffer
|
|
*
|
|
* Copyright (c) 2025 Red Hat, Inc.
|
|
* Author: Jason Wang <jasowang@redhat.com>
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "libqtest.h"
|
|
#include "qobject/qdict.h"
|
|
#include "qemu/iov.h"
|
|
#include "qemu/sockets.h"
|
|
|
|
/*
|
|
* Test that changing interval at runtime affects packet release timing.
|
|
*
|
|
* Traffic flow with filter-buffer and filter-redirector:
|
|
*
|
|
* test side | qemu side
|
|
* |
|
|
* +--------+ | +---------+
|
|
* | send +------------------------>| backend |
|
|
* | sock[0]| | +----+----+
|
|
* +--------+ | |
|
|
* | +----v----+
|
|
* | | fbuf0 | filter-buffer (queue=tx)
|
|
* | +----+----+
|
|
* | |
|
|
* | +----v----+ +----------+
|
|
* | | rd0 +->| chardev0 |
|
|
* | +---------+ +----+-----+
|
|
* | |
|
|
* +--------+ | |
|
|
* | recv |<--------------------------------------+
|
|
* | sock | |
|
|
* +--------+ |
|
|
*
|
|
* The test verifies that when interval is changed via qom-set, the timer
|
|
* is rescheduled immediately, causing buffered packets to be released
|
|
* at the new interval rather than waiting for the old interval to elapse.
|
|
*/
|
|
static void test_change_interval_timer(void)
|
|
{
|
|
QTestState *qts;
|
|
QDict *response;
|
|
int backend_sock[2], recv_sock;
|
|
int ret;
|
|
char send_buf[] = "Hello filter-buffer!";
|
|
char recv_buf[128];
|
|
char sock_path[] = "filter-buffer-test.XXXXXX";
|
|
uint32_t size = sizeof(send_buf);
|
|
uint32_t len;
|
|
|
|
size = htonl(size);
|
|
|
|
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock);
|
|
g_assert_cmpint(ret, !=, -1);
|
|
|
|
ret = mkstemp(sock_path);
|
|
g_assert_cmpint(ret, !=, -1);
|
|
|
|
/*
|
|
* Start QEMU with:
|
|
* - socket backend connected to our socketpair
|
|
* - filter-buffer with a very long interval (1000 seconds)
|
|
* - filter-redirector to send released packets to a chardev socket
|
|
*
|
|
* queue=tx intercepts packets going from backend to the guest,
|
|
* i.e., data we send from the test side.
|
|
*/
|
|
qts = qtest_initf(
|
|
"-nic socket,id=qtest-bn0,fd=%d "
|
|
"-chardev socket,id=chardev0,path=%s,server=on,wait=off "
|
|
"-object filter-buffer,id=fbuf0,netdev=qtest-bn0,"
|
|
"queue=tx,interval=1000000000 "
|
|
"-object filter-redirector,id=rd0,netdev=qtest-bn0,"
|
|
"queue=tx,outdev=chardev0",
|
|
backend_sock[1], sock_path);
|
|
|
|
/* Connect to the chardev socket to receive redirected packets */
|
|
recv_sock = unix_connect(sock_path, NULL);
|
|
g_assert_cmpint(recv_sock, !=, -1);
|
|
|
|
/* Send a QMP command to ensure chardev connection is established */
|
|
qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}");
|
|
|
|
/*
|
|
* Send a packet from the test side.
|
|
* It should be buffered by filter-buffer.
|
|
*/
|
|
struct iovec iov[] = {
|
|
{
|
|
.iov_base = &size,
|
|
.iov_len = sizeof(size),
|
|
}, {
|
|
.iov_base = send_buf,
|
|
.iov_len = sizeof(send_buf),
|
|
},
|
|
};
|
|
|
|
ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf));
|
|
g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
|
|
|
|
/*
|
|
* Advance virtual clock by 1 second (1,000,000,000 ns).
|
|
* This is much less than the 1000 second interval, so the packet
|
|
* should still be buffered.
|
|
*/
|
|
qtest_clock_step(qts, 1000000000LL);
|
|
|
|
/* Try to receive with non-blocking - should fail (packet still buffered) */
|
|
ret = recv(recv_sock, recv_buf, sizeof(recv_buf), MSG_DONTWAIT);
|
|
g_assert_cmpint(ret, ==, -1);
|
|
g_assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
|
|
|
/*
|
|
* Now change the interval to 1000 us (1ms) via qom-set.
|
|
* This should reschedule the timer to fire in 1ms from now.
|
|
*/
|
|
response = qtest_qmp(qts,
|
|
"{'execute': 'qom-set',"
|
|
" 'arguments': {"
|
|
" 'path': 'fbuf0',"
|
|
" 'property': 'interval',"
|
|
" 'value': 1000"
|
|
"}}");
|
|
g_assert(response);
|
|
g_assert(!qdict_haskey(response, "error"));
|
|
qobject_unref(response);
|
|
|
|
/*
|
|
* Advance virtual clock by 2ms (2,000,000 ns).
|
|
* This exceeds the new 1ms interval, so the timer should fire
|
|
* and release the buffered packet.
|
|
*
|
|
* If the interval change didn't take effect immediately, we would
|
|
* still be waiting for the original 1000 second interval to elapse,
|
|
* and the packet would not be released.
|
|
*/
|
|
qtest_clock_step(qts, 2000000LL);
|
|
|
|
/*
|
|
* Now we should be able to receive the packet through the redirector.
|
|
* The packet was released by filter-buffer and sent to filter-redirector,
|
|
* which forwarded it to the chardev socket.
|
|
*/
|
|
ret = recv(recv_sock, &len, sizeof(len), 0);
|
|
g_assert_cmpint(ret, ==, sizeof(len));
|
|
len = ntohl(len);
|
|
g_assert_cmpint(len, ==, sizeof(send_buf));
|
|
|
|
ret = recv(recv_sock, recv_buf, len, 0);
|
|
g_assert_cmpint(ret, ==, len);
|
|
g_assert_cmpstr(recv_buf, ==, send_buf);
|
|
|
|
close(recv_sock);
|
|
close(backend_sock[0]);
|
|
unlink(sock_path);
|
|
qtest_quit(qts);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
qtest_add_func("/netfilter/change_interval_timer",
|
|
test_change_interval_timer);
|
|
return g_test_run();
|
|
}
|