On Fri, Dec 26, 2025 at 11:15 AM Zhang Chen <[email protected]> wrote: > > On Fri, Dec 26, 2025 at 9:37 AM Jason Wang <[email protected]> wrote: > > > > 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. > > This case same like this test, before change the user target interval time, > filter-buffer maybe already buffered lots of packets, for this parts, the user > external measured time did not meet the expected settings.
There's indeed a change of the behaviour, but I'm not sure if there's a user that depends on the previous behaviour. Or if we really care, we need a new attribute. > > > > > > > > > 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? > > Yes, this is our expectation, but in actual operation, there may be > many interferences. > For example user setup filter-buffer interval to 100us, even on the > same physical machine > host to guest, external client may get test result as 110us(like ping). Yes, but this topic is somehow out of the scope of this patch. It require the support of the timer and probably some realtime support in the qemu timer. > > > > > > > Maybe need to tell user recommended test range? > > > > Could you elaborate more on this? > > Based on the above statement, is it necessary to provide an empirical value > for the time range, such as 10us(like 100us --> 110us)? It's better not since it could easily run out of sync with the behaviour of the code. Thanks > > Thanks > Chen > > > > > > 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 > > > > > > > > > >
