On Thu, Dec 25, 2025 at 6:27 PM Zhang Chen <[email protected]> wrote: > > On Thu, Dec 25, 2025 at 3:24 PM Jason Wang <[email protected]> wrote: > > > > 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. > > If user try to simulate network latency by filter-buffer, the accuracy > of time is important. > Do we need add some note about the first buffered packet time not > equel to dynamic > changed time (default interval time - new qmp cmd effected time + > changed time ?).
I'm not sure I will get here, we can't forcast when the first packet will come. So the behaviour is always that the filter-buffer will flush at a fixed interval. Or I may miss something here. > > Another issue is what level of stable accuracy the QEMU filter-buffer > can be achieved in actual testing? We use qemu_clock_get_us() so it's the us actually? > Maybe need to tell user recommended test range? Could you elaborate more on this? Thanks > > Thanks > Chen > > > > > Signed-off-by: Jason Wang <[email protected]> > > --- > > tests/qtest/meson.build | 1 + > > tests/qtest/test-filter-buffer.c | 169 +++++++++++++++++++++++++++++++ > > tests/qtest/test-netfilter.c | 3 + > > 3 files changed, 173 insertions(+) > > create mode 100644 tests/qtest/test-filter-buffer.c > > > > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > > index 669d07c06b..ffa85ba984 100644 > > --- a/tests/qtest/meson.build > > +++ b/tests/qtest/meson.build > > @@ -46,6 +46,7 @@ qtests_cxl = \ > > # for the availability of the default NICs in the tests > > qtests_filter = \ > > (get_option('default_devices') and slirp.found() ? ['test-netfilter'] : > > []) + \ > > + (get_option('default_devices') and host_os != 'windows' ? > > ['test-filter-buffer'] : []) + \ > > (get_option('default_devices') and host_os != 'windows' ? > > ['test-filter-mirror'] : []) + \ > > (get_option('default_devices') and host_os != 'windows' ? > > ['test-filter-redirector'] : []) > > > > diff --git a/tests/qtest/test-filter-buffer.c > > b/tests/qtest/test-filter-buffer.c > > new file mode 100644 > > index 0000000000..441cbb975c > > --- /dev/null > > +++ b/tests/qtest/test-filter-buffer.c > > @@ -0,0 +1,169 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * QTest testcase for filter-buffer > > + * > > + * Copyright (c) 2025 Red Hat, Inc. > > + * Author: Jason Wang <[email protected]> > > + */ > > + > > +#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(); > > +} > > diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c > > index 326d4bd85f..b7271055d6 100644 > > --- a/tests/qtest/test-netfilter.c > > +++ b/tests/qtest/test-netfilter.c > > @@ -10,7 +10,10 @@ > > > > #include "qemu/osdep.h" > > #include "libqtest-single.h" > > +#include "libqtest.h" > > #include "qobject/qdict.h" > > +#include "qemu/iov.h" > > +#include "qemu/sockets.h" > > > > /* add a netfilter to a netdev and then remove it */ > > static void add_one_netfilter(void) > > -- > > 2.34.1 > > >
