Linux bluetooth development
 help / color / mirror / Atom feed
From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ vRFC 2/4] monitor: Add latency standard deviation
Date: Thu, 27 Aug 2026 12:53:24 -0400	[thread overview]
Message-ID: <20260827165326.350079-2-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20260827165326.350079-1-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

The latency range and moving average alone do not tell a link that is
consistently slow apart from one that is unstable. A high average with a
low deviation points at the scheduling or the interval configuration,
while a low average with a high deviation points at interference,
retransmissions or controller buffer stalls.

Track the sample count along with the sum and the sum of the squares in
struct packet_latency, and report the resulting standard deviation on
the latency lines of both the live decoding and the analyze mode.

The samples are squared in usec and only scaled down to msec^2
afterwards, so that the sub-msec resolution is not lost while the
running sum stays clear of an overflow.

The model derived the accumulator layout and the scaling, which the
author reviewed and verified against an independently computed
deviation.

Assisted-by: opencode:claude-opus-5
---
 Makefile.tools    |  2 +-
 monitor/analyze.c |  6 ++++--
 monitor/packet.c  | 41 +++++++++++++++++++++++++++++++++++++++--
 monitor/packet.h  |  4 ++++
 4 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index b3ef4ae1c3df..8f9ffe97cbf0 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -62,7 +62,7 @@ monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
 				src/settings.h src/settings.c
 monitor_btmon_LDADD = lib/libbluetooth-internal.la \
 				src/libshared-mainloop.la \
-				$(GLIB_LIBS) $(UDEV_LIBS) -ldl
+				$(GLIB_LIBS) $(UDEV_LIBS) -ldl -lm
 
 if MANPAGES
 man_MANS += doc/btmon.1
diff --git a/monitor/analyze.c b/monitor/analyze.c
index de9c23603a21..b349887716da 100644
--- a/monitor/analyze.c
+++ b/monitor/analyze.c
@@ -155,10 +155,12 @@ static void print_stats(struct hci_stats *stats, const char *label)
 		return;
 
 	print_field("%s packets: %zu/%zu", label, stats->num, stats->num_comp);
-	print_field("%s Latency: %lld-%lld msec (~%lld msec)", label,
+	print_field("%s Latency: %lld-%lld msec (~%lld msec +/- %lld msec)",
+			label,
 			TV_MSEC(stats->latency.min),
 			TV_MSEC(stats->latency.max),
-			TV_MSEC(stats->latency.med));
+			TV_MSEC(stats->latency.med),
+			packet_latency_stddev(&stats->latency));
 	print_field("%s size: %u-%u octets (~%zd octets)", label,
 			stats->min, stats->max, stats->bytes / stats->num);
 
diff --git a/monitor/packet.c b/monitor/packet.c
index 0d3b23cc3fb7..174d844c05cb 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -27,6 +27,7 @@
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <limits.h>
+#include <math.h>
 
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/uuid.h"
@@ -11524,8 +11525,27 @@ static void role_change_evt(struct timeval *tv, uint16_t index,
 
 void packet_latency_add(struct packet_latency *latency, struct timeval *delta)
 {
+	uint64_t usec;
+
 	timeradd(&latency->total, delta, &latency->total);
 
+	/*
+	 * Negative deltas are the result of out of order timestamps and
+	 * would only skew the deviation, so leave them out.
+	 */
+	if (delta->tv_sec >= 0 && delta->tv_usec >= 0) {
+		usec = (uint64_t)delta->tv_sec * 1000000 + delta->tv_usec;
+
+		latency->count++;
+		latency->sum_usec += usec;
+		/*
+		 * Square first and scale down to msec^2 afterwards so the
+		 * sub-msec resolution is not lost, while keeping the running
+		 * sum well clear of an overflow.
+		 */
+		latency->sum_sq_msec += (usec * usec) / 1000000;
+	}
+
 	if ((!timerisset(&latency->min) || timercmp(delta, &latency->min, <))
 				&& delta->tv_sec >= 0 && delta->tv_usec >= 0)
 		latency->min = *delta;
@@ -11553,6 +11573,21 @@ void packet_latency_add(struct packet_latency *latency, struct timeval *delta)
 		latency->med = *delta;
 }
 
+long long packet_latency_stddev(const struct packet_latency *latency)
+{
+	double mean, var;
+
+	if (latency->count < 2)
+		return 0;
+
+	mean = (double)latency->sum_usec / latency->count / 1000;
+	var = (double)latency->sum_sq_msec / latency->count - mean * mean;
+	if (var <= 0)
+		return 0;
+
+	return (long long)sqrt(var);
+}
+
 static void packet_dequeue_tx(struct timeval *tv, uint16_t handle)
 {
 	struct packet_conn_data *conn;
@@ -11585,10 +11620,12 @@ static void packet_dequeue_tx(struct timeval *tv, uint16_t handle)
 	if (TV_MSEC(delta)) {
 		print_field("#%zu: len %zu (%lld Kb/s)", frame->num, frame->len,
 				frame->len * 8 / TV_MSEC(delta));
-		print_field("Latency: %lld msec (%lld-%lld msec ~%lld msec)",
+		print_field("Latency: %lld msec (%lld-%lld msec ~%lld msec "
+				"+/- %lld msec)",
 				TV_MSEC(delta), TV_MSEC(conn->tx_l.min),
 				TV_MSEC(conn->tx_l.max),
-				TV_MSEC(conn->tx_l.med));
+				TV_MSEC(conn->tx_l.med),
+				packet_latency_stddev(&conn->tx_l));
 	}
 
 	l2cap_dequeue_frame(&delta, conn);
diff --git a/monitor/packet.h b/monitor/packet.h
index 73a86f64b242..6b792c0e420c 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -31,6 +31,9 @@ struct packet_latency {
 	struct timeval min;
 	struct timeval max;
 	struct timeval med;
+	uint64_t count;
+	uint64_t sum_usec;	/* Sum of samples, in usec */
+	uint64_t sum_sq_msec;	/* Sum of squared samples, in msec^2 */
 };
 
 struct packet_frame {
@@ -69,6 +72,7 @@ struct packet_conn_data {
 
 struct packet_conn_data *packet_get_conn_data(uint16_t handle);
 void packet_latency_add(struct packet_latency *latency, struct timeval *delta);
+long long packet_latency_stddev(const struct packet_latency *latency);
 
 bool packet_has_filter(unsigned long filter);
 void packet_set_filter(unsigned long filter);
-- 
2.54.0


  reply	other threads:[~2026-08-27 16:53 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-27 16:53 [PATCH BlueZ vRFC 1/4] test-runner: Add support for PCIe passthrough Luiz Augusto von Dentz
2026-08-27 16:53 ` Luiz Augusto von Dentz [this message]
2026-08-27 16:53 ` [PATCH BlueZ vRFC 3/4] monitor: Add ISO packet loss counters Luiz Augusto von Dentz
2026-08-27 16:53 ` [PATCH BlueZ vRFC 4/4] doc/btmon: Document the deviation and " Luiz Augusto von Dentz
2026-08-28  1:17 ` [BlueZ,vRFC,1/4] test-runner: Add support for PCIe passthrough bluez.test.bot
2026-09-04 19:30 ` [PATCH BlueZ vRFC 1/4] " patchwork-bot+bluetooth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260827165326.350079-2-luiz.dentz@gmail.com \
    --to=luiz.dentz@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox