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
> >
>


Reply via email to