From patchwork Tue Apr 2 18:49:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pauli Virtanen X-Patchwork-Id: 13614473 Received: from lahtoruutu.iki.fi (lahtoruutu.iki.fi [185.185.170.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F91E15B14D for ; Tue, 2 Apr 2024 18:49:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=185.185.170.37 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712083791; cv=pass; b=q5enV6X9txZjppux9WfC50HZf8IUi2xkKY1kGu8SOP8k5q9zaiomWl+W1j/XNS1nq4ZhjPbPbFT+QSWdT8jhtqZRkKl/VTKabJu7azUlKktYMBYkbI2r+ZdgwwPtLZfDTT0dYjgy7RQrxyw5vM/3jC4kmrF3fdhn7tR9zQoocDg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712083791; c=relaxed/simple; bh=t8mZoABsYat898l5IvKQOi2rSOhkqftcalWGPvIniRM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KYryGlrTnXEK3y83Wih7udHVy/xsOjYUA+aE0yAmNFfm4V+GQnfioTD9J1scaXghc32yntbpEY+UnqOpGxS3ilKpIcFCVBXpuf8kwolmbgr0PrQmNG0FuLcRty5rP7IuXLzQ9z2POJMvqmoqrLdcU7D+MqT+uIVycro6HrjK9WQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi; spf=pass smtp.mailfrom=iki.fi; dkim=pass (2048-bit key) header.d=iki.fi header.i=@iki.fi header.b=krLjvsXI; arc=pass smtp.client-ip=185.185.170.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iki.fi Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=iki.fi header.i=@iki.fi header.b="krLjvsXI" Received: from monolith.lan (unknown [193.138.7.178]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: pav) by lahtoruutu.iki.fi (Postfix) with ESMTPSA id 4V8H3f5tFsz49Pxf; Tue, 2 Apr 2024 21:49:18 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=lahtoruutu; t=1712083759; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Jam6wyhJEGdcq9xW0ZNDbg/U6xHYkFB5s+KfC6a8eCo=; b=krLjvsXIHDd0jdih9z3r8MyUcmLJt7IMUBVcTIG4OmxyspXPtLJAIlsMj87X+NSOyGfnJZ Ty1l6mkAC46uex8jS7k93sLAZtpl9VC9oA/6MICOMT3jmkHFi6E9XvY8Aqjn0KGTt5HQ1b bssTU+xZM5Of2GkgaCP0VMWDEjn8kU00MJiV+yL1AvmA0hPUFoQ392IhIQcZGLFPEH2Xrn LwoePekHmXrxAX52c0jq55P/uidnd4PfRMFZ495Pn2txl5bS46Jp7E3AcfwC5kLs5ufzMy ra1ToEooKRyX3b84qqp4caGHEBFTo8fZTgEpQvW8FbFSMpCCyWELjgmM1YzUJA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=lahtoruutu; t=1712083759; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Jam6wyhJEGdcq9xW0ZNDbg/U6xHYkFB5s+KfC6a8eCo=; b=uyrKrppnibkaFXBRO5bkZ3GlVoG6rnf7P03AavLhYdOobCtzWNhh3YcIKutSj+JOYk1q51 6OsqGh2kF5v1K+sp0rsvO7ECAHL4vTJJzcPsVGbPaeqYO+pmhvz76LXICDUtMLmTm1lAx4 CNcd3eBj5mObL400AJP8L88bqlD37zgmwySl0lzOih5SmYawNYyMWj6qcwg+vyfVs7/7HV SDNNLgFF8ahhAOjSX1gA7P/WMp2xiaKXRzAHsVxbqBQeRtVMIemGVLNUZKnz8dcngRTd3E 6v7LpNAIafaNlrMj0b2SSbKUab1uxnd9mT8UQL9NtOJgb6ofF71Wmwj+8wZvJw== ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=pav smtp.mailfrom=pav@iki.fi ARC-Seal: i=1; s=lahtoruutu; d=iki.fi; t=1712083759; a=rsa-sha256; cv=none; b=Y6PwBRnb+AcYFCoL270yvd1iTEEe9qH9z8tyJZX4OpQh4vMyi7gKW9xaqt5He7fHxJGl0i zHnTUIMK2dLYm2C6ta7vxIPUp5cl6BeGcGgwPIDsK5K89edRT1QRS8KEbNxcKWvohqqhI2 AV04pRuz/o9trEIjK73Dinub8AkufyBBDLRZo52KLKe92fYLglTXuN+SxwvdfsiPHKgxhh WPgjVwXUUfAkOUDSe712VKBX0zeJt+6UtY8nLCFZZkYb/SrmFtuX0VvvatAjMt238UxA8i RM1uLjUB81pNdKHj14mwXBJhubydhLD8M23Tj3hJGpaRhBxTKub+FYH43l+qdw== From: Pauli Virtanen To: linux-bluetooth@vger.kernel.org Cc: Pauli Virtanen Subject: [PATCH BlueZ v4 2/7] iso-tester: Add tests for TX timestamping Date: Tue, 2 Apr 2024 21:49:09 +0300 Message-ID: <2f052962cfe91d05f722cb686deae5216f6e89f5.1712083655.git.pav@iki.fi> X-Mailer: git-send-email 2.44.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 #include +#include +#include + #include #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; }; 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); + 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 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 +#include +#include +#include +#include +#include +#include + +#include + +#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; +}