diff mbox series

[RFC,2/3] Bluetooth: ISO: add new ioctl() for reading tx latency

Message ID 8e5df7837d596cbedcfa04d766ce1caba50d853b.1709150574.git.pav@iki.fi
State New
Headers show
Series Bluetooth: add transmission latency tracking for ISO & L2CAP | expand

Commit Message

Pauli Virtanen Feb. 28, 2024, 8:03 p.m. UTC
Add ioctl BTGETTXINFO to read information of a previous packet
completion event concerning a given ISO socket.

It contains sufficient information so the user can know which previous
sendmsg() the packet corresponds to and what was its completion time in
monotonic clock, so latency can be calculated.  The user can then also
know the packet queue length (in units of their sendmsg packets).

Mark submitted skb so that hci_core updates the latency information, and
read it in the ioctl.

Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
 include/net/bluetooth/bluetooth.h | 37 ++++++++++++++++++++
 net/bluetooth/iso.c               | 58 ++++++++++++++++++++++++++++---
 2 files changed, 90 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index f6bdd040adaa..ff4230d15461 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -239,6 +239,43 @@  struct bt_codecs {
 
 #define BT_ISO_BASE		20
 
+/* BTGETTXINFO: Transmission latency information.
+ *
+ * Produces information of a previous event when a packet was sent, and the
+ * length of packet queue at that time.
+ *
+ * Applicable to: Bluetooth ISO sockets.
+ *
+ * Input: Zero-initialize reserved flag bits and other fields.
+ *
+ * Output:
+ *
+ * Fails with ENOENT if no packet has been sent.
+ *
+ * - flags: currently always 0, all bits reserved for extensions.
+ *
+ * - queue: total number of packets in queue (controller and socket buffers)
+ *   at the time of the event.
+ *
+ * - time: reference event timestamp (nsec, in system monotonic clock).
+ *
+ * - offset: offset (nsec) of actual packet timing from the reference timestamp.
+ *   Currently always 0.
+ *
+ * For packet latencies in nanoseconds, the application can track timestamps
+ * t[j] when it sent packet j. Then, given t[k] < tx_info.time < t[k + 1],
+ * event packet j = k - tx.queue and ref_latency_nsec = tx_info.time - t[j].
+ */
+
+#define BTGETTXINFO	_IOWR('B', 100, struct bt_tx_info)
+
+struct bt_tx_info {
+	__u32	flags;
+	__u32	queue;
+	__s64	time;
+	__s64	offset;
+} __packed;
+
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 30c777c469f9..4953c987e4fd 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -500,6 +500,8 @@  static int iso_send_frame(struct sock *sk, struct sk_buff *skb)
 
 	if (skb->len > qos->ucast.out.sdu)
 		return -EMSGSIZE;
+	if (sk->sk_state != BT_CONNECTED)
+		return -ENOTCONN;
 
 	len = skb->len;
 
@@ -509,10 +511,9 @@  static int iso_send_frame(struct sock *sk, struct sk_buff *skb)
 	hdr->slen = cpu_to_le16(hci_iso_data_len_pack(len,
 						      HCI_ISO_STATUS_VALID));
 
-	if (sk->sk_state == BT_CONNECTED)
-		hci_send_iso(conn->hcon, skb);
-	else
-		len = -ENOTCONN;
+	hci_mark_tx_latency(&conn->hcon->tx_latency, skb);
+
+	hci_send_iso(conn->hcon, skb);
 
 	return len;
 }
@@ -2232,6 +2233,53 @@  void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
 	kfree_skb(skb);
 }
 
+static int iso_sock_ioctl(struct socket *sock, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	struct tx_latency latency;
+	struct bt_tx_info info;
+
+	BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
+
+	switch (cmd) {
+	case BTGETTXINFO:
+		/* Require zero-initialized, to allow later extensions */
+		if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+			return -EFAULT;
+		if (info.flags || info.queue || info.time || info.offset)
+			return -EINVAL;
+
+		memset(&info, 0, sizeof(info));
+
+		lock_sock(sk);
+
+		if (sk->sk_state != BT_CONNECTED) {
+			release_sock(sk);
+			return -EINVAL;
+		}
+
+		hci_copy_tx_latency(&latency,
+				    &iso_pi(sk)->conn->hcon->tx_latency);
+
+		release_sock(sk);
+
+		if (!latency.now.time)
+			return -ENOENT;
+
+		info.queue = latency.now.queue;
+		info.time = ktime_to_ns(latency.now.time);
+		info.offset = latency.now.offset;
+
+		if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	return bt_sock_ioctl(sock, cmd, arg);
+}
+
 static struct hci_cb iso_cb = {
 	.name		= "ISO",
 	.connect_cfm	= iso_connect_cfm,
@@ -2270,7 +2318,7 @@  static const struct proto_ops iso_sock_ops = {
 	.sendmsg	= iso_sock_sendmsg,
 	.recvmsg	= iso_sock_recvmsg,
 	.poll		= bt_sock_poll,
-	.ioctl		= bt_sock_ioctl,
+	.ioctl		= iso_sock_ioctl,
 	.mmap		= sock_no_mmap,
 	.socketpair	= sock_no_socketpair,
 	.shutdown	= iso_sock_shutdown,