2024-03-07 21:05:47

by Pauli Virtanen

[permalink] [raw]
Subject: [PATCH BlueZ 1/3] shared/io-glib: add watcher to be used with TX timestamping

Add special implementation of fd watcher GSource for audio use.

For audio use cases, sound server may turn on TX timestamping on a
socket that we are watching. In this case, we shall not consider the TX
timestamping POLLERR as a socket error condition, nor read the TX
timestamps.

When TX timestamps appear in errqueue, switch from fd poll wait to
polling the fd at regular intervals. This is because unread errqueue
causes poll() to wake up immediately, so the mainloop cannot block on
that, and we have to use a timer instead with some reasonable timeout
for the use case.

This avoids consuming CPU waking up on timestamps we aren't going to
read, and also avoids busy looping if timestamping was left on but
errqueue is not flushed.

Implement this only for io-glib; it is only needed for audio use cases
that anyway are using glib. Uses features from GLib 2.36 (from 2013) so
update configure.ac also.
---

Notes:
This was the remaining BlueZ part of the TX timestamping additions.
Couldn't find a better way to do it, but it has to be done, so that the
TX timestamping can be used for the purpose it would be added.

I'll probably send v2 for the others in a few days.

acinclude.m4 | 3 +-
configure.ac | 2 +-
src/shared/io-glib.c | 153 ++++++++++++++++++++++++++++++++++++++++++-
src/shared/io-glib.h | 20 ++++++
4 files changed, 174 insertions(+), 4 deletions(-)
create mode 100644 src/shared/io-glib.h

diff --git a/acinclude.m4 b/acinclude.m4
index 4b73a5bfc..6c36c177e 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -62,8 +62,7 @@ AC_DEFUN([COMPILER_FLAGS], [
with_cflags="$with_cflags -Wswitch-enum"
with_cflags="$with_cflags -Wformat -Wformat-security"
with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
- with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
- with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32"
+ with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36"
fi
AC_SUBST([WARNING_CFLAGS], $with_cflags)
])
diff --git a/configure.ac b/configure.ac
index 9ebc250cf..959a27b4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,7 +72,7 @@ AC_CHECK_LIB(dl, dlopen, dummy=yes,

AC_CHECK_HEADERS(linux/types.h linux/if_alg.h linux/uinput.h linux/uhid.h sys/random.h)

-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28)
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.36)

if (test "${enable_threads}" = "yes"); then
AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c
index 754043db1..8b76348dd 100644
--- a/src/shared/io-glib.c
+++ b/src/shared/io-glib.c
@@ -13,10 +13,12 @@
#endif

#include <errno.h>
+#include <sys/socket.h>

#include <glib.h>

#include "src/shared/io.h"
+#include "src/shared/io-glib.h"

struct io_watch {
struct io *io;
@@ -29,11 +31,19 @@ struct io_watch {
struct io {
int ref_count;
GIOChannel *channel;
+ bool err_watch;
struct io_watch *read_watch;
struct io_watch *write_watch;
struct io_watch *disconnect_watch;
};

+struct io_err_watch {
+ GSource source;
+ GIOChannel *io;
+ GIOCondition events;
+ gpointer tag;
+};
+
static struct io *io_ref(struct io *io)
{
if (!io)
@@ -179,10 +189,17 @@ static struct io_watch *watch_new(struct io *io, GIOCondition cond,

prio = cond == G_IO_HUP ? G_PRIORITY_DEFAULT_IDLE : G_PRIORITY_DEFAULT;

- watch->id = g_io_add_watch_full(io->channel, prio,
+ if (!io->err_watch)
+ watch->id = g_io_add_watch_full(io->channel, prio,
cond | G_IO_ERR | G_IO_NVAL,
watch_callback, watch,
watch_destroy);
+ else
+ watch->id = io_glib_add_err_watch_full(io->channel, prio,
+ cond | G_IO_ERR | G_IO_NVAL,
+ watch_callback, watch,
+ watch_destroy);
+
if (watch->id == 0) {
watch_destroy(watch);
return NULL;
@@ -250,6 +267,15 @@ bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
return io_set_handler(io, G_IO_HUP, callback, user_data, destroy);
}

+bool io_set_use_err_watch(struct io *io, bool err_watch)
+{
+ if (!io)
+ return false;
+
+ io->err_watch = err_watch;
+ return true;
+}
+
ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
{
int fd;
@@ -278,3 +304,128 @@ bool io_shutdown(struct io *io)
return g_io_channel_shutdown(io->channel, TRUE, NULL)
== G_IO_STATUS_NORMAL;
}
+
+/*
+ * GSource implementation that tolerates non-empty MSG_ERRQUEUE, without
+ * attempting to flush it. This is intended for use with TX timestamping in
+ * cases where someone else is reading the timestamps and we are only interested
+ * in POLLHUP or socket errors.
+ */
+
+static gint64 io_err_watch_wakeup;
+
+static gboolean io_err_watch_dispatch(GSource *source,
+ GSourceFunc callback, gpointer user_data)
+{
+ struct io_err_watch *watch = (void *)source;
+ GIOFunc func = (void *)callback;
+ gint64 timeout = 500 * G_TIME_SPAN_MILLISECOND;
+ GIOCondition cond;
+ int fd;
+
+ if (!func)
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(watch->io);
+
+ /*
+ * If woken up by POLLERR only, and SO_ERROR is not set, ignore this
+ * event. Also disable polling for some time so that we don't busy loop
+ * if nobody is reading from the errqueue.
+ */
+
+ if (watch->tag)
+ cond = g_source_query_unix_fd(&watch->source, watch->tag);
+ else
+ cond = 0;
+
+ if (cond == G_IO_ERR) {
+ int err, ret;
+ socklen_t len = sizeof(err);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
+ if (ret == 0 && err == 0) {
+ g_source_remove_unix_fd(&watch->source, watch->tag);
+ watch->tag = NULL;
+
+ /* io_err watches all wake up at the same time */
+ if (!io_err_watch_wakeup)
+ io_err_watch_wakeup = g_get_monotonic_time()
+ + timeout;
+
+ g_source_set_ready_time(&watch->source,
+ io_err_watch_wakeup);
+ return TRUE;
+ }
+ }
+
+ if (g_source_get_ready_time(&watch->source) != -1) {
+ g_assert(!watch->tag);
+ io_err_watch_wakeup = 0;
+ watch->tag = g_source_add_unix_fd(&watch->source, fd,
+ watch->events);
+ g_source_set_ready_time(&watch->source, -1);
+ }
+
+ cond &= watch->events;
+
+ if (cond)
+ return func(watch->io, cond, user_data);
+ else
+ return TRUE;
+}
+
+static void io_err_watch_finalize(GSource *source)
+{
+ struct io_err_watch *watch = (void *)source;
+
+ if (watch->tag)
+ g_source_remove_unix_fd(&watch->source, watch->tag);
+
+ g_io_channel_unref(watch->io);
+}
+
+guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
+ GIOCondition events,
+ GIOFunc func, gpointer user_data,
+ GDestroyNotify notify)
+{
+ static GSourceFuncs source_funcs = {
+ .dispatch = io_err_watch_dispatch,
+ .finalize = io_err_watch_finalize,
+ };
+ GSourceFunc callback = (void *)func;
+ struct io_err_watch *watch;
+ gint fd;
+ guint id;
+
+ g_return_val_if_fail(!(events & (G_IO_IN | G_IO_OUT)), 0);
+ g_return_val_if_fail(func, 0);
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ watch = (void *)g_source_new(&source_funcs,
+ sizeof(struct io_err_watch));
+
+ watch->io = g_io_channel_ref(io);
+ watch->events = events;
+ watch->tag = g_source_add_unix_fd(&watch->source, fd, events);
+
+ g_source_set_static_name((void *)watch, "io_glib_err_watch");
+ g_source_set_callback(&watch->source, callback, user_data, notify);
+
+ if (priority != G_PRIORITY_DEFAULT)
+ g_source_set_priority(&watch->source, priority);
+
+ id = g_source_attach(&watch->source, NULL);
+ g_source_unref(&watch->source);
+
+ return id;
+}
+
+guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events, GIOFunc func,
+ gpointer user_data)
+{
+ return io_glib_add_err_watch_full(io, G_PRIORITY_DEFAULT, events,
+ func, user_data, NULL);
+}
diff --git a/src/shared/io-glib.h b/src/shared/io-glib.h
new file mode 100644
index 000000000..1db6fd468
--- /dev/null
+++ b/src/shared/io-glib.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#include <glib.h>
+
+guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events,
+ GIOFunc func, gpointer user_data);
+guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
+ GIOCondition events, GIOFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+bool io_set_use_err_watch(struct io *io, bool err_watch);
--
2.44.0



2024-03-07 21:05:52

by Pauli Virtanen

[permalink] [raw]
Subject: [PATCH BlueZ 2/3] avdtp: don't consider TX timestamps as errors

Use io_add_err_watch to avoid considering TX timestamps as errors in the
transport io channel.
---
profiles/audio/avdtp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 3667e0840..16d7f7385 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -31,6 +31,8 @@
#include "btio/btio.h"
#include "src/btd.h"
#include "src/log.h"
+#include "src/shared/io.h"
+#include "src/shared/io-glib.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
@@ -862,7 +864,8 @@ proceed:

avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);

- stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ stream->io_id = io_glib_add_err_watch(io,
+ G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) transport_cb, stream);

