Re: [PATCH BlueZ v3 2/8] iso-tester: Add tests for TX timestamping

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Pauli,

On Tue, Apr 2, 2024 at 12:43 PM Pauli Virtanen <pav@xxxxxx> wrote:
>
> Add TX timestamping test utilities in new tester-utils.h, so that they
> can be shared between testers.
>
> Add tests:
>
> ISO Send - TX Timestamping
> ISO Send - TX Sched Timestamping
> ISO Send - TX Msg Timestamping
> ---
>  tools/iso-tester.c   | 169 ++++++++++++++++++++++++++++++++++++++++---
>  tools/tester-utils.h | 163 +++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 322 insertions(+), 10 deletions(-)
>  create mode 100644 tools/tester-utils.h
>
> diff --git a/tools/iso-tester.c b/tools/iso-tester.c
> index 60afef301..c12675a18 100644
> --- a/tools/iso-tester.c
> +++ b/tools/iso-tester.c
> @@ -18,6 +18,9 @@
>  #include <poll.h>
>  #include <stdbool.h>
>
> +#include <linux/errqueue.h>
> +#include <linux/net_tstamp.h>
> +
>  #include <glib.h>
>
>  #include "lib/bluetooth.h"
> @@ -34,6 +37,8 @@
>  #include "src/shared/util.h"
>  #include "src/shared/queue.h"
>
> +#include "tester-utils.h"
> +
>  #define QOS_IO(_interval, _latency, _sdu, _phy, _rtn) \
>  { \
>         .interval = _interval, \
> @@ -462,11 +467,12 @@ struct test_data {
>         uint16_t handle;
>         uint16_t acl_handle;
>         struct queue *io_queue;
> -       unsigned int io_id[2];
> +       unsigned int io_id[3];
>         uint8_t client_num;
>         int step;
>         bool reconnect;
>         bool suspending;
> +       struct tx_tstamp_data tx_ts;
>  };
>
>  struct iso_client_data {
> @@ -487,6 +493,10 @@ struct iso_client_data {
>         size_t base_len;
>         bool listen_bind;
>         bool pa_bind;
> +       uint32_t so_timestamping;
> +       bool msg_timestamping;
> +       unsigned int send_extra;
> +       unsigned int send_extra_pre_ts;

It is probably a good idea to start documenting what each of these
fields are meant to do, I have no idea what are the last 2 for btw.

>  };
>
>  typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io);
> @@ -677,15 +687,14 @@ static void io_free(void *data)
>  static void test_data_free(void *test_data)
>  {
>         struct test_data *data = test_data;
> +       unsigned int i;
>
>         if (data->io_queue)
>                 queue_destroy(data->io_queue, io_free);
>
> -       if (data->io_id[0] > 0)
> -               g_source_remove(data->io_id[0]);
> -
> -       if (data->io_id[1] > 0)
> -               g_source_remove(data->io_id[1]);
> +       for (i = 0; i < ARRAY_SIZE(data->io_id); ++i)
> +               if (data->io_id[i] > 0)
> +                       g_source_remove(data->io_id[i]);
>
>         free(data);
>  }
> @@ -987,6 +996,38 @@ static const struct iso_client_data connect_16_2_1_send = {
>         .send = &send_16_2_1,
>  };
>
> +static const struct iso_client_data connect_send_tx_timestamping = {
> +       .qos = QOS_16_2_1,
> +       .expect_err = 0,
> +       .send = &send_16_2_1,
> +       .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE |
> +                                       SOF_TIMESTAMPING_OPT_ID |
> +                                       SOF_TIMESTAMPING_TX_SOFTWARE),
> +       .send_extra = 1,
> +       .send_extra_pre_ts = 2,
> +};
> +
> +static const struct iso_client_data connect_send_tx_sched_timestamping = {
> +       .qos = QOS_16_2_1,
> +       .expect_err = 0,
> +       .send = &send_16_2_1,
> +       .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE |
> +                                       SOF_TIMESTAMPING_TX_SOFTWARE |
> +                                       SOF_TIMESTAMPING_OPT_TSONLY |
> +                                       SOF_TIMESTAMPING_TX_SCHED),
> +       .send_extra = 1,
> +};
> +
> +static const struct iso_client_data connect_send_tx_msg_timestamping = {
> +       .qos = QOS_16_2_1,
> +       .expect_err = 0,
> +       .send = &send_16_2_1,
> +       .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE |
> +                                       SOF_TIMESTAMPING_TX_SOFTWARE),
> +       .send_extra = 1,
> +       .msg_timestamping = true,
> +};
> +
>  static const struct iso_client_data listen_16_2_1_recv = {
>         .qos = QOS_16_2_1,
>         .expect_err = 0,
> @@ -1410,14 +1451,17 @@ static void bthost_recv_data(const void *buf, uint16_t len, void *user_data)
>         struct test_data *data = user_data;
>         const struct iso_client_data *isodata = data->test_data;
>
> +       --data->step;
> +
>         tester_print("Client received %u bytes of data", len);
>
>         if (isodata->send && (isodata->send->iov_len != len ||
>                         memcmp(isodata->send->iov_base, buf, len))) {
>                 if (!isodata->recv->iov_base)
>                         tester_test_failed();
> -       } else
> +       } else if (!data->step) {
>                 tester_test_passed();
> +       }
>  }
>
>  static void bthost_iso_disconnected(void *user_data)
> @@ -2058,17 +2102,95 @@ static void iso_recv(struct test_data *data, GIOChannel *io)
>         data->io_id[0] = g_io_add_watch(io, G_IO_IN, iso_recv_data, data);
>  }
>
> -static void iso_send(struct test_data *data, GIOChannel *io)
> +static gboolean iso_recv_errqueue(GIOChannel *io, GIOCondition cond,
> +                                                       gpointer user_data)
>  {
> +       struct test_data *data = user_data;
>         const struct iso_client_data *isodata = data->test_data;
> -       ssize_t ret;
> +       int sk = g_io_channel_unix_get_fd(io);
> +       int err;
> +
> +       data->step--;
> +
> +       err = tx_tstamp_recv(&data->tx_ts, sk, isodata->send->iov_len);
> +       if (err > 0)
> +               return TRUE;
> +       else if (!err && !data->step)
> +               tester_test_passed();
> +       else
> +               tester_test_failed();
> +
> +       data->io_id[2] = 0;
> +       return FALSE;
> +}
> +
> +static void iso_tx_timestamping(struct test_data *data, GIOChannel *io)
> +{
> +       const struct iso_client_data *isodata = data->test_data;
> +       struct so_timestamping so = {
> +               .flags = isodata->so_timestamping,
> +       };
>         int sk;
> +       int err;
> +       unsigned int count;
> +
> +       if (!(isodata->so_timestamping & SOF_TIMESTAMPING_TX_RECORD_MASK))
> +               return;
> +
> +       tester_print("Enabling TX timestamping");
> +
> +       tx_tstamp_init(&data->tx_ts, isodata->so_timestamping);
> +
> +       for (count = 0; count < isodata->send_extra + 1; ++count)
> +               data->step += tx_tstamp_expect(&data->tx_ts);
>
>         sk = g_io_channel_unix_get_fd(io);
>
> +       data->io_id[2] = g_io_add_watch(io, G_IO_ERR, iso_recv_errqueue, data);
> +
> +       if (isodata->msg_timestamping)
> +               so.flags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK;
> +
> +       err = setsockopt(sk, SOL_SOCKET, SO_TIMESTAMPING, &so, sizeof(so));
> +       if (err < 0) {
> +               tester_warn("setsockopt SO_TIMESTAMPING: %s (%d)",
> +                                               strerror(errno), errno);
> +               tester_test_failed();
> +               return;
> +       }
> +}
> +
> +static void iso_send_data(struct test_data *data, GIOChannel *io)
> +{
> +       const struct iso_client_data *isodata = data->test_data;
> +       char control[CMSG_SPACE(sizeof(uint32_t))];
> +       struct msghdr msg = {
> +               .msg_iov = (struct iovec *)isodata->send,
> +               .msg_iovlen = 1,
> +       };
> +       struct cmsghdr *cmsg;
> +       ssize_t ret;
> +       int sk;
> +
>         tester_print("Writing %zu bytes of data", isodata->send->iov_len);
>
> -       ret = writev(sk, isodata->send, 1);
> +       sk = g_io_channel_unix_get_fd(io);
> +
> +       if (isodata->msg_timestamping) {
> +               memset(control, 0, sizeof(control));
> +               msg.msg_control = control;
> +               msg.msg_controllen = sizeof(control);
> +
> +               cmsg = CMSG_FIRSTHDR(&msg);
> +               cmsg->cmsg_level = SOL_SOCKET;
> +               cmsg->cmsg_type = SO_TIMESTAMPING;
> +               cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
> +
> +               *((uint32_t *)CMSG_DATA(cmsg)) = (isodata->so_timestamping &
> +                                       SOF_TIMESTAMPING_TX_RECORD_MASK);
> +       }
> +
> +       ret = sendmsg(sk, &msg, 0);
>         if (ret < 0 || isodata->send->iov_len != (size_t) ret) {
>                 tester_warn("Failed to write %zu bytes: %s (%d)",
>                                 isodata->send->iov_len, strerror(errno), errno);
> @@ -2076,6 +2198,22 @@ static void iso_send(struct test_data *data, GIOChannel *io)
>                 return;
>         }
>
> +       data->step++;
> +}
> +
> +static void iso_send(struct test_data *data, GIOChannel *io)
> +{
> +       const struct iso_client_data *isodata = data->test_data;
> +       unsigned int count;
> +
> +       for (count = 0; count < isodata->send_extra_pre_ts; ++count)
> +               iso_send_data(data, io);
> +
> +       iso_tx_timestamping(data, io);
> +
> +       for (count = 0; count < isodata->send_extra + 1; ++count)
> +               iso_send_data(data, io);
> +
>         if (isodata->bcast) {
>                 tester_test_passed();
>                 return;
> @@ -3197,6 +3335,17 @@ int main(int argc, char *argv[])
>         test_iso("ISO Send - Success", &connect_16_2_1_send, setup_powered,
>                                                         test_connect);
>
> +       test_iso("ISO Send - TX Timestamping", &connect_send_tx_timestamping,
> +                                               setup_powered, test_connect);
> +
> +       test_iso("ISO Send - TX Sched Timestamping",
> +                       &connect_send_tx_sched_timestamping, setup_powered,
> +                       test_connect);
> +
> +       test_iso("ISO Send - TX Msg Timestamping",
> +                       &connect_send_tx_msg_timestamping, setup_powered,
> +                       test_connect);

Add a comment for each test to describe what they are doing, are these
supposed to test different flags that can be passed on to
SO_TXTIMESTAMP?

>         test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered,
>                                                         test_listen);
>
> diff --git a/tools/tester-utils.h b/tools/tester-utils.h

Perhaps just have as tools/tester.h

> new file mode 100644
> index 000000000..617de842e
> --- /dev/null
> +++ b/tools/tester-utils.h
> @@ -0,0 +1,163 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2022  Intel Corporation.
> + *
> + */
> +
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <time.h>
> +#include <sys/socket.h>
> +#include <linux/errqueue.h>
> +#include <linux/net_tstamp.h>
> +
> +#include <glib.h>
> +
> +#define SEC_NSEC(_t)  ((_t) * 1000000000LL)
> +#define TS_NSEC(_ts)  (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec)
> +
> +struct tx_tstamp_data {
> +       struct {
> +               uint32_t id;
> +               uint32_t type;
> +       } expect[16];
> +       unsigned int pos;
> +       unsigned int count;
> +       unsigned int sent;
> +       uint32_t so_timestamping;
> +};
> +
> +static inline void tx_tstamp_init(struct tx_tstamp_data *data,
> +                               uint32_t so_timestamping)
> +{
> +       memset(data, 0, sizeof(*data));
> +       memset(data->expect, 0xff, sizeof(data->expect));
> +
> +       data->so_timestamping = so_timestamping;
> +}
> +
> +static inline int tx_tstamp_expect(struct tx_tstamp_data *data)
> +{
> +       unsigned int pos = data->count;
> +       int steps;
> +
> +       if (data->so_timestamping & SOF_TIMESTAMPING_TX_SCHED) {
> +               g_assert(pos < ARRAY_SIZE(data->expect));
> +               data->expect[pos].type = SCM_TSTAMP_SCHED;
> +               data->expect[pos].id = data->sent;
> +               pos++;
> +       }
> +
> +       if (data->so_timestamping & SOF_TIMESTAMPING_TX_SOFTWARE) {
> +               g_assert(pos < ARRAY_SIZE(data->expect));
> +               data->expect[pos].type = SCM_TSTAMP_SND;
> +               data->expect[pos].id = data->sent;
> +               pos++;
> +       }
> +
> +       data->sent++;
> +
> +       steps = pos - data->count;
> +       data->count = pos;
> +       return steps;
> +}
> +
> +static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len)
> +{
> +       unsigned char control[512];
> +       ssize_t ret;
> +       char buf[1024];
> +       struct msghdr msg;
> +       struct iovec iov;
> +       struct cmsghdr *cmsg;
> +       struct scm_timestamping *tss = NULL;
> +       struct sock_extended_err *serr = NULL;
> +       struct timespec now;
> +
> +       iov.iov_base = buf;
> +       iov.iov_len = sizeof(buf);
> +
> +       memset(&msg, 0, sizeof(msg));
> +       msg.msg_iov = &iov;
> +       msg.msg_iovlen = 1;
> +       msg.msg_control = control;
> +       msg.msg_controllen = sizeof(control);
> +
> +       ret = recvmsg(sk, &msg, MSG_ERRQUEUE);
> +       if (ret < 0) {
> +               tester_warn("Failed to read from errqueue: %s (%d)",
> +                                                       strerror(errno), errno);
> +               return -EINVAL;
> +       }
> +
> +       if (data->so_timestamping & SOF_TIMESTAMPING_OPT_TSONLY) {
> +               if (ret != 0) {
> +                       tester_warn("Packet copied back to errqueue");
> +                       return -EINVAL;
> +               }
> +       } else if (len > ret) {
> +               tester_warn("Packet not copied back to errqueue: %zd", ret);
> +               return -EINVAL;
> +       }
> +
> +       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
> +                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> +               if (cmsg->cmsg_level == SOL_SOCKET &&
> +                                       cmsg->cmsg_type == SCM_TIMESTAMPING) {
> +                       tss = (void *)CMSG_DATA(cmsg);
> +               } else if (cmsg->cmsg_level == SOL_BLUETOOTH &&
> +                                       cmsg->cmsg_type == BT_SCM_ERROR) {
> +                       serr = (void *)CMSG_DATA(cmsg);
> +               }
> +       }
> +
> +       if (!tss) {
> +               tester_warn("SCM_TIMESTAMPING not found");
> +               return -EINVAL;
> +       }
> +
> +       if (!serr) {
> +               tester_warn("BT_SCM_ERROR not found");
> +               return -EINVAL;
> +       }
> +
> +       if (serr->ee_errno != ENOMSG ||
> +                               serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
> +               tester_warn("BT_SCM_ERROR wrong for timestamping");
> +               return -EINVAL;
> +       }
> +
> +       clock_gettime(CLOCK_REALTIME, &now);
> +
> +       if (TS_NSEC(&now) < TS_NSEC(tss->ts) ||
> +                       TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) {
> +               tester_warn("nonsense in timestamp");
> +               return -EINVAL;
> +       }
> +
> +       if (data->pos >= data->count) {
> +               tester_warn("Too many timestamps");
> +               return -EINVAL;
> +       }
> +
> +       if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) &&
> +                               serr->ee_data != data->expect[data->pos].id) {
> +               tester_warn("Bad timestamp id %u", serr->ee_data);
> +               return -EINVAL;
> +       }
> +
> +       if (serr->ee_info != data->expect[data->pos].type) {
> +               tester_warn("Bad timestamp type %u", serr->ee_info);
> +               return -EINVAL;
> +       }
> +
> +       tester_print("Got valid TX timestamp %u", data->pos);
> +
> +       ++data->pos;
> +
> +       return data->count - data->pos;
> +}
> --
> 2.44.0
>
>


-- 
Luiz Augusto von Dentz





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux