Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes
@ 2026-10-06 10:59 dt
  2026-10-06 11:00 ` [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready dt
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: dt @ 2026-10-06 10:59 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: Luiz Augusto von Dentz

Hi Luiz,

This v2 rebases the BAP Release/reacquire fixes onto master
7edaa61403390fe1cf2f1fb6d45fdd90fe6e93b3 after the v1 CI PR expired:
https://github.com/bluez/bluez/pull/2550

The first fix prevents a late ready event from replying to Release with
an Acquire payload and losing the pending Disable request. The second
allows deferred IO recreation when a new Enable arrives before the old
CIS has finished disconnecting.

Changes since v1:
- Rebased both fixes and the regression test onto current master.
- Clarified the test's reply barriers in its description and comments.
- Added source-history-verified Fixes and Assisted-by attribution tags.

Regarding the sequencing review: the test waits for both initial Acquire
replies before Release, and for both Release replies before reacquiring.
Every following Acquire also completes before the next Release. Only
calls on different transport paths run concurrently, as in the existing
acquisition test; successive operations on one path are not queued
without waiting for its previous reply.

Fresh validation on this base:
- Current master plus the test: all three pause variants fail with
  malformed Release replies and pending-Disable cleanup crashes.
- Both fixes: all 10 selected BAP functional cases pass.
- ASan/UBSan: coordinated-set creation with SC and legacy pairing, plus
  coordinated-set acquisition, pass including teardown (3 cases).
- make check: all 35 unit-test executables pass, including 697 BAP cases.
- A separate 500 ms userspace-disconnect delay fixture isolates patch 2:
  with only patch 1, both reacquires time out after 26 seconds; with both
  fixes, they complete in about 375 ms and both transports become active.
  This diagnostic instrumentation is excluded from the series.

The v1 functional job failed in coordinated-set cleanup, while its new
resume cases passed. This base includes eb82d81bf26b and 8a1c53517399,
which address the recorded adapter-list and BAP-session lifetime stacks;
the fresh sanitizer checks above cover those previously failing paths.

These are virtual-controller lifecycle tests, not acoustic, full-duplex
range, or physical-earbud validation. The ordinary resume test does not
force the delayed-disconnect ordering exercised by the separate fixture.

Derek


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready
  2026-10-06 10:59 [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes dt
@ 2026-10-06 11:00 ` dt
  2026-10-06 16:43   ` BAP transport Release and reacquire fixes bluez.test.bot
  2026-10-06 11:00 ` [PATCH BlueZ v2 2/3] bap: Recreate stream IO after an early Enable dt
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: dt @ 2026-10-06 11:00 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: Luiz Augusto von Dentz

From: Derek Tern <1263187-dt@users.noreply.gitlab.freedesktop.org>

A late stream-ready event can reach bap_resume_complete() after Release
has queued Disable. It then replies to Release with an Acquire payload
and drops the Disable request ID. Client disconnection can subsequently
free the owner while the Disable callback still refers to it.

Only complete pending Acquire/TryAcquire requests from this path. Leave
Release and its request ID for the Disable callback or cancellation.

Fixes: 466fce0209a3 ("transport: handle BAP Enabling state correctly when resuming")
Assisted-by: Codex:GPT-6
---
 profiles/audio/transport.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 8cfc98a52..e001f7315 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -1965,8 +1965,16 @@ static void bap_resume_complete(struct media_transport *transport)
 	if (!owner)
 		return;
 