/* Release pending IO */
--
2.44.0


2024-03-07 21:25:57

by Pauli Virtanen

[permalink] [raw]
Subject: [PATCH BlueZ 3/3] bap: don't consider TX timestamps as errors

Use io_add_err_watch to avoid considering TX timestamps as errors in the
transport io channel.
---
profiles/audio/bap.c | 14 +++++++++-----
src/shared/bap.c | 3 +++
2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 1b8a47c52..3ed1ed750 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -39,6 +39,8 @@
#include "src/btd.h"
#include "src/dbus-common.h"
#include "src/shared/util.h"
+#include "src/shared/io.h"
+#include "src/shared/io-glib.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
@@ -1867,8 +1869,9 @@ static void setup_connect_io(struct bap_data *data, struct bap_setup *setup,
return;
}

- setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- setup_io_disconnected, setup);
+ setup->io_id = io_glib_add_err_watch(io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ setup_io_disconnected, setup);

setup->io = io;
setup->cig_active = !defer;
@@ -1925,7 +1928,8 @@ static void setup_connect_io_broadcast(struct bap_data *data,
return;
}

- setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ setup->io_id = io_glib_add_err_watch(io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
setup_io_disconnected, setup);

setup->io = io;
@@ -2558,7 +2562,7 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,

if (!setup->io) {
io = g_io_channel_unix_new(fd);
- setup->io_id = g_io_add_watch(io,
+ setup->io_id = io_glib_add_err_watch(io,
G_IO_HUP | G_IO_ERR | G_IO_NVAL,
setup_io_disconnected, setup);
setup->io = io;
@@ -2603,7 +2607,7 @@ static void bap_connecting_bcast(struct bt_bap_stream *stream, bool state,

if (!setup->io) {
io = g_io_channel_unix_new(fd);
- setup->io_id = g_io_add_watch(io,
+ setup->io_id = io_glib_add_err_watch(io,
G_IO_HUP | G_IO_ERR | G_IO_NVAL,
setup_io_disconnected, setup);
setup->io = io;
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 37fc1de4e..128d98b6a 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -20,6 +20,7 @@
#include "lib/uuid.h"

#include "src/shared/io.h"
+#include "src/shared/io-glib.h"
#include "src/shared/queue.h"
#include "src/shared/util.h"
#include "src/shared/timeout.h"
@@ -2130,6 +2131,8 @@ static struct bt_bap_stream_io *stream_io_new(struct bt_bap *bap, int fd)

DBG(bap, "fd %d", fd);

+ io_set_use_err_watch(io, true);
+
sio = new0(struct bt_bap_stream_io, 1);
sio->bap = bap;
sio->io = io;
--
2.44.0


2024-03-07 21:59:29

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,1/3] shared/io-glib: add watcher to be used with TX timestamping

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=833581

---Test result---

Test Summary:
CheckPatch PASS 1.60 seconds
GitLint FAIL 1.16 seconds
BuildEll PASS 23.85 seconds
BluezMake FAIL 134.64 seconds
MakeCheck FAIL 0.84 seconds
MakeDistcheck FAIL 34.88 seconds
CheckValgrind FAIL 100.30 seconds
CheckSmatch FAIL 227.49 seconds
bluezmakeextell FAIL 86.88 seconds
IncrementalBuild FAIL 1366.79 seconds
ScanBuild FAIL 771.91 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,1/3] shared/io-glib: add watcher to be used with TX timestamping

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
29: B2 Line has trailing whitespace: " "
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12723:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12723 | int main(int argc, char *argv[])
| ^~~~
src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4646: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make: *** [Makefile:12181: check] Error 2
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

Package cups was not found in the pkg-config search path.
Perhaps you should add the directory containing `cups.pc'
to the PKG_CONFIG_PATH environment variable
No package 'cups' found
../../src/shared/bap.c:23:10: fatal error: src/shared/io-glib.h: No such file or directory
23 | #include "src/shared/io-glib.h"
| ^~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [Makefile:8570: src/shared/libshared_mainloop_la-bap.lo] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [Makefile:4646: all] Error 2
make: *** [Makefile:12102: distcheck] Error 1
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12723:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12723 | int main(int argc, char *argv[])
| ^~~~
src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:12181: check] Error 2
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:

src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:768:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:830:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1323:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1354:23: warning: Variable length array is used.
src/shared/gatt-server.c:278:25: warning: Variable length array is used.
src/shared/gatt-server.c:621:25: warning: Variable length array is used.
src/shared/gatt-server.c:720:25: warning: Variable length array is used.
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:768:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:830:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1323:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1354:23: warning: Variable length array is used.
src/shared/gatt-server.c:278:25: warning: Variable length array is used.
src/shared/gatt-server.c:621:25: warning: Variable length array is used.
src/shared/gatt-server.c:720:25: warning: Variable length array is used.
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
tools/mesh-cfgtest.c:1453:17: warning: unknown escape sequence: '\%'
tools/sco-tester.c: note: in included file:
./lib/bluetooth.h:216:15: warning: array of flexible structures
./lib/bluetooth.h:221:31: warning: array of flexible structures
tools/bneptest.c:634:39: warning: unknown escape sequence: '\%'
tools/seq2bseq.c:57:26: warning: Variable length array is used.
tools/obex-client-tool.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
android/avctp.c:505:34: warning: Variable length array is used.
android/avctp.c:556:34: warning: Variable length array is used.
unit/test-avrcp.c:373:26: warning: Variable length array is used.
unit/test-avrcp.c:398:26: warning: Variable length array is used.
unit/test-avrcp.c:414:24: warning: Variable length array is used.
android/avrcp-lib.c:1085:34: warning: Variable length array is used.
android/avrcp-lib.c:1583:34: warning: Variable length array is used.
android/avrcp-lib.c:1612:34: warning: Variable length array is used.
android/avrcp-lib.c:1638:34: warning: Variable length array is used.
mesh/mesh-io-mgmt.c:523:67: warning: Variable length array is used.
client/display.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:768:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:830:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1323:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1354:23: warning: Variable length array is used.
src/shared/gatt-server.c:278:25: warning: Variable length array is used.
src/shared/gatt-server.c:621:25: warning: Variable length array is used.
src/shared/gatt-server.c:720:25: warning: Variable length array is used.
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4646: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:

src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4646: all] Error 2
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:
[BlueZ,1/3] shared/io-glib: add watcher to be used with TX timestamping

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12723:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12723 | int main(int argc, char *argv[])
| ^~~~
unit/test-avdtp.c: In function ‘main’:
unit/test-avdtp.c:766:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
766 | int main(int argc, char *argv[])
| ^~~~
unit/test-avrcp.c: In function ‘main’:
unit/test-avrcp.c:989:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
989 | int main(int argc, char *argv[])
| ^~~~
src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4646: all] Error 2
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:

src/shared/gatt-client.c:451:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:696:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:993:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1099:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1291:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1356:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1631:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1636:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2140:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2148:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3237:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3259:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
12 warnings generated.
src/shared/bap.c:1156:2: warning: Use of memory after it is freed
DBG(stream->bap, "stream %p", stream);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:40:2: note: expanded from macro 'DBG'
bap_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/shared/shell.c:1228:13: warning: Access to field 'options' results in a dereference of a null pointer (loaded from variable 'opt')
if (c != opt->options[index - offset].val) {
^~~~~~~~~~~~
1 warning generated.
src/shared/gatt-client.c:451:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:696:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:993:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1099:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1291:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1356:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1631:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1636:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2140:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2148:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3237:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3259:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
12 warnings generated.
src/shared/bap.c:1156:2: warning: Use of memory after it is freed
DBG(stream->bap, "stream %p", stream);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:40:2: note: expanded from macro 'DBG'
bap_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/shared/shell.c:1228:13: warning: Access to field 'options' results in a dereference of a null pointer (loaded from variable 'opt')
if (c != opt->options[index - offset].val) {
^~~~~~~~~~~~
1 warning generated.
tools/hciattach.c:816:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 10)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:864:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:886:8: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 10)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:908:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:929:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:973:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 6)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 warnings generated.
src/oui.c:50:2: warning: Value stored to 'hwdb' is never read
hwdb = udev_hwdb_unref(hwdb);
^ ~~~~~~~~~~~~~~~~~~~~~
src/oui.c:53:2: warning: Value stored to 'udev' is never read
udev = udev_unref(udev);
^ ~~~~~~~~~~~~~~~~
2 warnings generated.
tools/hcidump.c:180:9: warning: Potential leak of memory pointed to by 'dp'
if (fds[i].fd == sock)
^~~
tools/hcidump.c:248:17: warning: Assigned value is garbage or undefined
dh->ts_sec = htobl(frm.ts.tv_sec);
^ ~~~~~~~~~~~~~~~~~~~~
tools/hcidump.c:326:9: warning: 1st function call argument is an uninitialized value
if (be32toh(dp.flags) & 0x02) {
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:341:20: warning: 1st function call argument is an uninitialized value
frm.data_len = be32toh(dp.len);
^~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:346:14: warning: 1st function call argument is an uninitialized value
opcode = be32toh(dp.flags) & 0xffff;
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:384:17: warning: Assigned value is garbage or undefined
frm.data_len = btohs(dh.len);
^ ~~~~~~~~~~~~~
tools/hcidump.c:394:11: warning: Assigned value is garbage or undefined
frm.len = frm.data_len;
^ ~~~~~~~~~~~~
tools/hcidump.c:398:9: warning: 1st function call argument is an uninitialized value
ts = be64toh(ph.ts);
^~~~~~~~~~~~~~
/usr/include/endian.h:51:22: note: expanded from macro 'be64toh'
# define be64toh(x) __bswap_64 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:403:13: warning: 1st function call argument is an uninitialized value
frm.in = be32toh(dp.flags) & 0x01;
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:408:11: warning: Assigned value is garbage or undefined
frm.in = dh.in;
^ ~~~~~
tools/hcidump.c:437:7: warning: Null pointer passed to 1st parameter expecting 'nonnull'
fd = open(file, open_flags, 0644);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 warnings generated.
tools/rfcomm.c:234:3: warning: Value stored to 'i' is never read
i = execvp(cmdargv[0], cmdargv);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:234:7: warning: Null pointer passed to 1st parameter expecting 'nonnull'
i = execvp(cmdargv[0], cmdargv);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:354:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:497:14: warning: Assigned value is garbage or undefined
req.channel = raddr.rc_channel;
^ ~~~~~~~~~~~~~~~~
tools/rfcomm.c:515:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 warnings generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:300:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:338:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
3 warnings generated.
tools/ciptool.c:350:7: warning: 5th function call argument is an uninitialized value
sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/sdptool.c:941:26: warning: Result of 'malloc' is converted to a pointer of type 'uint32_t', which is incompatible with sizeof operand type 'int'
uint32_t *value_int = malloc(sizeof(int));
~~~~~~~~~~ ^~~~~~ ~~~~~~~~~~~
tools/sdptool.c:980:4: warning: 1st function call argument is an uninitialized value
free(allocArray[i]);
^~~~~~~~~~~~~~~~~~~
tools/sdptool.c:3777:2: warning: Potential leak of memory pointed to by 'si.name'
return add_service(0, &si);
^~~~~~~~~~~~~~~~~~~~~~~~~~
tools/sdptool.c:4112:4: warning: Potential leak of memory pointed to by 'context.svc'
return -1;
^~~~~~~~~
4 warnings generated.
tools/avtest.c:225:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:235:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:244:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:258:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~
tools/avtest.c:265:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~
tools/avtest.c:272:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~
tools/avtest.c:279:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~
tools/avtest.c:291:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:295:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:304:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:308:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:317:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:324:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:346:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:350:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:359:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:363:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:376:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:380:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:387:4: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:397:4: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:562:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:570:3: warning: Value stored to 'len' is never read
len = write(sk, buf, invalid ? 2 : 3);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:584:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 4 + sizeof(media_transport));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:597:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:607:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:619:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:634:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:646:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:655:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:662:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:698:2: warning: Value stored to 'len' is never read
len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 warnings generated.
tools/btproxy.c:836:15: warning: Null pointer passed to 1st parameter expecting 'nonnull'
tcp_port = atoi(optarg);
^~~~~~~~~~~~
tools/btproxy.c:839:8: warning: Null pointer passed to 1st parameter expecting 'nonnull'
if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
^~~~~~~~~~~~~~
2 warnings generated.
tools/create-image.c:76:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:84:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:92:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:105:2: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
4 warnings generated.
tools/btgatt-client.c:1824:2: warning: Value stored to 'argv' is never read
argv += optind;
^ ~~~~~~
1 warning generated.
tools/btgatt-server.c:1212:2: warning: Value stored to 'argv' is never read
argv -= optind;
^ ~~~~~~
1 warning generated.
tools/check-selftest.c:42:3: warning: Value stored to 'ptr' is never read
ptr = fgets(result, sizeof(result), fp);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/gatt-service.c:294:2: warning: 2nd function call argument is an uninitialized value
chr_write(chr, value, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/obex-server-tool.c:133:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
data->fd = open(name, O_WRONLY | O_CREAT | O_NOCTTY, 0600);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/obex-server-tool.c:192:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
data->fd = open(name, O_RDONLY | O_NOCTTY, 0);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
tools/test-runner.c:945:2: warning: 2nd function call argument is an uninitialized value
printf("Running command %s\n", cmdname ? cmdname : argv[0]);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/btpclientctl.c:402:3: warning: Value stored to 'bit' is never read
bit = 0;
^ ~
tools/btpclientctl.c:1655:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
memcpy(cp->data, ad_data, ad_len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdpd-request.c:211:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
pElem = malloc(sizeof(uint16_t));
^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:239:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
pElem = malloc(sizeof(uint32_t));
^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
android/avrcp-lib.c:1968:3: warning: 1st function call argument is an uninitialized value
g_free(text[i]);
^~~~~~~~~~~~~~~
1 warning generated.
profiles/health/hdp.c:644:3: warning: Use of memory after it is freed
hdp_tmp_dc_data_unref(dc_data);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
profiles/health/hdp.c:800:19: warning: Use of memory after it is freed
path = g_strdup(chan->path);
^~~~~~~~~~
profiles/health/hdp.c:1779:6: warning: Use of memory after it is freed
hdp_tmp_dc_data_ref(hdp_conn),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
profiles/health/hdp.c:1836:30: warning: Use of memory after it is freed
reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
^~~~~~~~~
4 warnings generated.
profiles/health/hdp_util.c:1052:2: warning: Use of memory after it is freed
conn_data->func(conn_data->data, gerr);
^~~~~~~~~~~~~~~
1 warning generated.
attrib/gatt.c:970:2: warning: Potential leak of memory pointed to by 'long_write'
return prepare_write(long_write);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdpd-request.c:211:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
pElem = malloc(sizeof(uint16_t));
^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:239:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
pElem = malloc(sizeof(uint32_t));
^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:300:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:338:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
3 warnings generated.
src/sdp-client.c:353:14: warning: Access to field 'cb' results in a dereference of a null pointer
(*ctxt)->cb = cb;
~~~~~~~~~~~~^~~~
1 warning generated.
src/gatt-database.c:1154:10: warning: Value stored to 'bits' during its initialization is never read
uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
^~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-header.c:67:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
memcpy(to, from, count);
^~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-transfer.c:423:7: warning: Use of memory after it is freed
if (!g_slist_find(transfers, transfer))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
mesh/main.c:161:3: warning: Value stored to 'optarg' is never read
optarg += strlen("auto");
^ ~~~~~~~~~~~~~~
1 warning generated.
lib/hci.c:97:4: warning: Value stored to 'ptr' is never read
ptr += sprintf(ptr, "%s", m->str);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gdbus/watch.c:204:3: warning: Attempt to free released memory
g_free(l->data);
^~~~~~~~~~~~~~~
1 warning generated.
lib/sdp.c:509:16: warning: Dereference of undefined pointer value
int8_t dtd = *(uint8_t *) dtds[i];
^~~~~~~~~~~~~~~~~~~~
lib/sdp.c:537:17: warning: Dereference of undefined pointer value
uint8_t dtd = *(uint8_t *) dtds[i];
^~~~~~~~~~~~~~~~~~~~
lib/sdp.c:582:12: warning: Access to field 'attrId' results in a dereference of a null pointer (loaded from variable 'd')
d->attrId = attr;
~ ^
lib/sdp.c:1872:26: warning: Potential leak of memory pointed to by 'ap'
for (; pdlist; pdlist = pdlist->next) {
^~~~~~
lib/sdp.c:1886:6: warning: Potential leak of memory pointed to by 'pds'
ap = sdp_list_append(ap, pds);
~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
lib/sdp.c:1931:10: warning: Potential leak of memory pointed to by 'u'
*seqp = sdp_list_append(*seqp, u);
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/sdp.c:2036:4: warning: Potential leak of memory pointed to by 'lang'
sdp_list_free(*langSeq, free);
^~~~~~~~~~~~~
lib/sdp.c:2125:9: warning: Potential leak of memory pointed to by 'profDesc'
return 0;
^
lib/sdp.c:3257:8: warning: Potential leak of memory pointed to by 'pSvcRec'
pSeq = sdp_list_append(pSeq, pSvcRec);
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/sdp.c:3258:9: warning: Potential leak of memory pointed to by 'pSeq'
pdata += sizeof(uint32_t);
~~~~~~^~~~~~~~~~~~~~~~~~~
lib/sdp.c:4594:13: warning: Potential leak of memory pointed to by 'rec_list'
} while (scanned < attr_list_len && pdata_len > 0);
^~~~~~~
lib/sdp.c:4890:40: warning: Potential leak of memory pointed to by 'tseq'
for (d = sdpdata->val.dataseq; d; d = d->next) {
^
lib/sdp.c:4926:8: warning: Potential leak of memory pointed to by 'subseq'
tseq = sdp_list_append(tseq, subseq);
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/gatt-client.c:451:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:696:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:993:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1099:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1291:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1356:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1631:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1636:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2140:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2148:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3237:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3259:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
12 warnings generated.
src/shared/bap.c:1156:2: warning: Use of memory after it is freed
DBG(stream->bap, "stream %p", stream);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:40:2: note: expanded from macro 'DBG'
bap_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/shared/io-glib.c: In function ‘io_glib_add_err_watch_full’:
src/shared/io-glib.c:414:2: error: implicit declaration of function ‘g_source_set_static_name’; did you mean ‘g_source_set_ready_time’? [-Werror=implicit-function-declaration]
414 | g_source_set_static_name((void *)watch, "io_glib_err_watch");
| ^~~~~~~~~~~~~~~~~~~~~~~~
| g_source_set_ready_time
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8395: src/shared/libshared_glib_la-io-glib.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
src/shared/shell.c:1228:13: warning: Access to field 'options' results in a dereference of a null pointer (loaded from variable 'opt')
if (c != opt->options[index - offset].val) {
^~~~~~~~~~~~
1 warning generated.
make: *** [Makefile:4646: all] Error 2


---
Regards,
Linux Bluetooth

2024-03-08 15:12:40

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 1/3] shared/io-glib: add watcher to be used with TX timestamping

Hi Pauli,

On Thu, Mar 7, 2024 at 4:05 PM Pauli Virtanen <[email protected]> wrote:
>
> Add special implementation of fd watcher GSource for audio use.
>
> For audio use cases, sound server may turn on TX timestamping on a
> socket that we are watching. In this case, we shall not consider the TX
> timestamping POLLERR as a socket error condition, nor read the TX
> timestamps.
>
> When TX timestamps appear in errqueue, switch from fd poll wait to
> polling the fd at regular intervals. This is because unread errqueue
> causes poll() to wake up immediately, so the mainloop cannot block on
> that, and we have to use a timer instead with some reasonable timeout
> for the use case.
>
> This avoids consuming CPU waking up on timestamps we aren't going to
> read, and also avoids busy looping if timestamping was left on but
> errqueue is not flushed.
>
> Implement this only for io-glib; it is only needed for audio use cases
> that anyway are using glib. Uses features from GLib 2.36 (from 2013) so
> update configure.ac also.

Do we really need to enable the TX timestamping on the daemon side
though? Or that being enabled at pipewire enables it on all fd that
have been duplicated? I'd assume that was not the case but perhaps the
kernel code is actually doing TX timestamping regardless if the socket
had it enabled or not? Btw, I'd consider disabling the POLLERR
entirely if that happens and just rely on POLLHUP/shutdown to clean
up, or perhaps we can set up a BPF filter that drops the errqueue
messages that way we don't have to change anything on how we handle
POLLERR since that should prevent the daemon to be wakeup.

> ---
>
> Notes:
> This was the remaining BlueZ part of the TX timestamping additions.
> Couldn't find a better way to do it, but it has to be done, so that the
> TX timestamping can be used for the purpose it would be added.
>
> I'll probably send v2 for the others in a few days.
>
> acinclude.m4 | 3 +-
> configure.ac | 2 +-
> src/shared/io-glib.c | 153 ++++++++++++++++++++++++++++++++++++++++++-
> src/shared/io-glib.h | 20 ++++++
> 4 files changed, 174 insertions(+), 4 deletions(-)
> create mode 100644 src/shared/io-glib.h
>
> diff --git a/acinclude.m4 b/acinclude.m4
> index 4b73a5bfc..6c36c177e 100644
> --- a/acinclude.m4
> +++ b/acinclude.m4
> @@ -62,8 +62,7 @@ AC_DEFUN([COMPILER_FLAGS], [
> with_cflags="$with_cflags -Wswitch-enum"
> with_cflags="$with_cflags -Wformat -Wformat-security"
> with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
> - with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
> - with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32"
> + with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36"
> fi
> AC_SUBST([WARNING_CFLAGS], $with_cflags)
> ])
> diff --git a/configure.ac b/configure.ac
> index 9ebc250cf..959a27b4d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -72,7 +72,7 @@ AC_CHECK_LIB(dl, dlopen, dummy=yes,
>
> AC_CHECK_HEADERS(linux/types.h linux/if_alg.h linux/uinput.h linux/uhid.h sys/random.h)
>
> -PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28)
> +PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.36)
>
> if (test "${enable_threads}" = "yes"); then
> AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
> diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c
> index 754043db1..8b76348dd 100644
> --- a/src/shared/io-glib.c
> +++ b/src/shared/io-glib.c
> @@ -13,10 +13,12 @@
> #endif
>
> #include <errno.h>
> +#include <sys/socket.h>
>
> #include <glib.h>
>
> #include "src/shared/io.h"
> +#include "src/shared/io-glib.h"
>
> struct io_watch {
> struct io *io;
> @@ -29,11 +31,19 @@ struct io_watch {
> struct io {
> int ref_count;
> GIOChannel *channel;
> + bool err_watch;
> struct io_watch *read_watch;
> struct io_watch *write_watch;
> struct io_watch *disconnect_watch;
> };
>
> +struct io_err_watch {
> + GSource source;
> + GIOChannel *io;
> + GIOCondition events;
> + gpointer tag;
> +};
> +
> static struct io *io_ref(struct io *io)
> {
> if (!io)
> @@ -179,10 +189,17 @@ static struct io_watch *watch_new(struct io *io, GIOCondition cond,
>
> prio = cond == G_IO_HUP ? G_PRIORITY_DEFAULT_IDLE : G_PRIORITY_DEFAULT;
>
> - watch->id = g_io_add_watch_full(io->channel, prio,
> + if (!io->err_watch)
> + watch->id = g_io_add_watch_full(io->channel, prio,
> cond | G_IO_ERR | G_IO_NVAL,
> watch_callback, watch,
> watch_destroy);
> + else
> + watch->id = io_glib_add_err_watch_full(io->channel, prio,
> + cond | G_IO_ERR | G_IO_NVAL,
> + watch_callback, watch,
> + watch_destroy);
> +
> if (watch->id == 0) {
> watch_destroy(watch);
> return NULL;
> @@ -250,6 +267,15 @@ bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
> return io_set_handler(io, G_IO_HUP, callback, user_data, destroy);
> }
>
> +bool io_set_use_err_watch(struct io *io, bool err_watch)
> +{
> + if (!io)
> + return false;
> +
> + io->err_watch = err_watch;
> + return true;
> +}
> +
> ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
> {
> int fd;
> @@ -278,3 +304,128 @@ bool io_shutdown(struct io *io)
> return g_io_channel_shutdown(io->channel, TRUE, NULL)
> == G_IO_STATUS_NORMAL;
> }
> +
> +/*
> + * GSource implementation that tolerates non-empty MSG_ERRQUEUE, without
> + * attempting to flush it. This is intended for use with TX timestamping in
> + * cases where someone else is reading the timestamps and we are only interested
> + * in POLLHUP or socket errors.
> + */
> +
> +static gint64 io_err_watch_wakeup;
> +
> +static gboolean io_err_watch_dispatch(GSource *source,
> + GSourceFunc callback, gpointer user_data)
> +{
> + struct io_err_watch *watch = (void *)source;
> + GIOFunc func = (void *)callback;
> + gint64 timeout = 500 * G_TIME_SPAN_MILLISECOND;
> + GIOCondition cond;
> + int fd;
> +
> + if (!func)
> + return FALSE;
> +
> + fd = g_io_channel_unix_get_fd(watch->io);
> +
> + /*
> + * If woken up by POLLERR only, and SO_ERROR is not set, ignore this
> + * event. Also disable polling for some time so that we don't busy loop
> + * if nobody is reading from the errqueue.
> + */
> +
> + if (watch->tag)
> + cond = g_source_query_unix_fd(&watch->source, watch->tag);
> + else
> + cond = 0;
> +
> + if (cond == G_IO_ERR) {
> + int err, ret;
> + socklen_t len = sizeof(err);
> +
> + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
> + if (ret == 0 && err == 0) {
> + g_source_remove_unix_fd(&watch->source, watch->tag);
> + watch->tag = NULL;
> +
> + /* io_err watches all wake up at the same time */
> + if (!io_err_watch_wakeup)
> + io_err_watch_wakeup = g_get_monotonic_time()
> + + timeout;
> +
> + g_source_set_ready_time(&watch->source,
> + io_err_watch_wakeup);
> + return TRUE;
> + }
> + }
> +
> + if (g_source_get_ready_time(&watch->source) != -1) {
> + g_assert(!watch->tag);
> + io_err_watch_wakeup = 0;
> + watch->tag = g_source_add_unix_fd(&watch->source, fd,
> + watch->events);
> + g_source_set_ready_time(&watch->source, -1);
> + }
> +
> + cond &= watch->events;
> +
> + if (cond)
> + return func(watch->io, cond, user_data);
> + else
> + return TRUE;
> +}
> +
> +static void io_err_watch_finalize(GSource *source)
> +{
> + struct io_err_watch *watch = (void *)source;
> +
> + if (watch->tag)
> + g_source_remove_unix_fd(&watch->source, watch->tag);
> +
> + g_io_channel_unref(watch->io);
> +}
> +
> +guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
> + GIOCondition events,
> + GIOFunc func, gpointer user_data,
> + GDestroyNotify notify)
> +{
> + static GSourceFuncs source_funcs = {
> + .dispatch = io_err_watch_dispatch,
> + .finalize = io_err_watch_finalize,
> + };
> + GSourceFunc callback = (void *)func;
> + struct io_err_watch *watch;
> + gint fd;
> + guint id;
> +
> + g_return_val_if_fail(!(events & (G_IO_IN | G_IO_OUT)), 0);
> + g_return_val_if_fail(func, 0);
> +
> + fd = g_io_channel_unix_get_fd(io);
> +
> + watch = (void *)g_source_new(&source_funcs,
> + sizeof(struct io_err_watch));
> +
> + watch->io = g_io_channel_ref(io);
> + watch->events = events;
> + watch->tag = g_source_add_unix_fd(&watch->source, fd, events);
> +
> + g_source_set_static_name((void *)watch, "io_glib_err_watch");
> + g_source_set_callback(&watch->source, callback, user_data, notify);
> +
> + if (priority != G_PRIORITY_DEFAULT)
> + g_source_set_priority(&watch->source, priority);
> +
> + id = g_source_attach(&watch->source, NULL);
> + g_source_unref(&watch->source);
> +
> + return id;
> +}
> +
> +guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events, GIOFunc func,
> + gpointer user_data)
> +{
> + return io_glib_add_err_watch_full(io, G_PRIORITY_DEFAULT, events,
> + func, user_data, NULL);
> +}
> diff --git a/src/shared/io-glib.h b/src/shared/io-glib.h
> new file mode 100644
> index 000000000..1db6fd468
> --- /dev/null
> +++ b/src/shared/io-glib.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
> + *
> + *
> + */
> +
> +#include <glib.h>
> +
> +guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events,
> + GIOFunc func, gpointer user_data);
> +guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
> + GIOCondition events, GIOFunc func,
> + gpointer user_data,
> + GDestroyNotify notify);
> +
> +bool io_set_use_err_watch(struct io *io, bool err_watch);
> --
> 2.44.0
>
>


--
Luiz Augusto von Dentz

2024-03-09 12:22:15

by Pauli Virtanen

[permalink] [raw]
Subject: Re: [PATCH BlueZ 1/3] shared/io-glib: add watcher to be used with TX timestamping

Hi Luiz,

pe, 2024-03-08 kello 10:12 -0500, Luiz Augusto von Dentz kirjoitti:
> Hi Pauli,
>
> On Thu, Mar 7, 2024 at 4:05 PM Pauli Virtanen <[email protected]> wrote:
> >
> > Add special implementation of fd watcher GSource for audio use.
> >
> > For audio use cases, sound server may turn on TX timestamping on a
> > socket that we are watching. In this case, we shall not consider the TX
> > timestamping POLLERR as a socket error condition, nor read the TX
> > timestamps.
> >
> > When TX timestamps appear in errqueue, switch from fd poll wait to
> > polling the fd at regular intervals. This is because unread errqueue
> > causes poll() to wake up immediately, so the mainloop cannot block on
> > that, and we have to use a timer instead with some reasonable timeout
> > for the use case.
> >
> > This avoids consuming CPU waking up on timestamps we aren't going to
> > read, and also avoids busy looping if timestamping was left on but
> > errqueue is not flushed.
> >
> > Implement this only for io-glib; it is only needed for audio use cases
> > that anyway are using glib. Uses features from GLib 2.36 (from 2013) so
> > update configure.ac also.
>
> Do we really need to enable the TX timestamping on the daemon side
> though? Or that being enabled at pipewire enables it on all fd that
> have been duplicated? I'd assume that was not the case but perhaps the
> kernel code is actually doing TX timestamping regardless if the socket
> had it enabled or not? Btw, I'd consider disabling the POLLERR
> entirely if that happens and just rely on POLLHUP/shutdown to clean
> up, or perhaps we can set up a BPF filter that drops the errqueue
> messages that way we don't have to change anything on how we handle
> POLLERR since that should prevent the daemon to be wakeup.

setsockopt(), and errqueue are socket-level things, so what you do on
one fd handle affects all the others. E.g. getsockopt() in BlueZ sees
any setsockopt() done in Pipewire, similarly for recvmsg() from RX
queue or errqueue, or poll() wakeups on POLLIN / POLLERR / etc.

SO_TIMESTAMPING is only set on Pipewire side, and kernel sends
timestamps only when it is set, but you get all its effects also on the
fd BlueZ has when it's enabled on a fd PW has, since it's the same
socket.

Since errqueue is shared, any packets recv'd from it on BlueZ side, are
not received on Pipewire side.

poll() wakes up with POLLERR when something is in errqueue, see sk-
>sk_error_queue check in bt_sock_poll. This is the same for all of
net/* afaics. The poll() wakeup is again socket-wide.

POLLERR is always enabled in poll() / epoll() / select(), this is set
in fs/eventpoll.c side. AFAIK there's no way to stop waking up on it.

E.g. removing G_IO_ERR from the g_io_add_watch flags results to busy
loop inside GLib if something is left in the errqueue.

The net/* TX timestamping design is also such that after turning off
SO_TIMESTAMPING on a socket, it will still send TX timestamps for any
packets queued before the setsockopt().

These things seem to be design decisions in the net/* TX timestamping.

***

Re BPF: it looks hard, we'd have to make epoll() wait differently for
different fds. I'm not sure if fs/eventpoll.c has necessary BPF hooks?

I don't think we can drop messages from errqueue, since it'd be socket
level and we'd like some process to read them. It also looks like
sk_filter is not used on the errqueue in net/*

Can it really be done?

***

We probably could however have a custom setsockopt(SOL_BLUETOOTH,
BT_DISABLE_ERRQUEUE_POLL) that optionally disables POLLERR for non-
empty errqueue in bt_sock_poll(), which probably should work.

Then the TX timestamps would be need to be read by periodically waking
up to flush the queue, but that'd be natural to do here since we are
anyway waking up periodically to send packets.

Unless I'm missing something, the viable options are we (i) do that, or
(ii) do like in this patch, or (iii) use ioctl or something instead.

I'd maybe go with the new setsockopt(), if that works?

>
> > ---
> >
> > Notes:
> > This was the remaining BlueZ part of the TX timestamping additions.
> > Couldn't find a better way to do it, but it has to be done, so that the
> > TX timestamping can be used for the purpose it would be added.
> >
> > I'll probably send v2 for the others in a few days.
> >
> > acinclude.m4 | 3 +-
> > configure.ac | 2 +-
> > src/shared/io-glib.c | 153 ++++++++++++++++++++++++++++++++++++++++++-
> > src/shared/io-glib.h | 20 ++++++
> > 4 files changed, 174 insertions(+), 4 deletions(-)
> > create mode 100644 src/shared/io-glib.h
> >
> > diff --git a/acinclude.m4 b/acinclude.m4
> > index 4b73a5bfc..6c36c177e 100644
> > --- a/acinclude.m4
> > +++ b/acinclude.m4
> > @@ -62,8 +62,7 @@ AC_DEFUN([COMPILER_FLAGS], [
> > with_cflags="$with_cflags -Wswitch-enum"
> > with_cflags="$with_cflags -Wformat -Wformat-security"
> > with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
> > - with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
> > - with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32"
> > + with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36"
> > fi
> > AC_SUBST([WARNING_CFLAGS], $with_cflags)
> > ])
> > diff --git a/configure.ac b/configure.ac
> > index 9ebc250cf..959a27b4d 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -72,7 +72,7 @@ AC_CHECK_LIB(dl, dlopen, dummy=yes,
> >
> > AC_CHECK_HEADERS(linux/types.h linux/if_alg.h linux/uinput.h linux/uhid.h sys/random.h)
> >
> > -PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28)
> > +PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.36)
> >
> > if (test "${enable_threads}" = "yes"); then
> > AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
> > diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c
> > index 754043db1..8b76348dd 100644
> > --- a/src/shared/io-glib.c
> > +++ b/src/shared/io-glib.c
> > @@ -13,10 +13,12 @@
> > #endif
> >
> > #include <errno.h>
> > +#include <sys/socket.h>
> >
> > #include <glib.h>
> >
> > #include "src/shared/io.h"
> > +#include "src/shared/io-glib.h"
> >
> > struct io_watch {
> > struct io *io;
> > @@ -29,11 +31,19 @@ struct io_watch {
> > struct io {
> > int ref_count;
> > GIOChannel *channel;
> > + bool err_watch;
> > struct io_watch *read_watch;
> > struct io_watch *write_watch;
> > struct io_watch *disconnect_watch;
> > };
> >
> > +struct io_err_watch {
> > + GSource source;
> > + GIOChannel *io;
> > + GIOCondition events;
> > + gpointer tag;
> > +};
> > +
> > static struct io *io_ref(struct io *io)
> > {
> > if (!io)
> > @@ -179,10 +189,17 @@ static struct io_watch *watch_new(struct io *io, GIOCondition cond,
> >
> > prio = cond == G_IO_HUP ? G_PRIORITY_DEFAULT_IDLE : G_PRIORITY_DEFAULT;
> >
> > - watch->id = g_io_add_watch_full(io->channel, prio,
> > + if (!io->err_watch)
> > + watch->id = g_io_add_watch_full(io->channel, prio,
> > cond | G_IO_ERR | G_IO_NVAL,
> > watch_callback, watch,
> > watch_destroy);
> > + else
> > + watch->id = io_glib_add_err_watch_full(io->channel, prio,
> > + cond | G_IO_ERR | G_IO_NVAL,
> > + watch_callback, watch,
> > + watch_destroy);
> > +
> > if (watch->id == 0) {
> > watch_destroy(watch);
> > return NULL;
> > @@ -250,6 +267,15 @@ bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
> > return io_set_handler(io, G_IO_HUP, callback, user_data, destroy);
> > }
> >
> > +bool io_set_use_err_watch(struct io *io, bool err_watch)
> > +{
> > + if (!io)
> > + return false;
> > +
> > + io->err_watch = err_watch;
> > + return true;
> > +}
> > +
> > ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
> > {
> > int fd;
> > @@ -278,3 +304,128 @@ bool io_shutdown(struct io *io)
> > return g_io_channel_shutdown(io->channel, TRUE, NULL)
> > == G_IO_STATUS_NORMAL;
> > }
> > +
> > +/*
> > + * GSource implementation that tolerates non-empty MSG_ERRQUEUE, without
> > + * attempting to flush it. This is intended for use with TX timestamping in
> > + * cases where someone else is reading the timestamps and we are only interested
> > + * in POLLHUP or socket errors.
> > + */
> > +
> > +static gint64 io_err_watch_wakeup;
> > +
> > +static gboolean io_err_watch_dispatch(GSource *source,
> > + GSourceFunc callback, gpointer user_data)
> > +{
> > + struct io_err_watch *watch = (void *)source;
> > + GIOFunc func = (void *)callback;
> > + gint64 timeout = 500 * G_TIME_SPAN_MILLISECOND;
> > + GIOCondition cond;
> > + int fd;
> > +
> > + if (!func)
> > + return FALSE;
> > +
> > + fd = g_io_channel_unix_get_fd(watch->io);
> > +
> > + /*
> > + * If woken up by POLLERR only, and SO_ERROR is not set, ignore this
> > + * event. Also disable polling for some time so that we don't busy loop
> > + * if nobody is reading from the errqueue.
> > + */
> > +
> > + if (watch->tag)
> > + cond = g_source_query_unix_fd(&watch->source, watch->tag);
> > + else
> > + cond = 0;
> > +
> > + if (cond == G_IO_ERR) {
> > + int err, ret;
> > + socklen_t len = sizeof(err);
> > +
> > + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
> > + if (ret == 0 && err == 0) {
> > + g_source_remove_unix_fd(&watch->source, watch->tag);
> > + watch->tag = NULL;
> > +
> > + /* io_err watches all wake up at the same time */
> > + if (!io_err_watch_wakeup)
> > + io_err_watch_wakeup = g_get_monotonic_time()
> > + + timeout;
> > +
> > + g_source_set_ready_time(&watch->source,
> > + io_err_watch_wakeup);
> > + return TRUE;
> > + }
> > + }
> > +
> > + if (g_source_get_ready_time(&watch->source) != -1) {
> > + g_assert(!watch->tag);
> > + io_err_watch_wakeup = 0;
> > + watch->tag = g_source_add_unix_fd(&watch->source, fd,
> > + watch->events);
> > + g_source_set_ready_time(&watch->source, -1);
> > + }
> > +
> > + cond &= watch->events;
> > +
> > + if (cond)
> > + return func(watch->io, cond, user_data);
> > + else
> > + return TRUE;
> > +}
> > +
> > +static void io_err_watch_finalize(GSource *source)
> > +{
> > + struct io_err_watch *watch = (void *)source;
> > +
> > + if (watch->tag)
> > + g_source_remove_unix_fd(&watch->source, watch->tag);
> > +
> > + g_io_channel_unref(watch->io);
> > +}
> > +
> > +guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
> > + GIOCondition events,
> > + GIOFunc func, gpointer user_data,
> > + GDestroyNotify notify)
> > +{
> > + static GSourceFuncs source_funcs = {
> > + .dispatch = io_err_watch_dispatch,
> > + .finalize = io_err_watch_finalize,
> > + };
> > + GSourceFunc callback = (void *)func;
> > + struct io_err_watch *watch;
> > + gint fd;
> > + guint id;
> > +
> > + g_return_val_if_fail(!(events & (G_IO_IN | G_IO_OUT)), 0);
> > + g_return_val_if_fail(func, 0);
> > +
> > + fd = g_io_channel_unix_get_fd(io);
> > +
> > + watch = (void *)g_source_new(&source_funcs,
> > + sizeof(struct io_err_watch));
> > +
> > + watch->io = g_io_channel_ref(io);
> > + watch->events = events;
> > + watch->tag = g_source_add_unix_fd(&watch->source, fd, events);
> > +
> > + g_source_set_static_name((void *)watch, "io_glib_err_watch");
> > + g_source_set_callback(&watch->source, callback, user_data, notify);
> > +
> > + if (priority != G_PRIORITY_DEFAULT)
> > + g_source_set_priority(&watch->source, priority);
> > +
> > + id = g_source_attach(&watch->source, NULL);
> > + g_source_unref(&watch->source);
> > +
> > + return id;
> > +}
> > +
> > +guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events, GIOFunc func,
> > + gpointer user_data)
> > +{
> > + return io_glib_add_err_watch_full(io, G_PRIORITY_DEFAULT, events,
> > + func, user_data, NULL);
> > +}
> > diff --git a/src/shared/io-glib.h b/src/shared/io-glib.h
> > new file mode 100644
> > index 000000000..1db6fd468
> > --- /dev/null
> > +++ b/src/shared/io-glib.h
> > @@ -0,0 +1,20 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + *
> > + * BlueZ - Bluetooth protocol stack for Linux
> > + *
> > + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
> > + *
> > + *
> > + */
> > +
> > +#include <glib.h>
> > +
> > +guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events,
> > + GIOFunc func, gpointer user_data);
> > +guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
> > + GIOCondition events, GIOFunc func,
> > + gpointer user_data,
> > + GDestroyNotify notify);
> > +
> > +bool io_set_use_err_watch(struct io *io, bool err_watch);
> > --
> > 2.44.0
> >
> >
>
>