-	if (owner->pending)
+	if (owner->pending) {
+		const char *member;
+
+		member = dbus_message_get_member(owner->pending->msg);
+		/* A late ready event must not complete a pending Release. */
+		if (strcmp(member, "Acquire") && strcmp(member, "TryAcquire"))
+			return;
+
 		owner->pending->id = 0;
+	}
 
 	if (transport->fd < 0) {
 		media_transport_remove_owner(transport);
-- 
2.56.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v2 2/3] bap: Recreate stream IO after an early Enable
  2026-10-06 10:59 [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes dt
  2026-10-06 11:00 ` [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready dt
@ 2026-10-06 11:00 ` dt
  2026-10-06 11:01 ` [PATCH BlueZ v2 3/3] test: Cover BAP transport release and reacquire dt
  2026-10-07 18:40 ` [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes patchwork-bot+bluetooth
  3 siblings, 0 replies; 6+ messages in thread
From: dt @ 2026-10-06 11:00 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: Luiz Augusto von Dentz

From: Derek Tern <1263187-dt@users.noreply.gitlab.freedesktop.org>

When reacquiring a transport, the stream can reach Enabling before its
previous CIS finishes disconnecting. Once teardown completes, IO
recreation accepts only QoS, while CIS enabling requires existing IO.
The pending Acquire consequently stalls.

Allow deferred IO recreation in Enabling too. Keep the existing CIG-busy
checks and recreate all IO before enabling CISes.

Fixes: 7feff47a9fbe ("bap: delay QoS & IO creation if CIG is busy or setups configuring")
Assisted-by: Codex:GPT-6
---
 profiles/audio/bap.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 928796e1e..a0e0ba60d 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -2576,12 +2576,17 @@ static void update_cig_setup_io(void *data, void *match_data)
 	struct update_cig_data *info = match_data;
 	struct bt_bap_stream *stream = setup->stream;
 	struct bt_bap_qos *qos = bt_bap_stream_get_qos(stream);
+	uint8_t state;
 
 	if (qos && qos->ucast.cig_id != info->cig)
 		return;
 	if (!setup->want_io || !stream || setup->io || setup->closing)
 		return;
-	if (bt_bap_stream_get_state(stream) != BT_BAP_STREAM_STATE_QOS)
+
+	/* Enable may complete while the previous IO is still disconnecting. */
+	state = bt_bap_stream_get_state(stream);
+	if (state != BT_BAP_STREAM_STATE_QOS &&
+					state != BT_BAP_STREAM_STATE_ENABLING)
 		return;
 
 	DBG("%p", setup);
-- 
2.56.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v2 3/3] test: Cover BAP transport release and reacquire
  2026-10-06 10:59 [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes dt
  2026-10-06 11:00 ` [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready dt
  2026-10-06 11:00 ` [PATCH BlueZ v2 2/3] bap: Recreate stream IO after an early Enable dt
@ 2026-10-06 11:01 ` dt
  2026-10-07 18:40 ` [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes patchwork-bot+bluetooth
  3 siblings, 0 replies; 6+ messages in thread
From: dt @ 2026-10-06 11:01 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: Luiz Augusto von Dentz

From: Derek Tern <1263187-dt@users.noreply.gitlab.freedesktop.org>

Acquire both unicast transports, close their sockets and release them
through the same D-Bus owner. Require an empty Release reply so a late
ready callback cannot pass unnoticed by returning an Acquire payload.

Wait for both Acquire replies before Release, and for both Release
replies before the next Acquire. Only requests on different transport
paths are concurrent; no path has successive operations queued without
waiting for its preceding reply.

Repeat three times with immediate, 100 ms and one-second reacquisition.
Check Acquire reply types and active transport states. Close returned
FDs even when a reply is malformed.

Assisted-by: Codex:GPT-6
---
 doc/functional-bap.rst      |  23 ++++++++
 test/functional/test_bap.py | 103 ++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)

diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 74a6604ad..42fa7fda8 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -105,6 +105,29 @@ test_bap_unicast_transport_acquire
 	be established, as `bluetoothd` sets up the ISO listener on its own
 	when the stream is enabled.
 
+test_bap_unicast_transport_resume[0|0.1|1.0]
+------------------------------------------
+
+:Setup: As above, with both transports already created.
+
+:Steps:
+	1. Acquire both transports through one D-Bus connection and wait for
+	   both replies.
+	2. Close both acquired sockets and call ``MediaTransport1.Release``
+	   for both transports. Requests on different paths may be in flight
+	   together; wait for both Release replies before the next step.
+	3. Reacquire immediately or after 0.1 or 1 second, and wait for both
+	   Acquire replies before continuing.
+	4. Repeat the release/reacquire cycle three times.
+
+:Expected: Each ``Release`` succeeds with an empty reply. Every ``Acquire``
+	returns an FD and MTUs, and both transports become active again.
+
+:Notes: D-Bus is used directly to check the Release reply signature and
+	close the client sockets before releasing the transports. A late ready
+	callback must not answer a pending Release with an Acquire payload.
+	These tests do not force ISO disconnection to overlap the next Enable.
+
 test_bap_unicast_reconfigure_metadata[empty|media]
 ------------------------------------------------
 
diff --git a/test/functional/test_bap.py b/test/functional/test_bap.py
index 8d0b4489d..c4bd5fc6a 100644
--- a/test/functional/test_bap.py
+++ b/test/functional/test_bap.py
@@ -4,6 +4,7 @@
 Tests for BAP (LE Audio) using bluetoothctl in VM instances
 """
 
+import os
 import threading
 import time
 import warnings
@@ -282,6 +283,108 @@ def test_bap_unicast_transport_acquire(unicast_hosts):
     )
 
 
+def resume_transports(paths, delay):
+    """Wait for each operation on all CIS before starting the next one."""
+    bus = None
+    interfaces = []
+    fds = {}
+
+    @mainloop_wrap
+    def setup():
+        nonlocal bus, interfaces
+        bus = get_dbus(private=True)
+        interfaces = [
+            dbus.Interface(
+                bus.get_object("org.bluez", path, introspect=False),
+                "org.bluez.MediaTransport1",
+            )
+            for path in paths
+        ]
+
+    def operation(method):
+        done = threading.Event()
+        replies = []
+        errors = []
+
+        def finished(index, error=None, values=()):
+            replies.append(index)
+            if error is not None:
+                errors.append((paths[index], str(error)))
+            elif method == "Acquire" and tuple(type(value) for value in values) == (
+                dbus.types.UnixFd,
+                dbus.UInt16,
+                dbus.UInt16,
+            ):
+                fds[index] = values[0].take()
+            elif method == "Acquire" or values:
+                # Release has an empty reply: a late ready callback must not
+                # answer it with an Acquire payload. Dispose of any stray FDs.
+                for value in values:
+                    if isinstance(value, dbus.types.UnixFd):
+                        os.close(value.take())
+                errors.append((paths[index], f"invalid {method} reply"))
+            if len(replies) == len(paths):
+                done.set()
+
+        @mainloop_wrap
+        def submit():
+            for index, interface in enumerate(interfaces):
+                if method == "Release":
+                    os.close(fds.pop(index))
+                getattr(interface, method)(
+                    reply_handler=lambda *values, i=index: finished(i, values=values),
+                    error_handler=lambda error, i=index: finished(i, error=error),
+                    timeout=DEFAULT_TIMEOUT,
+                )
+
+        submit()
+        # Different transport paths may have requests in flight together, but
+        # wait for every reply before another operation uses any of them.
+        assert done.wait(DEFAULT_TIMEOUT + 1), f"{method} did not complete"
+        assert not errors, (method, errors)
+
+    @mainloop_wrap
+    def check_active():
+        objects = dbus.Interface(
+            bus.get_object("org.bluez", "/"),
+            "org.freedesktop.DBus.ObjectManager",
+        ).GetManagedObjects()
+        for path in paths:
+            assert objects[path]["org.bluez.MediaTransport1"]["State"] == "active"
+
+    @mainloop_wrap
+    def cleanup():
+        for fd in fds.values():
+            os.close(fd)
+        fds.clear()
+        if bus is not None:
+            bus.close()
+
+    try:
+        setup()
+        operation("Acquire")
+        check_active()
+        for _ in range(3):
+            # Acquire has completed on both paths before Release starts.
+            # Release also waits for both replies before the next Acquire.
+            operation("Release")
+            if delay:
+                time.sleep(delay)
+            operation("Acquire")
+            check_active()
+    finally:
+        cleanup()
+
+
+@unicast_host_config
+@pytest.mark.parametrize("delay", [0, 0.1, 1.0])
+def test_bap_unicast_transport_resume(unicast_hosts, delay):
+    host0, host1, initiator, acceptor = unicast_hosts
+    transports = expect_transports(initiator)
+    expect_transports(acceptor)
+    host0.call(resume_transports, transports, delay)
+
+
 def clear_remote_transports(remote):
     """Release the peer ASEs while retaining the connected GATT session."""
     done = threading.Event()
-- 
2.56.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: BAP transport Release and reacquire fixes
  2026-10-06 11:00 ` [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready dt
@ 2026-10-06 16:43   ` bluez.test.bot
  0 siblings, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2026-10-06 16:43 UTC (permalink / raw)
  To: linux-bluetooth, derektern

[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]

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/series/1180235/

---Test result---

Test Summary:
CheckPatch                    PASS      1.23 seconds
GitLint                       PASS      1.21 seconds
BuildEll                      PASS      19.40 seconds
BluezMake                     PASS      392.20 seconds
MakeCheck                     PASS      14.09 seconds
MakeDistcheck                 PASS      138.44 seconds
CheckValgrind                 PASS      229.50 seconds
CheckSmatch                   PASS      287.01 seconds
bluezmakeextell               PASS      92.95 seconds
TestFunctional                PASS      1139.30 seconds
IncrementalBuild              PASS      397.83 seconds
ScanBuild                     PASS      1103.29 seconds



https://github.com/bluez/bluez/pull/2629

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes
  2026-10-06 10:59 [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes dt
                   ` (2 preceding siblings ...)
  2026-10-06 11:01 ` [PATCH BlueZ v2 3/3] test: Cover BAP transport release and reacquire dt
@ 2026-10-07 18:40 ` patchwork-bot+bluetooth
  3 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+bluetooth @ 2026-10-07 18:40 UTC (permalink / raw)
  To: dt; +Cc: linux-bluetooth, luiz.dentz

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Tue, 06 Oct 2026 10:59:41 +0000 you wrote:
> Hi Luiz,
> 
> This v2 rebases the BAP Release/reacquire fixes onto master
> 7edaa61403390fe1cf2f1fb6d45fdd90fe6e93b3 after the v1 CI PR expired:
> https://github.com/bluez/bluez/pull/2550
> 
> The first fix prevents a late ready event from replying to Release with
> an Acquire payload and losing the pending Disable request. The second
> allows deferred IO recreation when a new Enable arrives before the old
> CIS has finished disconnecting.
> 
> [...]

Here is the summary with links:
  - [BlueZ,v2,1/3] transport: Preserve Release on late BAP ready
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=d852faf0b1d1
  - [BlueZ,v2,2/3] bap: Recreate stream IO after an early Enable
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=47f783ff5ba4
  - [BlueZ,v2,3/3] test: Cover BAP transport release and reacquire
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=4b44e0726c6f

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-07 18:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-06 10:59 [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes dt
2026-10-06 11:00 ` [PATCH BlueZ v2 1/3] transport: Preserve Release on late BAP ready dt
2026-10-06 16:43   ` BAP transport Release and reacquire fixes bluez.test.bot
2026-10-06 11:00 ` [PATCH BlueZ v2 2/3] bap: Recreate stream IO after an early Enable dt
2026-10-06 11:01 ` [PATCH BlueZ v2 3/3] test: Cover BAP transport release and reacquire dt
2026-10-07 18:40 ` [PATCH BlueZ v2 0/3] BAP transport Release and reacquire fixes patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox