| 0/0 |
2026/06/01 03:47 |
flow |
assessment-security |
53m
Results: map[DenialOfService:false Explanation:Based on the analysis of the kernel source code and the provided crash report,
here is the assessment of the bug:
### Root Cause Analysis
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered in
`__queue_work()` when a work item is queued on a workqueue that is currently
being destroyed or drained (`wq->flags & (__WQ_DESTROYING | __WQ_DRAINING)`).
In this specific report, the warning is triggered by `l2cap_info_timeout()`,
which is the callback for the `conn->info_timer` delayed work. This timer is
scheduled when the host sends an `L2CAP_INFO_REQ` to a remote device. If the
remote device does not respond in time, the timer fires, and
`l2cap_info_timeout()` attempts to queue `hdev->tx_work` on `hdev->workqueue` to
send further commands.
The race condition occurs when the HCI device is being closed or unregistered
(e.g., via `hciconfig hci0 down`, closing a `/dev/vhci` file descriptor, or
physically unplugging a USB Bluetooth dongle). The teardown process calls
`hci_dev_do_close()`, which in turn calls `hci_dev_close_sync()`.
Inside `hci_dev_close_sync()`, the code first drains the workqueue:
```c
/* Avoid potential lockdep warnings from the *_flush() calls by
* ensuring the workqueue is empty up front.
*/
drain_workqueue(hdev->workqueue);
```
This sets the `__WQ_DRAINING` flag on the workqueue. Later in the same function,
it flushes the connections and cancels their timers:
```c
hci_conn_hash_flush(hdev);
```
Because the timers (like `conn->info_timer`) are not cancelled until *after*
`drain_workqueue()` is called, it is possible for `conn->info_timer` to fire
exactly while `drain_workqueue()` is running. When `l2cap_info_timeout()`
executes, it calls `queue_work(hdev->workqueue, &hdev->tx_work)`. Since the
workqueue has the `__WQ_DRAINING` flag set, the workqueue subsystem rejects the
work and emits the `WARN_ONCE`.
### Security Impact
**Not Exploitable / No Denial of Service**
This is a benign warning. The workqueue subsystem safely handles this condition
by returning early without queueing the work (as seen in `__queue_work()`):
```c
if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue
%ps on wq %s\n", ...))) {
...
return;
}
```
Since the work is not queued, the packet is simply dropped. This is a perfectly
safe behavior, as the device is in the process of being shut down anyway. The
associated `skb` is safely purged later during the connection cleanup
(`hci_chan_list_flush()`), so there are no memory leaks, use-after-free
vulnerabilities, or deadlocks.
The bug cannot be triggered by unprivileged users, as bringing down the HCI
interface requires `CAP_NET_ADMIN`, and `/dev/vhci` is restricted to root. It
can technically be triggered by a malicious USB peripheral that simulates a
disconnect at the exact right time, but it only results in a harmless kernel
warning. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/06/01 03:47 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/06/01 03:47 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/696b1bd2544595eb0539dcbe2c0c1c9465478908]
|
| 3/1 |
2026/06/01 03:49 |
action |
kernel-builder |
28m
Results: map[KernelObj:/app/workdir/cache/build/93f080d70c348fc89e5c7035b0db1905678e1f02]
|
| 4/1 |
2026/06/01 04:17 |
action |
codesearch-prepare |
13m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/06/01 04:31 |
agent |
expert |
9m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
workqueue: cannot queue hci_tx_work on wq hci0
WARNING: kernel/workqueue.c:2298 at __queue_work+0xd3f/0x1040 kernel/workqueue.c:2296, CPU#0: kworker/0:5/5313
Modules linked in:
CPU: 0 UID: 0 PID: 5313 Comm: kworker/0:5 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: events l2cap_info_timeout
RIP: 0010:__queue_work+0xd67/0x1040 kernel/workqueue.c:2296
Code: a6 0e 49 8d 7d 18 48 89 f8 48 c1 e8 03 42 80 3c 20 00 74 05 e8 ba 5d a5 00 49 8b 75 18 49 81 c7 70 01 00 00 4c 89 f7 4c 89 fa <67> 48 0f b9 3a 4c 89 e8 48 c1 e8 03 42 80 3c 20 00 74 08 4c 89 ef
RSP: 0018:ffffc9000f02f820 EFLAGS: 00010082
RAX: 1ffff1100257f18a RBX: 0000000000000008 RCX: ffff88801f5f8000
RDX: ffff888043287170 RSI: ffffffff8a9e2d90 RDI: ffffffff9033e950
RBP: 0000000000000020 R08: ffff888012bf8c3f R09: 1ffff1100257f187
R10: dffffc0000000000 R11: ffffed100257f188 R12: dffffc0000000000
R13: ffff888012bf8c38 R14: ffffffff9033e950 R15: ffff888043287170
FS: 0000000000000000(0000) GS:ffff88808c87f000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f4a445909c0 CR3: 0000000012e1c000 CR4: 0000000000352ef0
Call Trace:
<TASK>
queue_work_on+0x106/0x1d0 kernel/workqueue.c:2444
l2cap_conn_start+0x5c9/0xf20 net/bluetooth/l2cap_core.c:1575
l2cap_info_timeout+0x68/0xa0 net/bluetooth/l2cap_core.c:1685
process_one_work kernel/workqueue.c:3314 [inline]
process_scheduled_works+0xb5d/0x1860 kernel/workqueue.c:3397
worker_thread+0xa53/0xfc0 kernel/workqueue.c:3478
kthread+0x389/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess), 2 bytes skipped:
0: 49 8d 7d 18 lea 0x18(%r13),%rdi
4: 48 89 f8 mov %rdi,%rax
7: 48 c1 e8 03 shr $0x3,%rax
b: 42 80 3c 20 00 cmpb $0x0,(%rax,%r12,1)
10: 74 05 je 0x17
12: e8 ba 5d a5 00 call 0xa55dd1
17: 49 8b 75 18 mov 0x18(%r13),%rsi
1b: 49 81 c7 70 01 00 00 add $0x170,%r15
22: 4c 89 f7 mov %r14,%rdi
25: 4c 89 fa mov %r15,%rdx
* 28: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2d: 4c 89 e8 mov %r13,%rax
30: 48 c1 e8 03 shr $0x3,%rax
34: 42 80 3c 20 00 cmpb $0x0,(%rax,%r12,1)
39: 74 08 je 0x43
3b: 4c 89 ef mov %r13,%rdi
Reply: Based on the analysis of the kernel source code and the provided crash report, here is the assessment of the bug:
### Root Cause Analysis
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered in `__queue_work()` when a work item is queued on a workqueue that is currently being destroyed or drained (`wq->flags & (__WQ_DESTROYING | __WQ_DRAINING)`).
In this specific report, the warning is triggered by `l2cap_info_timeout()`, which is the callback for the `conn->info_timer` delayed work. This timer is scheduled when the host sends an `L2CAP_INFO_REQ` to a remote device. If the remote device does not respond in time, the timer fires, and `l2cap_info_timeout()` attempts to queue `hdev->tx_work` on `hdev->workqueue` to send further commands.
The race condition occurs when the HCI device is being closed or unregistered (e.g., via `hciconfig hci0 down`, closing a `/dev/vhci` file descriptor, or physically unplugging a USB Bluetooth dongle). The teardown process calls `hci_dev_do_close()`, which in turn calls `hci_dev_close_sync()`.
Inside `hci_dev_close_sync()`, the code first drains the workqueue:
```c
/* Avoid potential lockdep warnings from the *_flush() calls by
* ensuring the workqueue is empty up front.
*/
drain_workqueue(hdev->workqueue);
```
This sets the `__WQ_DRAINING` flag on the workqueue. Later in the same function, it flushes the connections and cancels their timers:
```c
hci_conn_hash_flush(hdev);
```
Because the timers (like `conn->info_timer`) are not cancelled until *after* `drain_workqueue()` is called, it is possible for `conn->info_timer` to fire exactly while `drain_workqueue()` is running. When `l2cap_info_timeout()` executes, it calls `queue_work(hdev->workqueue, &hdev->tx_work)`. Since the workqueue has the `__WQ_DRAINING` flag set, the workqueue subsystem rejects the work and emits the `WARN_ONCE`.
### Security Impact
**Not Exploitable / No Denial of Service**
This is a benign warning. The workqueue subsystem safely handles this condition by returning early without queueing the work (as seen in `__queue_work()`):
```c
if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n", ...))) {
...
return;
}
```
Since the work is not queued, the packet is simply dropped. This is a perfectly safe behavior, as the device is in the process of being shut down anyway. The associated `skb` is safely purged later during the connection cleanup (`hci_chan_list_flush()`), so there are no memory leaks, use-after-free vulnerabilities, or deadlocks.
The bug cannot be triggered by unprivileged users, as bringing down the HCI interface requires `CAP_NET_ADMIN`, and `/dev/vhci` is restricted to root. It can technically be triggered by a malicious USB peripheral that simulates a disconnect at the exact right time, but it only results in a harmless kernel warning.
|
| 6/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 7046
output: 42
thoughts: 188
Thoughts: **Debugging a Workqueue Issue in the Bluetooth Subsystem**
Okay, so I've got a warning cropping up in `__queue_work` within `kernel/workqueue.c`. The message is pretty explicit: "workqueue: cannot queue hci_tx_work on wq hci0". This is triggered by a call to `queue_work_on`, which gives us a bit of a lead. I can see the call trace is pointing to `queue_work_on` then `l2cap_conn_start` and `l2cap_info_timeout`.
My initial thought is to dive into the `l2cap_info_timeout` and `l2cap_conn_start` functions in `net/bluetooth/l2cap_core.c`. Clearly, there is something happening there related to this specific workqueue ("wq hci0") when a connection start or an info timeout event occurs. The key here is to understand why a work item associated with HCI transmission ("hci_tx_work") is being scheduled on the wrong workqueue. Is it a misconfiguration or perhaps a race condition? I need to figure out what's leading to that call being made on the wrong queue. Looks like I'll need to go spelunking in the Bluetooth L2CAP code.
|
| 7/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_info_timeout]
Results: map[SourceCode:1676: static void l2cap_info_timeout(struct work_struct *work)
1677: {
1678: struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
1679: info_timer.work);
1680:
1681: conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
1682: conn->info_ident = 0;
1683:
1684: mutex_lock(&conn->lock);
1685: l2cap_conn_start(conn);
1686: mutex_unlock(&conn->lock);
1687: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 8/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 7772
output: 42
thoughts: 14
Thoughts: Okay, let's dive into this `l2cap_conn_start` function. I need to understand its role within the L2CAP connection establishment process. I'm already visualizing the architecture β it's likely a key entry point for initiating a new connection, but I want to confirm that. Is it handling the initial signaling, the allocation of resources, or perhaps the final handshake with the peer? I'm curious about the specific parameters it takes, and how it validates those parameters. Are there checks for available resources, or does it defer those checks to a subsequent step? Does it leverage any other existing L2CAP primitives or is it a fully self-contained function? I'm mentally tracing the possible code paths to look for potential bottlenecks, or areas that could benefit from optimization. Understanding the nuances of this function's logic is crucial for debugging connection issues, performance analysis, or any future modifications to the L2CAP layer. I need to figure out what it does in each state the connection could be in.
|
| 9/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_conn_start]
Results: map[SourceCode:1517: /* ---- L2CAP connections ---- */
1518: static void l2cap_conn_start(struct l2cap_conn *conn)
1519: {
1520: struct l2cap_chan *chan, *tmp;
1521:
1522: BT_DBG("conn %p", conn);
1523:
1524: list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
1525: l2cap_chan_lock(chan);
1526:
1527: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
1528: l2cap_chan_ready(chan);
1529: l2cap_chan_unlock(chan);
1530: continue;
1531: }
1532:
1533: if (chan->state == BT_CONNECT) {
1534: if (!l2cap_chan_check_security(chan, true) ||
1535: !__l2cap_no_conn_pending(chan)) {
1536: l2cap_chan_unlock(chan);
1537: continue;
1538: }
1539:
1540: if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
1541: && test_bit(CONF_STATE2_DEVICE,
1542: &chan->conf_state)) {
1543: l2cap_chan_close(chan, ECONNRESET);
1544: l2cap_chan_unlock(chan);
1545: continue;
1546: }
1547:
1548: if (l2cap_check_enc_key_size(conn->hcon, chan))
1549: l2cap_start_connection(chan);
1550: else
1551: l2cap_chan_close(chan, ECONNREFUSED);
1552:
1553: } else if (chan->state == BT_CONNECT2) {
1554: struct l2cap_conn_rsp rsp;
1555: char buf[128];
1556: rsp.scid = cpu_to_le16(chan->dcid);
1557: rsp.dcid = cpu_to_le16(chan->scid);
1558:
1559: if (l2cap_chan_check_security(chan, false)) {
1560: if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
1561: rsp.result = cpu_to_le16(L2CAP_CR_PEND);
1562: rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
1563: chan->ops->defer(chan);
1564:
1565: } else {
1566: l2cap_state_change(chan, BT_CONFIG);
1567: rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
1568: rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
1569: }
1570: } else {
1571: rsp.result = cpu_to_le16(L2CAP_CR_PEND);
1572: rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
1573: }
1574:
1575: l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
1576: sizeof(rsp), &rsp);
1577:
1578: if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
1579: rsp.result != L2CAP_CR_SUCCESS) {
1580: l2cap_chan_unlock(chan);
1581: continue;
1582: }
1583:
1584: set_bit(CONF_REQ_SENT, &chan->conf_state);
1585: l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
1586: l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
1587: chan->num_conf_req++;
1588: }
1589:
1590: l2cap_chan_unlock(chan);
1591: }
1592: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 10/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 9329
output: 39
thoughts: 92
Thoughts: Okay, let's break this down.
**Investigating a Workqueue Warning**
Hmm, I'm seeing a warning in the kernel log: "workqueue: cannot queue hci_tx_work on wq hci0". Right away, I'm thinking, "Okay, so `hci_tx_work` is trying to be scheduled, but the workqueue `hci0` is probably in an invalid state. Maybe it's being destroyed, or there's some kind of resource conflict?". My first instinct is to trace what is causing the error. I'll need more information, but this is a starting point.
Ah, the warning specifically points to `kernel/workqueue.c:2298`. That's extremely helpful! Now I know exactly where to start digging in the code. I'll have a look at `kernel/workqueue.c` around that line number β 2298 and the surrounding context. That should help to illuminate exactly what the system is doing, or trying to do, when this error crops up, and what potential issues exist.
|
| 11/2 |
2026/06/01 04:31 |
tool |
read-file |
0m
Error:
Args: map[File:kernel/workqueue.c FirstLine:2280 LineCount:50]
|
| 12/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 9718
output: 35
thoughts: 8
Thoughts: Okay, here's the summary, assuming I'm the one having these thoughts and that I'm an expert in the field:
**Initial Data Type Assessment**
Alright, looking at this code or data structure... my first thought is, "FirstLine needs to be an integer." There's just no way it's going to work reliably as anything else. Everything is pointing to a numerical representation, probably a row or line number, and a string or other non-integer type would introduce so many problems further down the line. It would cause conversion errors and unexpected behavior in any logic that depends on it. This is a basic, fundamental assessment I have to make before doing anything else.
|
| 13/2 |
2026/06/01 04:31 |
tool |
read-file |
0m
Args: map[File:kernel/workqueue.c FirstLine:2280 LineCount:50]
Results: map[Contents:2280: unsigned int work_flags;
2281: unsigned int req_cpu = cpu;
2282:
2283: /*
2284: * While a work item is PENDING && off queue, a task trying to
2285: * steal the PENDING will busy-loop waiting for it to either get
2286: * queued or lose PENDING. Grabbing PENDING and queueing should
2287: * happen with IRQ disabled.
2288: */
2289: lockdep_assert_irqs_disabled();
2290:
2291: /*
2292: * For a draining wq, only works from the same workqueue are
2293: * allowed. The __WQ_DESTROYING helps to spot the issue that
2294: * queues a new work item to a wq after destroy_workqueue(wq).
2295: */
2296: if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
2297: WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n",
2298: work->func, wq->name))) {
2299: struct work_offq_data offqd;
2300:
2301: /*
2302: * State on entry: PENDING is set, work is off-queue (no
2303: * insert_work() has run).
2304: *
2305: * Returning without clearing PENDING would leave the work
2306: * in a weird state (PENDING=1, PWQ=0, entry empty)
2307: */
2308: work_offqd_unpack(&offqd, *work_data_bits(work));
2309: set_work_pool_and_clear_pending(work, offqd.pool_id,
2310: work_offqd_pack_flags(&offqd));
2311: return;
2312: }
2313: rcu_read_lock();
2314: retry:
2315: /* pwq which will be used unless @work is executing elsewhere */
2316: if (req_cpu == WORK_CPU_UNBOUND) {
2317: if (wq->flags & WQ_UNBOUND)
2318: cpu = wq_select_unbound_cpu(raw_smp_processor_id());
2319: else
2320: cpu = raw_smp_processor_id();
2321: }
2322:
2323: pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu));
2324: pool = pwq->pool;
2325:
2326: /*
2327: * If @work was previously on a different pool, it might still be
2328: * running there, in which case the work needs to be queued on that
2329: * pool to guarantee non-reentrancy.
]
|
| 14/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 10738
output: 42
thoughts: 196
Thoughts: **Analyzing a Workqueue Warning**
Okay, so I've hit this `WARN_ONCE` message: "workqueue: cannot queue %ps on wq %s". Right off the bat, I know this is a concurrency issue with workqueues. The kernel's telling me I'm trying to add a new work item when the workqueue is already in a bad state, specifically being destroyed or drained. The warning is triggered when `wq->flags` has either `__WQ_DESTROYING` or `__WQ_DRAINING` set. That's my immediate point of focus.
Let's drill down to the specifics of *this* instance. The work item in question is `hci_tx_work`, and it's being queued on the `hci0` workqueue. A quick scan of the call trace reveals a chain that starts with `l2cap_info_timeout` and flows through `l2cap_conn_start`, `l2cap_send_cmd`, `hci_send_acl`, `hci_queue_acl`, and finally lands at `queue_work(hdev->workqueue, &hdev->tx_work)`. So the problem starts with the L2CAP layer.
My next step is to examine the `l2cap_send_cmd` function. That's where I need to start my investigation. I'll be looking for where and why it might be triggering a send down to HCI when the connection is going away. I need to understand the conditions that lead to this work item being queued and, critically, why the workqueue might be undergoing destruction or draining *at the same time*. This will almost certainly be a race condition.
|
| 15/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_send_cmd]
Results: map[SourceCode: 974: static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
975: void *data)
976: {
977: struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
978: u8 flags;
979:
980: BT_DBG("code 0x%2.2x", code);
981:
982: if (!skb)
983: return;
984:
985: /* Use NO_FLUSH if supported or we have an LE link (which does
986: * not support auto-flushing packets) */
987: if (lmp_no_flush_capable(conn->hcon->hdev) ||
988: conn->hcon->type == LE_LINK)
989: flags = ACL_START_NO_FLUSH;
990: else
991: flags = ACL_START;
992:
993: bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
994: skb->priority = HCI_PRIO_MAX;
995:
996: l2cap_send_acl(conn, skb, flags);
997: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 16/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 11754
output: 42
thoughts: 13
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `l2cap_send_acl`**
Okay, time to dissect `l2cap_send_acl`. This function is fundamental to the ACL connection process within the Bluetooth L2CAP layer. I need to understand its role in data transmission and how it interfaces with the lower layers of the stack. I'll need to remember all the nuances of ACL connections, particularly the data segmentation and reassembly. I wonder how it handles flow control and error checking at the ACL level, specifically within the Bluetooth protocols I'm working with. This is going to be about understanding the critical path for delivering user data across the air. I'll need to trace the data flow from the application, through L2CAP, and then down to the HCI level. Let's see how this function handles the complexities of asynchronous connection-oriented links and what optimizations, if any, are implemented.
|
| 17/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_send_acl]
Results: map[SourceCode: 964: static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
965: u8 flags)
966: {
967: /* Check if the hcon still valid before attempting to send */
968: if (hci_conn_valid(conn->hcon->hdev, conn->hcon))
969: hci_send_acl(conn->hchan, skb, flags);
970: else
971: kfree_skb(skb);
972: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 18/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 12199
output: 38
thoughts: 11
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**Digging into `hci_send_acl`**
Alright, time to dive in and take a look at `hci_send_acl`. This function... it's a critical piece of the puzzle, isn't it? It's where the rubber meets the road, where our higher-level Bluetooth stack actually pushes ACL data down to the HCI layer. So, I need to understand its mechanics, *intimately*. I want to see if there are any obvious bottlenecks, inefficiencies, or, frankly, potential gotchas lurking within. I need to know how it handles fragmentation, flow control, retransmissions, all of it. Specifically, I'm thinking about how it interacts with the underlying transport layer. Does it utilize a hardware queue? How are buffer descriptors being managed? Are there any potential race conditions I should be aware of, especially considering multi-threaded access in a real-world scenario? I want to trace the data flow.
|
| 19/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_send_acl]
Results: map[SourceCode:3275: void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
3276: {
3277: struct hci_dev *hdev = chan->conn->hdev;
3278:
3279: BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
3280:
3281: hci_queue_acl(chan, &chan->data_q, skb, flags);
3282:
3283: queue_work(hdev->workqueue, &hdev->tx_work);
3284: }
SourceFile:net/bluetooth/hci_core.c]
|
| 20/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 12683
output: 43
thoughts: 82
Thoughts: Okay, let me break this down.
**Debugging a Potential Workqueue Destruction Race Condition**
Alright, so I'm seeing this warning related to `queue_work` and a disappearing workqueue. Specifically, the function `hci_send_acl` is firing off a `queue_work` call, targeted at `hdev->tx_work` and `hdev->workqueue`. The warning suggests that the `hdev->workqueue` is being destroyed *while* we're trying to queue work on it, which is obviously problematic.
My immediate thought is to trace the lifecycle of this `hdev->workqueue`. I need to figure out *when* `hdev->workqueue` is getting destroyed in the first place. That means I need to search the code for any instances of `destroy_workqueue` being called on `hdev->workqueue` . I need to pinpoint the exact sequence of events that could lead to this race condition. I bet if I find where that destroy is happening I can understand how this warning is happening.
|
| 21/2 |
2026/06/01 04:31 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeSnippetLines:2 Name:destroy_workqueue]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__alloc_workqueue SourceFile:kernel/workqueue.c SourceLine:5919 SourceSnippet:5917: apply_wqattrs_unlock();
5918: err_destroy:
5919: destroy_workqueue(wq);
5920: return NULL;
5921: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__destroy SourceFile:drivers/md/dm-cache-target.c SourceLine:1957 SourceSnippet:1955:
1956: if (cache->wq)
1957: destroy_workqueue(cache->wq);
1958:
1959: if (cache->dirty_bitset)
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__drmm_workqueue_release SourceFile:drivers/gpu/drm/drm_managed.c SourceLine:319 SourceSnippet: 317: struct workqueue_struct *wq = res;
318:
319: destroy_workqueue(wq);
320: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__ext4_fill_super SourceFile:fs/ext4/super.c SourceLine:5764 SourceSnippet:5762: ext4_msg(sb, KERN_ERR, "mount failed");
5763: if (EXT4_SB(sb)->rsv_conversion_wq)
5764: destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
5765: failed_mount_wq:
5766: ext4_xattr_destroy_cache(sbi->s_ea_inode_cache);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__free_client SourceFile:fs/nfsd/nfs4state.c SourceLine:2419 SourceSnippet:2417:
2418: free_svc_cred(&clp->cl_cred);
2419: destroy_workqueue(clp->cl_callback_wq);
2420: kfree(clp->cl_ownerstr_hashtbl);
2421: kfree(clp->cl_name.data);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__free_workqueue_destroy SourceFile:drivers/firewire/core-card.c SourceLine:596 SourceSnippet: 596: DEFINE_FREE(workqueue_destroy, struct workqueue_struct *, if (_T) destroy_workqueue(_T))
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__padata_free SourceFile:kernel/padata.c SourceLine:812 SourceSnippet: 810: free_cpumask_var(pinst->cpumask.cbcpu);
811: free_cpumask_var(pinst->validate_cpumask);
812: destroy_workqueue(pinst->serial_wq);
813: destroy_workqueue(pinst->parallel_wq);
814: kfree(pinst);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__padata_free SourceFile:kernel/padata.c SourceLine:813 SourceSnippet: 811: free_cpumask_var(pinst->validate_cpumask);
812: destroy_workqueue(pinst->serial_wq);
813: destroy_workqueue(pinst->parallel_wq);
814: kfree(pinst);
815: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__pool_destroy SourceFile:drivers/md/dm-thin.c SourceLine:2922 SourceSnippet:2920: cancel_delayed_work_sync(&pool->no_space_timeout);
2921: if (pool->wq)
2922: destroy_workqueue(pool->wq);
2923:
2924: if (pool->next_mapping)
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__rds_conn_create SourceFile:net/rds/connection.c SourceLine:354 SourceSnippet: 352: for (i = 0; i < npaths; i++)
353: if (free_cp[i].cp_wq != rds_wq)
354: destroy_workqueue(free_cp[i].cp_wq);
355: kfree(free_cp);
356: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_ceph_msgr_exit SourceFile:net/ceph/messenger.c SourceLine:231 SourceSnippet: 229: {
230: if (ceph_msgr_wq) {
231: destroy_workqueue(ceph_msgr_wq);
232: ceph_msgr_wq = NULL;
233: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_mlx4_ib_mcg_port_cleanup SourceFile:drivers/infiniband/hw/mlx4/mcg.c SourceLine:1109 SourceSnippet:1107: flush_workqueue(ctx->mcg_wq);
1108: if (destroy_wq)
1109: destroy_workqueue(ctx->mcg_wq);
1110:
1111: mutex_lock(&ctx->mcg_table_lock);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_ec_destroy_workqueues SourceFile:drivers/acpi/ec.c SourceLine:2276 SourceSnippet:2274: {
2275: if (ec_wq) {
2276: destroy_workqueue(ec_wq);
2277: ec_wq = NULL;
2278: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_ec_destroy_workqueues SourceFile:drivers/acpi/ec.c SourceLine:2280 SourceSnippet:2278: }
2279: if (ec_query_wq) {
2280: destroy_workqueue(ec_query_wq);
2281: ec_query_wq = NULL;
2282: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_os_terminate SourceFile:drivers/acpi/osl.c SourceLine:1742 SourceSnippet:1740: acpi_os_unmap_generic_address(&acpi_gbl_FADT.reset_register);
1741:
1742: destroy_workqueue(kacpid_wq);
1743: destroy_workqueue(kacpi_notify_wq);
1744: destroy_workqueue(kacpi_hotplug_wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_os_terminate SourceFile:drivers/acpi/osl.c SourceLine:1743 SourceSnippet:1741:
1742: destroy_workqueue(kacpid_wq);
1743: destroy_workqueue(kacpi_notify_wq);
1744: destroy_workqueue(kacpi_hotplug_wq);
1745:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_os_terminate SourceFile:drivers/acpi/osl.c SourceLine:1744 SourceSnippet:1742: destroy_workqueue(kacpid_wq);
1743: destroy_workqueue(kacpi_notify_wq);
1744: destroy_workqueue(kacpi_hotplug_wq);
1745:
1746: return AE_OK;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_thermal_exit SourceFile:drivers/acpi/thermal.c SourceLine:1055 SourceSnippet:1053: {
1054: platform_driver_unregister(&acpi_thermal_driver);
1055: destroy_workqueue(acpi_thermal_pm_queue);
1056: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_thermal_init SourceFile:drivers/acpi/thermal.c SourceLine:1045 SourceSnippet:1043: result = platform_driver_register(&acpi_thermal_driver);
1044: if (result < 0) {
1045: destroy_workqueue(acpi_thermal_pm_queue);
1046: return -ENODEV;
1047: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:addr_cleanup SourceFile:drivers/infiniband/core/addr.c SourceLine:865 SourceSnippet: 863: {
864: unregister_netevent_notifier(&nb);
865: destroy_workqueue(addr_wq);
866: WARN_ON(!list_empty(&req_list));
867: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:addrconf_cleanup SourceFile:net/ipv6/addrconf.c SourceLine:7631 SourceSnippet:7629: rtnl_net_unlock(&init_net);
7630:
7631: destroy_workqueue(addrconf_wq);
7632: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:addrconf_init SourceFile:net/ipv6/addrconf.c SourceLine:7600 SourceSnippet:7598: unregister_netdevice_notifier(&ipv6_dev_notf);
7599: errlo:
7600: destroy_workqueue(addrconf_wq);
7601: out_nowq:
7602: unregister_pernet_subsys(&addrconf_ops);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_exit_aer SourceFile:drivers/crypto/intel/qat/qat_common/adf_aer.c SourceLine:292 SourceSnippet: 290: {
291: if (device_reset_wq)
292: destroy_workqueue(device_reset_wq);
293: device_reset_wq = NULL;
294:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_exit_aer SourceFile:drivers/crypto/intel/qat/qat_common/adf_aer.c SourceLine:296 SourceSnippet: 294:
295: if (device_sriov_wq)
296: destroy_workqueue(device_sriov_wq);
297: device_sriov_wq = NULL;
298: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_exit_misc_wq SourceFile:drivers/crypto/intel/qat/qat_common/adf_isr.c SourceLine:396 SourceSnippet: 394: {
395: if (adf_misc_wq)
396: destroy_workqueue(adf_misc_wq);
397:
398: adf_misc_wq = NULL;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_exit_pf_wq SourceFile:drivers/crypto/intel/qat/qat_common/adf_sriov.c SourceLine:310 SourceSnippet: 308: {
309: if (pf2vf_resp_wq) {
310: destroy_workqueue(pf2vf_resp_wq);
311: pf2vf_resp_wq = NULL;
312: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_exit_vf_wq SourceFile:drivers/crypto/intel/qat/qat_common/adf_vf_isr.c SourceLine:311 SourceSnippet: 309: {
310: if (adf_vf_stop_wq)
311: destroy_workqueue(adf_vf_stop_wq);
312:
313: adf_vf_stop_wq = NULL;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adf_init_aer SourceFile:drivers/crypto/intel/qat/qat_common/adf_aer.c SourceLine:281 SourceSnippet: 279: device_sriov_wq = alloc_workqueue("qat_device_sriov_wq", WQ_PERCPU, 0);
280: if (!device_sriov_wq) {
281: destroy_workqueue(device_reset_wq);
282: device_reset_wq = NULL;
283: return -EFAULT;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:af_rxrpc_exit SourceFile:net/rxrpc/af_rxrpc.c SourceLine:1137 SourceSnippet:1135: rcu_barrier();
1136:
1137: destroy_workqueue(rxrpc_workqueue);
1138: rxrpc_exit_security();
1139: kmem_cache_destroy(rxrpc_call_jar);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:af_rxrpc_init SourceFile:net/rxrpc/af_rxrpc.c SourceLine:1111 SourceSnippet:1109: rxrpc_exit_security();
1110: error_security:
1111: destroy_workqueue(rxrpc_workqueue);
1112: error_work_queue:
1113: kmem_cache_destroy(rxrpc_call_jar);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_exit SourceFile:fs/afs/main.c SourceLine:230 SourceSnippet: 228: afs_fs_exit();
229: unregister_pernet_device(&afs_net_ops);
230: destroy_workqueue(afs_lock_manager);
231: destroy_workqueue(afs_async_calls);
232: destroy_workqueue(afs_wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_exit SourceFile:fs/afs/main.c SourceLine:231 SourceSnippet: 229: unregister_pernet_device(&afs_net_ops);
230: destroy_workqueue(afs_lock_manager);
231: destroy_workqueue(afs_async_calls);
232: destroy_workqueue(afs_wq);
233: afs_clean_up_permit_cache();
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_exit SourceFile:fs/afs/main.c SourceLine:232 SourceSnippet: 230: destroy_workqueue(afs_lock_manager);
231: destroy_workqueue(afs_async_calls);
232: destroy_workqueue(afs_wq);
233: afs_clean_up_permit_cache();
234: rcu_barrier();
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_init SourceFile:fs/afs/main.c SourceLine:204 SourceSnippet: 202: unregister_pernet_device(&afs_net_ops);
203: error_net:
204: destroy_workqueue(afs_lock_manager);
205: error_lockmgr:
206: destroy_workqueue(afs_async_calls);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_init SourceFile:fs/afs/main.c SourceLine:206 SourceSnippet: 204: destroy_workqueue(afs_lock_manager);
205: error_lockmgr:
206: destroy_workqueue(afs_async_calls);
207: error_async:
208: destroy_workqueue(afs_wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:afs_init SourceFile:fs/afs/main.c SourceLine:208 SourceSnippet: 206: destroy_workqueue(afs_async_calls);
207: error_async:
208: destroy_workqueue(afs_wq);
209: error_afs_wq:
210: rcu_barrier();
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:anx7411_i2c_probe SourceFile:drivers/usb/typec/anx7411.c SourceLine:1545 SourceSnippet:1543:
1544: free_wq:
1545: destroy_workqueue(plat->workqueue);
1546:
1547: free_typec_port:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:anx7411_i2c_remove SourceFile:drivers/usb/typec/anx7411.c SourceLine:1568 SourceSnippet:1566:
1567: if (plat->workqueue)
1568: destroy_workqueue(plat->workqueue);
1569:
1570: i2c_unregister_device(plat->spi_client);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:aoe_exit SourceFile:drivers/block/aoe/aoemain.c SourceLine:39 SourceSnippet: 37: aoedev_exit();
38: aoeblk_exit(); /* free cache after de-allocating bufs */
39: destroy_workqueue(aoe_wq);
40: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:aoe_init SourceFile:drivers/block/aoe/aoemain.c SourceLine:87 SourceSnippet: 85: aoedev_exit();
86: dev_fail:
87: destroy_workqueue(aoe_wq);
88:
89: printk(KERN_INFO "aoe: initialisation failure.\n");
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ar5523_disconnect SourceFile:drivers/net/wireless/ath/ar5523/ar5523.c SourceLine:1765 SourceSnippet:1763: ar5523_free_rx_bufs(ar);
1764:
1765: destroy_workqueue(ar->wq);
1766:
1767: ieee80211_free_hw(hw);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ar5523_probe SourceFile:drivers/net/wireless/ath/ar5523/ar5523.c SourceLine:1743 SourceSnippet:1741: ar5523_free_rx_bufs(ar);
1742: out_free_wq:
1743: destroy_workqueue(ar->wq);
1744: out_free_ar:
1745: ieee80211_free_hw(hw);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:asus_setup_pci_hotplug SourceFile:drivers/platform/x86/asus-wmi.c SourceLine:2325 SourceSnippet:2323: error_register:
2324: asus->hotplug_slot.ops = NULL;
2325: destroy_workqueue(asus->hotplug_workqueue);
2326: error_workqueue:
2327: return ret;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:asus_wmi_led_exit SourceFile:drivers/platform/x86/asus-wmi.c SourceLine:2018 SourceSnippet:2016:
2017: if (asus->led_workqueue)
2018: destroy_workqueue(asus->led_workqueue);
2019: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:asus_wmi_rfkill_exit SourceFile:drivers/platform/x86/asus-wmi.c SourceLine:2455 SourceSnippet:2453: pci_hp_deregister(&asus->hotplug_slot);
2454: if (asus->hotplug_workqueue)
2455: destroy_workqueue(asus->hotplug_workqueue);
2456:
2457: if (asus->bluetooth.rfkill) {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_sff_exit SourceFile:drivers/ata/libata-sff.c SourceLine:3204 SourceSnippet:3202: void ata_sff_exit(void)
3203: {
3204: destroy_workqueue(ata_sff_wq);
3205: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_create SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3753 SourceSnippet:3751: free_netdev(ar->napi_dev);
3752: err_free_tx_complete:
3753: destroy_workqueue(ar->workqueue_tx_complete);
3754: err_free_aux_wq:
3755: destroy_workqueue(ar->workqueue_aux);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_create SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3755 SourceSnippet:3753: destroy_workqueue(ar->workqueue_tx_complete);
3754: err_free_aux_wq:
3755: destroy_workqueue(ar->workqueue_aux);
3756: err_free_wq:
3757: destroy_workqueue(ar->workqueue);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_create SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3757 SourceSnippet:3755: destroy_workqueue(ar->workqueue_aux);
3756: err_free_wq:
3757: destroy_workqueue(ar->workqueue);
3758: err_free_mac:
3759: ath10k_mac_destroy(ar);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_destroy SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3767 SourceSnippet:3765: void ath10k_core_destroy(struct ath10k *ar)
3766: {
3767: destroy_workqueue(ar->workqueue);
3768:
3769: destroy_workqueue(ar->workqueue_aux);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_destroy SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3769 SourceSnippet:3767: destroy_workqueue(ar->workqueue);
3768:
3769: destroy_workqueue(ar->workqueue_aux);
3770:
3771: destroy_workqueue(ar->workqueue_tx_complete);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath10k_core_destroy SourceFile:drivers/net/wireless/ath/ath10k/core.c SourceLine:3771 SourceSnippet:3769: destroy_workqueue(ar->workqueue_aux);
3770:
3771: destroy_workqueue(ar->workqueue_tx_complete);
3772:
3773: free_netdev(ar->napi_dev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath11k_core_alloc SourceFile:drivers/net/wireless/ath/ath11k/core.c SourceLine:2791 SourceSnippet:2789:
2790: err_free_wq:
2791: destroy_workqueue(ab->workqueue);
2792: err_sc_free:
2793: kfree(ab);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath11k_core_free SourceFile:drivers/net/wireless/ath/ath11k/core.c SourceLine:2738 SourceSnippet:2736: void ath11k_core_free(struct ath11k_base *ab)
2737: {
2738: destroy_workqueue(ab->workqueue_aux);
2739: destroy_workqueue(ab->workqueue);
2740:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath11k_core_free SourceFile:drivers/net/wireless/ath/ath11k/core.c SourceLine:2739 SourceSnippet:2737: {
2738: destroy_workqueue(ab->workqueue_aux);
2739: destroy_workqueue(ab->workqueue);
2740:
2741: kfree(ab);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath11k_qmi_deinit_service SourceFile:drivers/net/wireless/ath/ath11k/qmi.c SourceLine:3356 SourceSnippet:3354: qmi_handle_release(&ab->qmi.handle);
3355: cancel_work_sync(&ab->qmi.event_work);
3356: destroy_workqueue(ab->qmi.event_wq);
3357: ath11k_qmi_m3_free(ab);
3358: ath11k_qmi_free_target_mem_chunk(ab);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath11k_qmi_init_service SourceFile:drivers/net/wireless/ath/ath11k/qmi.c SourceLine:3345 SourceSnippet:3343: if (ret < 0) {
3344: ath11k_warn(ab, "failed to add qmi lookup: %d\n", ret);
3345: destroy_workqueue(ab->qmi.event_wq);
3346: return ret;
3347: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath6kl_core_cleanup SourceFile:drivers/net/wireless/ath/ath6kl/core.c SourceLine:336 SourceSnippet: 334: ath6kl_recovery_cleanup(ar);
335:
336: destroy_workqueue(ar->ath6kl_wq);
337:
338: if (ar->htc_target)
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath6kl_core_init SourceFile:drivers/net/wireless/ath/ath6kl/core.c SourceLine:261 SourceSnippet: 259: ath6kl_bmi_cleanup(ar);
260: err_wq:
261: destroy_workqueue(ar->ath6kl_wq);
262:
263: return ret;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath6kl_usb_destroy SourceFile:drivers/net/wireless/ath/ath6kl/usb.c SourceLine:622 SourceSnippet: 620: kfree(ar_usb->diag_cmd_buffer);
621: kfree(ar_usb->diag_resp_buffer);
622: destroy_workqueue(ar_usb->wq);
623:
624: kfree(ar_usb);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_exit SourceFile:net/batman-adv/main.c SourceLine:131 SourceSnippet: 129: unregister_netdevice_notifier(&batadv_hard_if_notifier);
130:
131: destroy_workqueue(batadv_event_workqueue);
132: batadv_event_workqueue = NULL;
133:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcache_exit SourceFile:drivers/md/bcache/super.c SourceLine:2857 SourceSnippet:2855: kobject_put(bcache_kobj);
2856: if (bcache_wq)
2857: destroy_workqueue(bcache_wq);
2858: if (bch_journal_wq)
2859: destroy_workqueue(bch_journal_wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcache_exit SourceFile:drivers/md/bcache/super.c SourceLine:2859 SourceSnippet:2857: destroy_workqueue(bcache_wq);
2858: if (bch_journal_wq)
2859: destroy_workqueue(bch_journal_wq);
2860: if (bch_flush_wq)
2861: destroy_workqueue(bch_flush_wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcache_exit SourceFile:drivers/md/bcache/super.c SourceLine:2861 SourceSnippet:2859: destroy_workqueue(bch_journal_wq);
2860: if (bch_flush_wq)
2861: destroy_workqueue(bch_flush_wq);
2862: bch_btree_exit();
2863:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bch_btree_exit SourceFile:drivers/md/bcache/btree.c SourceLine:2820 SourceSnippet:2818: {
2819: if (btree_io_wq)
2820: destroy_workqueue(btree_io_wq);
2821: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bch_cached_dev_writeback_start SourceFile:drivers/md/bcache/writeback.c SourceLine:1087 SourceSnippet:1085: if (IS_ERR(dc->writeback_thread)) {
1086: cached_dev_put(dc);
1087: destroy_workqueue(dc->writeback_write_wq);
1088: return PTR_ERR(dc->writeback_thread);
1089: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bch_writeback_thread SourceFile:drivers/md/bcache/writeback.c SourceLine:835 SourceSnippet: 833:
834: if (dc->writeback_write_wq)
835: destroy_workqueue(dc->writeback_write_wq);
836:
837: cached_dev_put(dc);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bioset_exit SourceFile:block/bio.c SourceLine:1904 SourceSnippet:1902: bio_alloc_cache_destroy(bs);
1903: if (bs->rescue_workqueue)
1904: destroy_workqueue(bs->rescue_workqueue);
1905: bs->rescue_workqueue = NULL;
1906:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:blk_crypto_fallback_init SourceFile:block/blk-crypto-fallback.c SourceLine:606 SourceSnippet: 604: kfree(blk_crypto_keyslots);
605: fail_free_wq:
606: destroy_workqueue(blk_crypto_wq);
607: fail_destroy_profile:
608: blk_crypto_profile_destroy(blk_crypto_fallback_profile);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bond_destructor SourceFile:drivers/net/bonding/bond_main.c SourceLine:5977 SourceSnippet:5975:
5976: if (bond->wq)
5977: destroy_workqueue(bond->wq);
5978:
5979: free_percpu(bond->rr_tx_counter);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:btrfs_destroy_workqueue SourceFile:fs/btrfs/async-thread.c SourceLine:358 SourceSnippet: 356: if (!wq)
357: return;
358: destroy_workqueue(wq->normal_wq);
359: trace_btrfs_workqueue_destroy(wq);
360: kfree(wq);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:btrfs_stop_all_workers SourceFile:fs/btrfs/disk-io.c SourceLine:1743 SourceSnippet:1741: btrfs_destroy_workqueue(fs_info->workers);
1742: if (fs_info->endio_workers)
1743: destroy_workqueue(fs_info->endio_workers);
1744: if (fs_info->rmw_workers)
1745: destroy_workqueue(fs_info->rmw_workers);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:btrfs_stop_all_workers SourceFile:fs/btrfs/disk-io.c SourceLine:1745 SourceSnippet:1743: destroy_workqueue(fs_info->endio_workers);
1744: if (fs_info->rmw_workers)
1745: destroy_workqueue(fs_info->rmw_workers);
1746: btrfs_destroy_workqueue(fs_info->endio_write_workers);
1747: btrfs_destroy_workqueue(fs_info->endio_freespace_worker);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:btrfs_stop_all_workers SourceFile:fs/btrfs/disk-io.c SourceLine:1753 SourceSnippet:1751: btrfs_destroy_workqueue(fs_info->qgroup_rescan_workers);
1752: if (fs_info->discard_ctl.discard_workers)
1753: destroy_workqueue(fs_info->discard_ctl.discard_workers);
1754: /*
1755: * Now that all other work queues are destroyed, we can safely destroy
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:btrfs_stop_all_workers SourceFile:fs/btrfs/disk-io.c SourceLine:1760 SourceSnippet:1758: */
1759: if (fs_info->endio_meta_workers)
1760: destroy_workqueue(fs_info->endio_meta_workers);
1761: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cache_set_free SourceFile:drivers/md/bcache/super.c SourceLine:1705 SourceSnippet:1703:
1704: if (c->moving_gc_wq)
1705: destroy_workqueue(c->moving_gc_wq);
1706: bioset_exit(&c->bio_split);
1707: mempool_exit(&c->fill_iter);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ceph_osdc_init SourceFile:net/ceph/osd_client.c SourceLine:5256 SourceSnippet:5254:
5255: out_notify_wq:
5256: destroy_workqueue(osdc->notify_wq);
5257: out_msgpool_reply:
5258: ceph_msgpool_destroy(&osdc->msgpool_op_reply);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ceph_osdc_stop SourceFile:net/ceph/osd_client.c SourceLine:5271 SourceSnippet:5269: void ceph_osdc_stop(struct ceph_osd_client *osdc)
5270: {
5271: destroy_workqueue(osdc->completion_wq);
5272: destroy_workqueue(osdc->notify_wq);
5273: cancel_delayed_work_sync(&osdc->timeout_work);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ceph_osdc_stop SourceFile:net/ceph/osd_client.c SourceLine:5272 SourceSnippet:5270: {
5271: destroy_workqueue(osdc->completion_wq);
5272: destroy_workqueue(osdc->notify_wq);
5273: cancel_delayed_work_sync(&osdc->timeout_work);
5274: cancel_delayed_work_sync(&osdc->osds_timeout_work);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cfg80211_exit SourceFile:net/wireless/core.c SourceLine:2037 SourceSnippet:2035: regulatory_exit();
2036: unregister_pernet_device(&cfg80211_pernet_ops);
2037: destroy_workqueue(cfg80211_wq);
2038: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ci_hdrc_otg_destroy SourceFile:drivers/usb/chipidea/otg.c SourceLine:268 SourceSnippet: 266: {
267: if (ci->wq)
268: destroy_workqueue(ci->wq);
269:
270: /* Disable all OTG irq and clear status */
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cleanup_dev SourceFile:drivers/char/xillybus/xillyusb.c SourceLine:559 SourceSnippet: 557:
558: if (xdev->workq)
559: destroy_workqueue(xdev->workq);
560:
561: usb_put_dev(xdev->udev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cleanup_mapped_device SourceFile:drivers/md/dm.c SourceLine:2230 SourceSnippet:2228: {
2229: if (md->wq)
2230: destroy_workqueue(md->wq);
2231: dm_free_md_mempools(md->mempools);
2232:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cleanup_netconsole SourceFile:drivers/net/netconsole.c SourceLine:2241 SourceSnippet:2239: }
2240:
2241: destroy_workqueue(netconsole_wq);
2242: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:clone_ctr SourceFile:drivers/md/dm-clone-target.c SourceLine:1912 SourceSnippet:1910: dm_kcopyd_client_destroy(clone->kcopyd_client);
1911: out_with_wq:
1912: destroy_workqueue(clone->wq);
1913: out_with_ht:
1914: hash_table_exit(clone);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:clone_dtr SourceFile:drivers/md/dm-clone-target.c SourceLine:1943 SourceSnippet:1941: dm_kcopyd_client_destroy(clone->kcopyd_client);
1942: cancel_delayed_work_sync(&clone->waker);
1943: destroy_workqueue(clone->wq);
1944: hash_table_exit(clone);
1945: dm_clone_metadata_close(clone->cmd);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cma_cleanup SourceFile:drivers/infiniband/core/cma.c SourceLine:5545 SourceSnippet:5543: ib_sa_unregister_client(&sa_client);
5544: unregister_pernet_subsys(&cma_pernet_operations);
5545: destroy_workqueue(cma_wq);
5546: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cma_init SourceFile:drivers/infiniband/core/cma.c SourceLine:5533 SourceSnippet:5531: unregister_pernet_subsys(&cma_pernet_operations);
5532: err_wq:
5533: destroy_workqueue(cma_wq);
5534: return ret;
5535: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_fs_client SourceFile:fs/ceph/super.c SourceLine:869 SourceSnippet: 867:
868: fail_inode_wq:
869: destroy_workqueue(fsc->inode_wq);
870: fail_client:
871: ceph_destroy_client(fsc->client);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:crypt_dtr SourceFile:drivers/md/dm-crypt.c SourceLine:2707 SourceSnippet:2705:
2706: if (cc->io_queue)
2707: destroy_workqueue(cc->io_queue);
2708: if (cc->crypt_queue)
2709: destroy_workqueue(cc->crypt_queue);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:crypt_dtr SourceFile:drivers/md/dm-crypt.c SourceLine:2709 SourceSnippet:2707: destroy_workqueue(cc->io_queue);
2708: if (cc->crypt_queue)
2709: destroy_workqueue(cc->crypt_queue);
2710:
2711: if (cc->workqueue_id)
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cryptd_exit SourceFile:crypto/cryptd.c SourceLine:1035 SourceSnippet:1033: static void __exit cryptd_exit(void)
1034: {
1035: destroy_workqueue(cryptd_wq);
1036: cryptd_fini_queue(&queue);
1037: crypto_unregister_template(&cryptd_tmpl);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cryptd_init SourceFile:crypto/cryptd.c SourceLine:1029 SourceSnippet:1027: cryptd_fini_queue(&queue);
1028: err_destroy_wq:
1029: destroy_workqueue(cryptd_wq);
1030: return err;
1031: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ct_cleanup_module SourceFile:net/sched/act_ct.c SourceLine:1701 SourceSnippet:1699: tcf_unregister_action(&act_ct_ops, &ct_net_ops);
1700: tcf_ct_flow_tables_uninit();
1701: destroy_workqueue(act_ct_wq);
1702: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ct_init_module SourceFile:net/sched/act_ct.c SourceLine:1692 SourceSnippet:1690: tcf_ct_flow_tables_uninit();
1691: err_tbl_init:
1692: destroy_workqueue(act_ct_wq);
1693: return err;
1694: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_initialize SourceFile:drivers/media/i2c/cx25840/cx25840-core.c SourceLine:1091 SourceSnippet:1089: schedule();
1090: finish_wait(&state->fw_wait, &wait);
1091: destroy_workqueue(q);
1092: }
1093:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx23885_initialize SourceFile:drivers/media/i2c/cx25840/cx25840-core.c SourceLine:966 SourceSnippet: 964: schedule();
965: finish_wait(&state->fw_wait, &wait);
966: destroy_workqueue(q);
967: }
968:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx25840_initialize SourceFile:drivers/media/i2c/cx25840/cx25840-core.c SourceLine:729 SourceSnippet: 727: schedule();
728: finish_wait(&state->fw_wait, &wait);
729: destroy_workqueue(q);
730: }
731:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:destroy_fs_client SourceFile:fs/ceph/super.c SourceLine:895 SourceSnippet: 893:
894: ceph_mdsc_destroy(fsc);
895: destroy_workqueue(fsc->inode_wq);
896: destroy_workqueue(fsc->cap_wq);
897:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:destroy_fs_client SourceFile:fs/ceph/super.c SourceLine:896 SourceSnippet: 894: ceph_mdsc_destroy(fsc);
895: destroy_workqueue(fsc->inode_wq);
896: destroy_workqueue(fsc->cap_wq);
897:
898: destroy_mount_options(fsc->mount_options);
]] TruncatedOutput:true]
|
| 22/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 26569
output: 34
thoughts: 18
Thoughts: Here's a summary of my thought process, as requested:
**Navigating the HCI Core for Workqueue Destruction**
Okay, so I'm thinking, where would the workqueue destruction be handled in the HCI stack? `hci_core.c` seems like the most likely place to start. It's the core of the Host Controller Interface, so it's probably responsible for managing resources like workqueues that are fundamental to its operation. I'll need to confirm if it has the `destroy_workqueue` function. This function is the key to ensuring that we properly clean up our workqueues when they're no longer needed, avoiding resource leaks and potential issues down the line. I'll need to dive into the code and see if it's there, and then, how it's being used within the HCI context.
|
| 23/2 |
2026/06/01 04:31 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue.*hdev->workqueue PathPrefix:net/bluetooth/]
Results: map[Output:net/bluetooth/hci_core.c=2585=int hci_register_dev(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-2613- if (!hdev->req_workqueue) {
net/bluetooth/hci_core.c:2614: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2615- error = -ENOMEM;
--
net/bluetooth/hci_core.c-2680- debugfs_remove_recursive(hdev->debugfs);
net/bluetooth/hci_core.c:2681: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2682- destroy_workqueue(hdev->req_workqueue);
--
net/bluetooth/hci_core.c=2744=void hci_release_dev(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-2749-
net/bluetooth/hci_core.c:2750: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2751- destroy_workqueue(hdev->req_workqueue);
]
|
| 24/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 27123
output: 38
thoughts: 35
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into Bluetooth Device Release**
Right, so `hci_release_dev` is the function that's being called, which makes sense, as the context points towards device release. That's usually where the cleanup happens. And, as suspected, it's immediately calling `destroy_workqueue(hdev->workqueue)`. Hmm, this is interesting, because I know that workqueues are a common mechanism in the Bluetooth stack for deferred processing, especially in handling events and asynchronous operations. So, we're explicitly destroying the workqueue associated with the HCI device, `hdev`. This tells me that this particular function is responsible for shutting down all outstanding work scheduled for this specific Bluetooth device. This is as I would have expected, so to confirm this I would now investigate `hci_release_dev` directly to be certain of all the actions being performed within that function. I'll need to double check the specifics of how everything gets cleaned up there.
|
| 25/2 |
2026/06/01 04:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_release_dev]
Results: map[SourceCode:2743: /* Release HCI device */
2744: void hci_release_dev(struct hci_dev *hdev)
2745: {
2746: debugfs_remove_recursive(hdev->debugfs);
2747: kfree_const(hdev->hw_info);
2748: kfree_const(hdev->fw_info);
2749:
2750: destroy_workqueue(hdev->workqueue);
2751: destroy_workqueue(hdev->req_workqueue);
2752:
2753: hci_dev_lock(hdev);
2754: hci_bdaddr_list_clear(&hdev->reject_list);
2755: hci_bdaddr_list_clear(&hdev->accept_list);
2756: hci_uuids_clear(hdev);
2757: hci_link_keys_clear(hdev);
2758: hci_smp_ltks_clear(hdev);
2759: hci_smp_irks_clear(hdev);
2760: hci_remote_oob_data_clear(hdev);
2761: hci_adv_instances_clear(hdev);
2762: hci_adv_monitors_clear(hdev);
2763: hci_bdaddr_list_clear(&hdev->le_accept_list);
2764: hci_bdaddr_list_clear(&hdev->le_resolv_list);
2765: hci_conn_params_clear_all(hdev);
2766: hci_discovery_filter_clear(hdev);
2767: hci_blocked_keys_clear(hdev);
2768: hci_codec_list_clear(&hdev->local_codecs);
2769: msft_release(hdev);
2770: hci_dev_unlock(hdev);
2771:
2772: ida_destroy(&hdev->unset_handle_ida);
2773: ida_free(&hci_index_ida, hdev->id);
2774: kfree_skb(hdev->sent_cmd);
2775: kfree_skb(hdev->req_skb);
2776: kfree_skb(hdev->recv_event);
2777: kfree(hdev);
2778: }
SourceFile:net/bluetooth/hci_core.c]
|
| 26/2 |
2026/06/01 04:31 |
llm |
expert |
0m
Model:
Tokens:
input: 28089
output: 38
thoughts: 48
Thoughts: Okay, let me think this through.
**Analyzing the Bluetooth HCI Device Release Sequence**
So, `hci_release_dev` is clearly meant to clean up resources when a Bluetooth HCI device is no longer in use, which makes sense. But the odd thing is, I'm reading the code, and it looks like `hci_unregister_dev` gets called *before* `hci_release_dev`. That seems... counterintuitive. Why would you unregister the device *before* releasing it? There's got to be a reason for this specific ordering. Let me jump into the `hci_unregister_dev` function and see what's going on in there. Perhaps the unregistration process itself sets up some preconditions or invalidates certain states that need to happen prior to the actual release. I need to understand this sequence.
|
| 27/2 |
2026/06/01 04:32 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_unregister_dev]
Results: map[SourceCode:2690: /* Unregister HCI device */
2691: void hci_unregister_dev(struct hci_dev *hdev)
2692: {
2693: BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
2694:
2695: mutex_lock(&hdev->unregister_lock);
2696: hci_dev_set_flag(hdev, HCI_UNREGISTER);
2697: mutex_unlock(&hdev->unregister_lock);
2698:
2699: write_lock(&hci_dev_list_lock);
2700: list_del(&hdev->list);
2701: write_unlock(&hci_dev_list_lock);
2702:
2703: synchronize_srcu(&hdev->srcu);
2704: cleanup_srcu_struct(&hdev->srcu);
2705:
2706: disable_work_sync(&hdev->rx_work);
2707: disable_work_sync(&hdev->cmd_work);
2708: disable_work_sync(&hdev->tx_work);
2709: disable_work_sync(&hdev->power_on);
2710: disable_work_sync(&hdev->error_reset);
2711:
2712: hci_cmd_sync_clear(hdev);
2713:
2714: hci_unregister_suspend_notifier(hdev);
2715:
2716: hci_dev_do_close(hdev);
2717:
2718: if (!test_bit(HCI_INIT, &hdev->flags) &&
2719: !hci_dev_test_flag(hdev, HCI_SETUP) &&
2720: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
2721: hci_dev_lock(hdev);
2722: mgmt_index_removed(hdev);
2723: hci_dev_unlock(hdev);
2724: }
2725:
2726: /* mgmt_index_removed should take care of emptying the
2727: * pending list */
2728: BUG_ON(!list_empty(&hdev->mgmt_pending));
2729:
2730: hci_sock_dev_event(hdev, HCI_DEV_UNREG);
2731:
2732: if (hdev->rfkill) {
2733: rfkill_unregister(hdev->rfkill);
2734: rfkill_destroy(hdev->rfkill);
2735: }
2736:
2737: device_del(&hdev->dev);
2738: /* Actual cleanup is deferred until hci_release_dev(). */
2739: hci_dev_put(hdev);
2740: }
SourceFile:net/bluetooth/hci_core.c]
|
| 28/2 |
2026/06/01 04:32 |
llm |
expert |
0m
Model:
Tokens:
input: 29176
output: 40
thoughts: 35
Thoughts: Here's a breakdown of my thought process:
**Diving into the HCI Device Unregistration Process**
Okay, so I'm digging into the Bluetooth HCI device unregistration procedure. I see that `hci_unregister_dev` is the function that initiates the unregistration. The first thing that jumps out is that it calls `hci_dev_do_close(hdev)`. My immediate reaction is to check out what that `hci_dev_do_close` function is doing. That's the core of where the cleanup likely happens. Let's see whatβs inside.
|
| 29/2 |
2026/06/01 04:32 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_dev_do_close]
Results: map[SourceCode: 494: int hci_dev_do_close(struct hci_dev *hdev)
495: {
496: int err;
497:
498: BT_DBG("%s %p", hdev->name, hdev);
499:
500: hci_req_sync_lock(hdev);
501:
502: err = hci_dev_close_sync(hdev);
503:
504: hci_req_sync_unlock(hdev);
505:
506: return err;
507: }
SourceFile:net/bluetooth/hci_core.c]
|
| 30/2 |
2026/06/01 04:32 |
llm |
expert |
2m
Model:
Tokens:
input: 29583
output: 40
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on `hci_dev_close_sync`:
**Understanding hci_dev_close_sync**
Alright, so I'm looking at `hci_dev_close_sync`. Immediately, I'm thinking, "This is about cleanly shutting down a Bluetooth HCI (Host Controller Interface) device." Given the "sync" in the name, I'm expecting this is going to be a blocking call. That makes sense; we wouldn't want to just cut off communication and leave things in an undefined state. We need to ensure that whatever processes are using the HCI device are properly handled before it gets closed.
My experience tells me this function is likely responsible for:
* **Flushing Pending Operations:** Making sure any pending commands or data transfers are completed, or at least gracefully cancelled. We don't want to lose any information.
* **Signalling Completion:** It will probably wait for all outstanding operations to finish. Perhaps there's an internal flag that changes state to communicate this process to other functions within the Bluetooth stack.
* **Releasing Resources:** Finally, this function should deal with releasing any resources associated with the device. This could include memory, file descriptors, or internal data structures related to the HCI connection.
* **Error Handling:** It will need to handle cases where there are issues during the shutdown. Things go wrong sometimes, and this needs to be graceful with providing some indication of the issue.
I'm now curious about the specific implementation details: What are the low-level interactions? Are there locks involved to protect critical sections? What are the potential error conditions that can arise? A look at the code should provide me with all the answers I'm looking for.
|
| 31/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c Name:hci_dev_close_sync]
Results: map[SourceCode:5294: int hci_dev_close_sync(struct hci_dev *hdev)
5295: {
5296: bool auto_off;
5297: int err = 0;
5298:
5299: bt_dev_dbg(hdev, "");
5300:
5301: if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
5302: disable_delayed_work(&hdev->power_off);
5303: disable_delayed_work(&hdev->ncmd_timer);
5304: disable_delayed_work(&hdev->le_scan_disable);
5305: } else {
5306: cancel_delayed_work(&hdev->power_off);
5307: cancel_delayed_work(&hdev->ncmd_timer);
5308: cancel_delayed_work(&hdev->le_scan_disable);
5309: }
5310:
5311: hci_cmd_sync_cancel_sync(hdev, ENODEV);
5312:
5313: cancel_interleave_scan(hdev);
5314:
5315: if (hdev->adv_instance_timeout) {
5316: cancel_delayed_work_sync(&hdev->adv_instance_expire);
5317: hdev->adv_instance_timeout = 0;
5318: }
5319:
5320: err = hci_dev_shutdown(hdev);
5321:
5322: if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
5323: cancel_delayed_work_sync(&hdev->cmd_timer);
5324: return err;
5325: }
5326:
5327: hci_leds_update_powered(hdev, false);
5328:
5329: /* Flush RX and TX works */
5330: flush_work(&hdev->tx_work);
5331: flush_work(&hdev->rx_work);
5332:
5333: if (hdev->discov_timeout > 0) {
5334: hdev->discov_timeout = 0;
5335: hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
5336: hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
5337: }
5338:
5339: if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
5340: cancel_delayed_work(&hdev->service_cache);
5341:
5342: if (hci_dev_test_flag(hdev, HCI_MGMT)) {
5343: struct adv_info *adv_instance;
5344:
5345: cancel_delayed_work_sync(&hdev->rpa_expired);
5346:
5347: list_for_each_entry(adv_instance, &hdev->adv_instances, list)
5348: cancel_delayed_work_sync(&adv_instance->rpa_expired_cb);
5349: }
5350:
5351: /* Avoid potential lockdep warnings from the *_flush() calls by
5352: * ensuring the workqueue is empty up front.
5353: */
5354: drain_workqueue(hdev->workqueue);
5355:
5356: hci_dev_lock(hdev);
5357:
5358: hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
5359:
5360: auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
5361:
5362: if (!auto_off && !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
5363: hci_dev_test_flag(hdev, HCI_MGMT))
5364: __mgmt_power_off(hdev);
5365:
5366: hci_inquiry_cache_flush(hdev);
5367: hci_pend_le_actions_clear(hdev);
5368: hci_conn_hash_flush(hdev);
5369: /* Prevent data races on hdev->smp_data or hdev->smp_bredr_data */
5370: smp_unregister(hdev);
5371: hci_dev_unlock(hdev);
5372:
5373: hci_sock_dev_event(hdev, HCI_DEV_DOWN);
5374:
5375: if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
5376: aosp_do_close(hdev);
5377: msft_do_close(hdev);
5378: }
5379:
5380: if (hdev->flush)
5381: hdev->flush(hdev);
5382:
5383: /* Reset device */
5384: skb_queue_purge(&hdev->cmd_q);
5385: atomic_set(&hdev->cmd_cnt, 1);
5386: if (hci_test_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE) &&
5387: !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
5388: set_bit(HCI_INIT, &hdev->flags);
5389: hci_reset_sync(hdev);
5390: clear_bit(HCI_INIT, &hdev->flags);
5391: }
5392:
5393: /* flush cmd work */
5394: flush_work(&hdev->cmd_work);
5395:
5396: /* Drop queues */
5397: skb_queue_purge(&hdev->rx_q);
5398: skb_queue_purge(&hdev->cmd_q);
5399: skb_queue_purge(&hdev->raw_q);
5400:
5401: /* Drop last sent command */
5402: if (hdev->sent_cmd) {
5403: cancel_delayed_work_sync(&hdev->cmd_timer);
5404: kfree_skb(hdev->sent_cmd);
5405: hdev->sent_cmd = NULL;
5406: }
5407:
5408: /* Drop last request */
5409: if (hdev->req_skb) {
5410: kfree_skb(hdev->req_skb);
5411: hdev->req_skb = NULL;
5412: }
5413:
5414: clear_bit(HCI_RUNNING, &hdev->flags);
5415: hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
5416:
5417: /* After this point our queues are empty and no tasks are scheduled. */
5418: hdev->close(hdev);
5419:
5420: /* Clear flags */
5421: hdev->flags &= BIT(HCI_RAW);
5422: hci_dev_clear_volatile_flags(hdev);
5423:
5424: memset(hdev->eir, 0, sizeof(hdev->eir));
5425: memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
5426: bacpy(&hdev->random_addr, BDADDR_ANY);
5427: hci_codec_list_clear(&hdev->local_codecs);
5428:
5429: hci_dev_put(hdev);
5430: return err;
5431: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 32/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 32232
output: 40
thoughts: 39
Thoughts: Okay, let's see...
**Analyzing `hci_dev_close_sync` and its Implications**
Hmm, so `hci_dev_close_sync` calls `hci_conn_hash_flush(hdev)`. That's interesting, and makes me think about potential concurrency issues during device closure. Let's dig deeper into what `hci_conn_hash_flush` actually does, because that's where the heart of the matter lies. I need to understand how it's clearing connections, and if it does so in a thread-safe way. My main concern is the implications this might have on other processes or threads interacting with the Bluetooth device. I need to verify its behavior.
|
| 33/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_hash_flush]
Results: map[SourceCode:2718: /* Drop all connection on the device */
2719: void hci_conn_hash_flush(struct hci_dev *hdev)
2720: {
2721: struct list_head *head = &hdev->conn_hash.list;
2722: struct hci_conn *conn;
2723:
2724: BT_DBG("hdev %s", hdev->name);
2725:
2726: /* We should not traverse the list here, because hci_conn_del
2727: * can remove extra links, which may cause the list traversal
2728: * to hit items that have already been released.
2729: */
2730: while ((conn = list_first_entry_or_null(head,
2731: struct hci_conn,
2732: list)) != NULL) {
2733: conn->state = BT_CLOSED;
2734: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
2735: hci_conn_del(conn);
2736: }
2737: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 34/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 32798
output: 39
thoughts: 59
Thoughts: Okay, let's break this down.
**Diving into the Connection Flush**
Right, so `hci_conn_hash_flush` is the function we're looking at here. It seems like its primary job is to clean up a connection table. Interestingly, it's not just a simple delete; it's got a multi-step process. First, it calls `hci_disconn_cfm`, and then it calls `hci_conn_del`. That sequence is important. Now, I see why this is constructed like this, from an HCI perspective.
The `hci_disconn_cfm` call is the key. My immediate thought is, "Ah, this is the notification step." It's likely responsible for letting L2CAP (or the higher-level protocols) know that the underlying connection is being disconnected. This makes perfect sense; L2CAP needs to be informed, so it can handle its channels and any associated data streams appropriately. It prevents L2CAP from trying to use a connection that is no longer there.
I need to understand what this `hci_disconn_cfm` function *specifically* does, that's where I need to focus my effort. I'll need to go and examine its code.
|
| 35/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_event.c Name:hci_disconn_cfm]
Results: map[SourceCode:2147: static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
2148: {
2149: struct hci_cb *cb;
2150:
2151: mutex_lock(&hci_cb_list_lock);
2152: list_for_each_entry(cb, &hci_cb_list, list) {
2153: if (cb->disconn_cfm)
2154: cb->disconn_cfm(conn, reason);
2155: }
2156: mutex_unlock(&hci_cb_list_lock);
2157:
2158: if (conn->disconn_cfm_cb)
2159: conn->disconn_cfm_cb(conn, reason);
2160: }
SourceFile:include/net/bluetooth/hci_core.h]
|
| 36/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 33411
output: 43
thoughts: 50
Thoughts: Here's my take on the thought process:
**Diving into the Bluetooth Disconnection Confirmation Code**
Okay, let me see... I'm tracing through this disconnection confirmation process, and my initial thought is, where's the `hci_disconn_cfm` function defined? Ah, right, a quick search reveals it's sitting pretty in `include/net/bluetooth/hci_core.h`. That makes sense. It's a fundamental part of the HCI layer.
Now, I'm shifting gears to L2CAP and the connection establishment confirmation. Looking at the control block here, the `cb->disconn_cfm` callback is specifically pointing to `l2cap_disconn_cfm`. Excellent! So, L2CAP has its own dedicated function to handle disconnection confirmations. That's a clean separation of concerns, and quite efficient. Now that I know the specific function and its location, I can dig a bit deeper into what these functions do.
|
| 37/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_disconn_cfm]
Results: map[SourceCode:7433: static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
7434: {
7435: if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
7436: return;
7437:
7438: BT_DBG("hcon %p reason %d", hcon, reason);
7439:
7440: l2cap_conn_del(hcon, bt_to_errno(reason));
7441: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 38/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 33889
output: 42
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on `l2cap_conn_del`:
**Deconstructing `l2cap_conn_del`**
Alright, I see we're looking at `l2cap_conn_del`. Immediately, I'm thinking L2CAP β that's the Logical Link Control and Adaptation Protocol, the foundation for connection-oriented communication in Bluetooth. And "del" suggests we're dealing with deleting or removing something. So, we're talking about connection management within the L2CAP layer.
My initial assumption is this function is critical for cleaning up resources associated with an established L2CAP connection. It's likely called when a connection is closed, either gracefully by one of the endpoints, or perhaps due to an error condition or timeout. The function is likely responsible for several tasks. I would expect it to free the allocated memory, remove the connection from relevant data structures like connection tables or lists, and potentially signal the underlying transport layer that the connection is no longer active.
Furthermore, I'm curious about the specific arguments passed to this function. I'd need to consider the context of the parameters, such as the L2CAP channel identifier, the Bluetooth address of the peer device, and any associated connection data structures. These could be key in enabling a secure and orderly cleanup.
My expert intuition suggests this function will have implications on Bluetooth security. Ensuring all memory and resources associated with the connection are freed correctly could prevent potential vulnerabilities, like use-after-free scenarios. It is critical that this is done with utmost diligence. I want to see how these connections are safely released, including the connection security context, and that this code is reviewed in the context of the security design.
This is where I'd need to look at the implementation details, but my initial thought process is leading me down the right path.
|
| 39/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_conn_del]
Results: map[SourceCode:1764: static void l2cap_conn_del(struct hci_conn *hcon, int err)
1765: {
1766: struct l2cap_conn *conn = hcon->l2cap_data;
1767: struct l2cap_chan *chan, *l;
1768:
1769: if (!conn)
1770: return;
1771:
1772: BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
1773:
1774: disable_delayed_work_sync(&conn->info_timer);
1775: disable_delayed_work_sync(&conn->id_addr_timer);
1776:
1777: mutex_lock(&conn->lock);
1778:
1779: kfree_skb(conn->rx_skb);
1780:
1781: skb_queue_purge(&conn->pending_rx);
1782:
1783: /* We can not call flush_work(&conn->pending_rx_work) here since we
1784: * might block if we are running on a worker from the same workqueue
1785: * pending_rx_work is waiting on.
1786: */
1787: if (work_pending(&conn->pending_rx_work))
1788: cancel_work_sync(&conn->pending_rx_work);
1789:
1790: ida_destroy(&conn->tx_ida);
1791:
1792: l2cap_unregister_all_users(conn);
1793:
1794: /* Force the connection to be immediately dropped */
1795: hcon->disc_timeout = 0;
1796:
1797: /* Kill channels */
1798: list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
1799: l2cap_chan_hold(chan);
1800: l2cap_chan_lock(chan);
1801:
1802: l2cap_chan_del(chan, err);
1803:
1804: chan->ops->close(chan);
1805:
1806: l2cap_chan_unlock(chan);
1807: l2cap_chan_put(chan);
1808: }
1809:
1810: hci_chan_del(conn->hchan);
1811: conn->hchan = NULL;
1812:
1813: hcon->l2cap_data = NULL;
1814: mutex_unlock(&conn->lock);
1815: l2cap_conn_put(conn);
1816: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 40/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 35137
output: 37
thoughts: 293
Thoughts: **Analyzing the Race Condition in L2CAP Connection Deletion**
Okay, let's break this down. My initial thought is triggered by a warning related to `l2cap_info_timeout`. Now, I remember that `l2cap_conn_del` calls `disable_delayed_work_sync(&conn->info_timer)`. The warning is coming from the `l2cap_info_timeout` function, which is the work function associated with `conn->info_timer.work`.
Aha, `disable_delayed_work_sync` is designed to ensure the work is finished, and it waits. But here's the kicker: what if `l2cap_info_timeout` is already running concurrently? If it *is* running, the `disable_delayed_work_sync` call should patiently wait for it to complete.
But then, I recall the lock. `l2cap_info_timeout` acquires `conn->lock`, right? And crucially, `l2cap_conn_del` calls `disable_delayed_work_sync` *before* it grabs `conn->lock`. So, my thinking is that `disable_delayed_work_sync` is designed to be safe, waiting for `l2cap_info_timeout` to finish its work before `l2cap_conn_del` proceeds.
Essentially, `disable_delayed_work_sync` cancels the delayed work and waits until it's done. If `l2cap_info_timeout` is already in execution, it will be allowed to run to completion.
One potential point of confusion is whether the work is delayed or regular. Wait, `conn->info_timer` is a `delayed_work` struct, so that is not an issue.
Now, let me go back and re-check the `disable_delayed_work_sync` function. It seems there is not an actual race condition, though I need to verify that.
|
| 41/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c Name:disable_delayed_work_sync]
Results: map[SourceCode:4641: /**
4642: * disable_delayed_work_sync - Disable, cancel and drain a delayed work item
4643: * @dwork: delayed work item to disable
4644: *
4645: * disable_work_sync() for delayed work items.
4646: */
4647: bool disable_delayed_work_sync(struct delayed_work *dwork)
4648: {
4649: return __cancel_work_sync(&dwork->work,
4650: WORK_CANCEL_DELAYED | WORK_CANCEL_DISABLE);
4651: }
SourceFile:kernel/workqueue.c]
|
| 42/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 36078
output: 48
thoughts: 1357
Thoughts: **Analyzing a Bluetooth Driver Race Condition**
Okay, let's break this down. We have this `disable_delayed_work_sync` function, and it's designed to wait for a delayed work item to complete. The bug report points to a problem with `l2cap_info_timeout`, specifically within `l2cap_conn_start`, which is called by `l2cap_info_timeout` and protected by a mutex.
The core of the issue seems to be the interaction between the workqueue and connection cleanup during device unregistration. `hci_dev_close_sync` is called, which calls `drain_workqueue` followed by `hci_conn_hash_flush`. `hci_conn_hash_flush` calls `l2cap_conn_del`, which, crucially, uses `disable_delayed_work_sync` on a timer associated with the connection (`conn->info_timer`).
Here's where it gets interesting: the `hdev->workqueue` is destroyed in `hci_release_dev`. `hci_release_dev` is called when the device's reference count drops to zero, and the reference count drops to 0 after `hci_unregister_dev` is done. `hci_unregister_dev` calls `hci_dev_do_close` which calls `hci_dev_close_sync` which is the process above.
However, `l2cap_info_timeout` is scheduled on the system workqueue, and it's calling `l2cap_conn_start`, which in turn eventually queues work (`hdev->tx_work`) on `hdev->workqueue`. The race is that `hdev->workqueue` is destroyed by `hci_release_dev`.
My initial thought was that there was a race where `l2cap_info_timeout` might be running concurrently with the workqueue destruction. However, the use of `disable_delayed_work_sync` in `l2cap_conn_del` implies a synchronization point. It's designed to wait.
So `l2cap_conn_del` should wait for `l2cap_info_timeout` to finish. It calls `disable_delayed_work_sync`, which should wait for the work item. This means `l2cap_info_timeout` MUST finish before `hci_dev_close_sync` returns.
However, since `hci_unregister_dev` calls `hci_dev_put(hdev)` *after* `hci_dev_close_sync` has returned. This implies `hci_dev_close_sync` has finished and there is no race condition. How is `l2cap_info_timeout` running and hitting the race? There has to be something else going on.
|
| 43/2 |
2026/06/01 04:34 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:l2cap_info_timeout]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:l2cap_conn_add SourceFile:net/bluetooth/l2cap_core.c SourceLine:7029 SourceSnippet:7027: INIT_LIST_HEAD(&conn->users);
7028:
7029: INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
7030: ida_init(&conn->tx_ida);
7031:
]] TruncatedOutput:false]
|
| 44/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 38239
output: 42
thoughts: 42
Thoughts: Okay, let's see. My first thought is: **Okay, let's zero in on this timer issue.**
Is `l2cap_conn_del` truly the *only* place where `conn->info_timer` is cancelled? That seems a little... risky, doesn't it? I need to be absolutely certain of this. Improper timer handling is a classic source of hard-to-debug race conditions and memory corruption.
Alright, I'm going to revisit `l2cap_conn_del` again and scrutinize it to confirm the timer cancellation. I need to be 100% sure this is the only spot we are doing it. It wouldn't be the first time I've missed a subtle edge case in this system. This will require a thorough code review.
|
| 45/2 |
2026/06/01 04:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_conn_del]
Results: map[SourceCode:1764: static void l2cap_conn_del(struct hci_conn *hcon, int err)
1765: {
1766: struct l2cap_conn *conn = hcon->l2cap_data;
1767: struct l2cap_chan *chan, *l;
1768:
1769: if (!conn)
1770: return;
1771:
1772: BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
1773:
1774: disable_delayed_work_sync(&conn->info_timer);
1775: disable_delayed_work_sync(&conn->id_addr_timer);
1776:
1777: mutex_lock(&conn->lock);
1778:
1779: kfree_skb(conn->rx_skb);
1780:
1781: skb_queue_purge(&conn->pending_rx);
1782:
1783: /* We can not call flush_work(&conn->pending_rx_work) here since we
1784: * might block if we are running on a worker from the same workqueue
1785: * pending_rx_work is waiting on.
1786: */
1787: if (work_pending(&conn->pending_rx_work))
1788: cancel_work_sync(&conn->pending_rx_work);
1789:
1790: ida_destroy(&conn->tx_ida);
1791:
1792: l2cap_unregister_all_users(conn);
1793:
1794: /* Force the connection to be immediately dropped */
1795: hcon->disc_timeout = 0;
1796:
1797: /* Kill channels */
1798: list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
1799: l2cap_chan_hold(chan);
1800: l2cap_chan_lock(chan);
1801:
1802: l2cap_chan_del(chan, err);
1803:
1804: chan->ops->close(chan);
1805:
1806: l2cap_chan_unlock(chan);
1807: l2cap_chan_put(chan);
1808: }
1809:
1810: hci_chan_del(conn->hchan);
1811: conn->hchan = NULL;
1812:
1813: hcon->l2cap_data = NULL;
1814: mutex_unlock(&conn->lock);
1815: l2cap_conn_put(conn);
1816: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 46/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 39296
output: 48
thoughts: 43
Thoughts: Okay, let me think this through.
**Analyzing l2cap_conn_del Callsites**
Hmm, so `l2cap_conn_del` is definitely called from within `l2cap_disconn_cfm`. That's a key piece of the puzzle. I need to understand the control flow here, but first, I need to know: are there any *other* locations, other functions, other event handlers that are invoking `l2cap_conn_del`? My experience tells me that without knowing the full call graph, I'm missing context. I need to be absolutely sure I've identified every potential callsite before drawing any firm conclusions about why it's being called where it is. I need the full picture to understand the implications of this.
|
| 47/2 |
2026/06/01 04:34 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:l2cap_conn_del]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:l2cap_connect_cfm SourceFile:net/bluetooth/l2cap_core.c SourceLine:7374 SourceSnippet:7372:
7373: if (status) {
7374: l2cap_conn_del(hcon, bt_to_errno(status));
7375: return;
7376: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:l2cap_disconn_cfm SourceFile:net/bluetooth/l2cap_core.c SourceLine:7440 SourceSnippet:7438: BT_DBG("hcon %p reason %d", hcon, reason);
7439:
7440: l2cap_conn_del(hcon, bt_to_errno(reason));
7441: }
]] TruncatedOutput:false]
|
| 48/2 |
2026/06/01 04:34 |
llm |
expert |
0m
Model:
Tokens:
input: 39823
output: 40
thoughts: 80
Thoughts: Okay, let me think this through.
**Analyzing L2CAP Connection and Disconnection Handling**
Alright, so `l2cap_conn_del` gets triggered in two specific scenarios: when the L2CAP connection confirmation comes back, and also when a disconnection is confirmed. That's straightforward enough. Now, here's a potential snag I need to consider: what if `hci_conn_hash_flush` *doesn't* actually call `l2cap_disconn_cfm` for *all* connections? That would create a disconnect in how those connections are handled! That could be a pretty significant bug. I need to re-examine that `hci_conn_hash_flush` function with this new question in mind. It's time to dig into that code again.
|
| 49/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_hash_flush]
Results: map[SourceCode:2718: /* Drop all connection on the device */
2719: void hci_conn_hash_flush(struct hci_dev *hdev)
2720: {
2721: struct list_head *head = &hdev->conn_hash.list;
2722: struct hci_conn *conn;
2723:
2724: BT_DBG("hdev %s", hdev->name);
2725:
2726: /* We should not traverse the list here, because hci_conn_del
2727: * can remove extra links, which may cause the list traversal
2728: * to hit items that have already been released.
2729: */
2730: while ((conn = list_first_entry_or_null(head,
2731: struct hci_conn,
2732: list)) != NULL) {
2733: conn->state = BT_CLOSED;
2734: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
2735: hci_conn_del(conn);
2736: }
2737: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 50/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 40448
output: 38
thoughts: 63
Thoughts: **Analyzing Connection Dropping in the Host Device**
Okay, so I'm looking at this connection dropping code, and my initial thought is that it simply iterates through `hdev->conn_hash.list` and cleans up every single connection found there. That's straightforward enough.
But then, the alarm bells start to ring. What about connections that *aren't* in that list? Are they just forgotten? Are they still active? That's a huge potential vulnerability, or at least a resource leak. I need to understand the lifecycle of a connection more thoroughly.
Right, so where *are* connections added to that hash list? A quick glance should reveal it... Ah, here it is: `hci_conn_add`. Okay, so `hci_conn_add` is the key. That's where the connection gets registered, and presumably, where it becomes subject to this `hdev->conn_hash.list` cleanup. I need to review that function's implementation next to confirm my assumptions and make sure all relevant cases are handled correctly. This also opens up a question about de-registering the connections from that list too. Is that happening? If not, that is another potential problem.
|
| 51/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_add]
Results: map[SourceCode:1160: struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
1161: u8 dst_type, u8 role, u16 handle)
1162: {
1163: if (handle > HCI_CONN_HANDLE_MAX)
1164: return ERR_PTR(-EINVAL);
1165:
1166: return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
1167: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 52/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 40982
output: 39
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `__hci_conn_add`**
Alright, let's get down to brass tacks. The target here is `__hci_conn_add`. My expert mind immediately jumps to a connection add function within a Bluetooth HCI implementation, likely a core function for managing active Bluetooth connections. I can almost visualize the data structures involved: probably a connection handle, device address, link type, maybe some flags for security and role. I'm already anticipating questions about resource allocation β are we dealing with a fixed-size connection table, or a dynamic allocation scheme? What about error handling? What does this function *do* specifically? Does it just create the data structures, or does it also initiate any lower-level commands to the HCI controller? I'm eager to understand the details. My focus sharpens to understand the inner workings. I need to understand its relationship with other related functions, likely within the HCI stack. Is it called directly by the upper layers of the Bluetooth protocol, or does it sit lower down? Let's figure out what makes this function tick.
|
| 53/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:__hci_conn_add]
Results: map[SourceCode: 992: static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
993: bdaddr_t *dst, u8 dst_type,
994: u8 role, u16 handle)
995: {
996: struct hci_conn *conn;
997: struct smp_irk *irk = NULL;
998:
999: switch (type) {
1000: case ACL_LINK:
1001: if (!hdev->acl_mtu)
1002: return ERR_PTR(-ECONNREFUSED);
1003: break;
1004: case CIS_LINK:
1005: case BIS_LINK:
1006: case PA_LINK:
1007: if (!hdev->iso_mtu)
1008: return ERR_PTR(-ECONNREFUSED);
1009: irk = hci_get_irk(hdev, dst, dst_type);
1010: break;
1011: case LE_LINK:
1012: if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
1013: return ERR_PTR(-ECONNREFUSED);
1014: if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
1015: return ERR_PTR(-ECONNREFUSED);
1016: irk = hci_get_irk(hdev, dst, dst_type);
1017: break;
1018: case SCO_LINK:
1019: case ESCO_LINK:
1020: if (!hdev->sco_pkts)
1021: /* Controller does not support SCO or eSCO over HCI */
1022: return ERR_PTR(-ECONNREFUSED);
1023: break;
1024: default:
1025: return ERR_PTR(-ECONNREFUSED);
1026: }
1027:
1028: bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
1029:
1030: conn = kzalloc_obj(*conn);
1031: if (!conn)
1032: return ERR_PTR(-ENOMEM);
1033:
1034: /* If and IRK exists use its identity address */
1035: if (!irk) {
1036: bacpy(&conn->dst, dst);
1037: conn->dst_type = dst_type;
1038: } else {
1039: bacpy(&conn->dst, &irk->bdaddr);
1040: conn->dst_type = irk->addr_type;
1041: }
1042:
1043: bacpy(&conn->src, &hdev->bdaddr);
1044: conn->handle = handle;
1045: conn->hdev = hdev;
1046: conn->type = type;
1047: conn->role = role;
1048: conn->mode = HCI_CM_ACTIVE;
1049: conn->state = BT_OPEN;
1050: conn->auth_type = HCI_AT_GENERAL_BONDING;
1051: conn->io_capability = hdev->io_capability;
1052: conn->remote_auth = 0xff;
1053: conn->key_type = 0xff;
1054: conn->rssi = HCI_RSSI_INVALID;
1055: conn->tx_power = HCI_TX_POWER_INVALID;
1056: conn->max_tx_power = HCI_TX_POWER_INVALID;
1057: conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
1058: conn->sid = HCI_SID_INVALID;
1059:
1060: set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
1061: conn->disc_timeout = HCI_DISCONN_TIMEOUT;
1062:
1063: /* Set Default Authenticated payload timeout to 30s */
1064: conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
1065:
1066: if (conn->role == HCI_ROLE_MASTER)
1067: conn->out = true;
1068:
1069: switch (type) {
1070: case ACL_LINK:
1071: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
1072: conn->link_policy = hdev->link_policy;
1073: conn->mtu = hdev->acl_mtu;
1074: break;
1075: case LE_LINK:
1076: /* conn->src should reflect the local identity address */
1077: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1078: conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
1079: /* Use the controller supported PHYS as default until the
1080: * remote features are resolved.
1081: */
1082: conn->le_tx_def_phys = hdev->le_tx_def_phys;
1083: conn->le_rx_def_phys = hdev->le_tx_def_phys;
1084: break;
1085: case CIS_LINK:
1086: /* conn->src should reflect the local identity address */
1087: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1088:
1089: if (conn->role == HCI_ROLE_MASTER)
1090: conn->cleanup = cis_cleanup;
1091:
1092: conn->mtu = hdev->iso_mtu;
1093: break;
1094: case PA_LINK:
1095: case BIS_LINK:
1096: /* conn->src should reflect the local identity address */
1097: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1098: conn->cleanup = bis_cleanup;
1099: conn->mtu = hdev->iso_mtu;
1100: break;
1101: case SCO_LINK:
1102: if (lmp_esco_capable(hdev))
1103: conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
1104: (hdev->esco_type & EDR_ESCO_MASK);
1105: else
1106: conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
1107:
1108: conn->mtu = hdev->sco_mtu;
1109: break;
1110: case ESCO_LINK:
1111: conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
1112: conn->mtu = hdev->sco_mtu;
1113: break;
1114: }
1115:
1116: skb_queue_head_init(&conn->data_q);
1117: skb_queue_head_init(&conn->tx_q.queue);
1118:
1119: INIT_LIST_HEAD(&conn->chan_list);
1120: INIT_LIST_HEAD(&conn->link_list);
1121:
1122: INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
1123: INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
1124: INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
1125: INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
1126:
1127: atomic_set(&conn->refcnt, 0);
1128:
1129: hci_dev_hold(hdev);
1130:
1131: hci_conn_hash_add(hdev, conn);
1132:
1133: /* The SCO and eSCO connections will only be notified when their
1134: * setup has been completed. This is different to ACL links which
1135: * can be notified right away.
1136: */
1137: if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
1138: if (hdev->notify)
1139: hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
1140: }
1141:
1142: hci_conn_init_sysfs(conn);
1143: return conn;
1144: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 54/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 43788
output: 38
thoughts: 405
Thoughts: **Analyzing Connection Management and Potential Concurrency Issues**
Okay, so I'm looking at how connections are managed. It looks like `hci_conn_hash_add(hdev, conn)` adds a connection (`conn`) to `hdev->conn_hash.list`. That's the primary storage for connection information, so all active connections are essentially linked through that list. My first thought is about concurrent access to this list. What happens if `hci_conn_del` is called at the same time?
Right, `hci_conn_del` removes the connection from the hash list using `hci_conn_hash_del(hdev, conn)`. But that list manipulation creates a clear race condition if done at the same time.
Now I'm thinking about `hci_conn_hash_flush`. That code has:
```c
while ((conn = list_first_entry_or_null(head,
struct hci_conn,
list)) != NULL) {
conn->state = BT_CLOSED;
hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_del(conn);
}
```
Okay, so it iterates through the list, closes each connection, and calls `hci_conn_del` on each. The interesting part here is that `hci_disconn_cfm` is called *before* `hci_conn_del`.
The next point I considered is other removal paths. Specifically, the `l2cap_conn_del` function. Does it cancel `conn->info_timer`? Yes, it does. Okay, it handles the timer, so that's good.
But, if `l2cap_conn_del` *isn't* called for some reason, how does it get cleaned up? `hci_conn_del` doesn't call `l2cap_disconn_cfm`, so there's a difference in behavior here. That's a point of concern.
Lastly, and more generally, I need to consider all the places `hci_conn_del` might be called, not just those that originate from `hci_conn_hash_flush`. What happens if it's called from `hci_conn_timeout`, for example? I need to think through these various potential call paths and ensure that this removal logic is sound, especially in the face of concurrent accesses to the connection data structures.
|
| 55/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_timeout]
Results: map[SourceCode: 626: static void hci_conn_timeout(struct work_struct *work)
627: {
628: struct hci_conn *conn = container_of(work, struct hci_conn,
629: disc_work.work);
630: int refcnt = atomic_read(&conn->refcnt);
631:
632: BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
633:
634: WARN_ON(refcnt < 0);
635:
636: /* FIXME: It was observed that in pairing failed scenario, refcnt
637: * drops below 0. Probably this is because l2cap_conn_del calls
638: * l2cap_chan_del for each channel, and inside l2cap_chan_del conn is
639: * dropped. After that loop hci_chan_del is called which also drops
640: * conn. For now make sure that ACL is alive if refcnt is higher then 0,
641: * otherwise drop it.
642: */
643: if (refcnt > 0)
644: return;
645:
646: hci_abort_conn(conn, hci_proto_disconn_ind(conn));
647: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 56/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 45156
output: 44
thoughts: 53
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this Bluetooth HCI code:
**Analyzing Connection Abort Logic**
Right, so `hci_abort_conn` is the function that's supposed to handle the graceful (or not-so-graceful) disconnection of a Bluetooth connection. And as expected, it's using `hci_cmd_sync_queue` to actually *do* the work β queuing up a command, specifically `abort_conn_sync`. Makes sense; that's the standard way we manage these commands within the HCI layer, ensuring proper sequencing and synchronization. It's the go-to pattern.
But hang on a second... what if `hci_conn_del` is called *directly*? I need to think about that. Is there a race condition? Does `hci_conn_del` bypass this queued abort command? Are there situations where that direct call is intended, or is it a sign of a deeper issue that could lead to inconsistent state? I need to consider all the ways a connection can be removed, and ensure that the abort process is robust in all scenarios. Time to review how that deletion path is coded and its relationship to the standard disconnect process. This is an important consideration for ensuring link integrity, especially with how critical things can be in a wireless connection scenario.
|
| 57/2 |
2026/06/01 04:35 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeSnippetLines:2 Name:hci_conn_del]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_big_complete SourceFile:net/bluetooth/hci_conn.c SourceLine:2288 SourceSnippet:2286: bt_dev_err(hdev, "Unable to create BIG: %d", err);
2287: hci_connect_cfm(conn, err);
2288: hci_conn_del(conn);
2289: }
2290:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_abort_conn_sync SourceFile:net/bluetooth/hci_sync.c SourceLine:5761 SourceSnippet:5759: conn->state = BT_CLOSED;
5760: hci_disconn_cfm(conn, reason);
5761: hci_conn_del(conn);
5762: } else {
5763: hci_conn_failed(conn, reason);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cc_le_setup_iso_path SourceFile:net/bluetooth/hci_event.c SourceLine:3915 SourceSnippet:3913: if (rp->status) {
3914: hci_connect_cfm(conn, rp->status);
3915: hci_conn_del(conn);
3916: goto unlock;
3917: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_failed SourceFile:net/bluetooth/hci_conn.c SourceLine:1410 SourceSnippet:1408: conn->state = BT_CLOSED;
1409: hci_connect_cfm(conn, status);
1410: hci_conn_del(conn);
1411: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_hash_flush SourceFile:net/bluetooth/hci_conn.c SourceLine:2735 SourceSnippet:2733: conn->state = BT_CLOSED;
2734: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
2735: hci_conn_del(conn);
2736: }
2737: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_acl SourceFile:net/bluetooth/hci_conn.c SourceLine:1787 SourceSnippet:1785: err = hci_connect_acl_sync(hdev, acl);
1786: if (err) {
1787: hci_conn_del(acl);
1788: return ERR_PTR(err);
1789: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_le SourceFile:net/bluetooth/hci_conn.c SourceLine:1512 SourceSnippet:1510: err = hci_connect_le_sync(hdev, conn);
1511: if (err) {
1512: hci_conn_del(conn);
1513: return ERR_PTR(err);
1514: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_le_scan SourceFile:net/bluetooth/hci_conn.c SourceLine:1726 SourceSnippet:1724:
1725: if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0) {
1726: hci_conn_del(conn);
1727: return ERR_PTR(-EBUSY);
1728: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_add_sco SourceFile:net/bluetooth/hci_event.c SourceLine:2313 SourceSnippet:2311:
2312: hci_connect_cfm(link->conn, status);
2313: hci_conn_del(link->conn);
2314: }
2315: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_create_conn SourceFile:net/bluetooth/hci_event.c SourceLine:2269 SourceSnippet:2267: conn->state = BT_CLOSED;
2268: hci_connect_cfm(conn, status);
2269: hci_conn_del(conn);
2270: }
2271: } else {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_disconnect SourceFile:net/bluetooth/hci_event.c SourceLine:2772 SourceSnippet:2770: * advertising if necessary.
2771: */
2772: hci_conn_del(conn);
2773: unlock:
2774: hci_dev_unlock(hdev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_le_create_cis SourceFile:net/bluetooth/hci_event.c SourceLine:4353 SourceSnippet:4351: conn->state = BT_CLOSED;
4352: hci_connect_cfm(conn, status);
4353: hci_conn_del(conn);
4354: }
4355: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_disconn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3482 SourceSnippet:3480: }
3481:
3482: hci_conn_del(conn);
3483:
3484: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_big_sync_lost_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7278 SourceSnippet:7276: clear_bit(HCI_CONN_BIG_SYNC, &bis->flags);
7277: hci_disconn_cfm(bis, ev->reason);
7278: hci_conn_del(bis);
7279: }
7280:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_cis_established_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7009 SourceSnippet:7007: conn->state = BT_CLOSED;
7008: hci_connect_cfm(conn, ev->status);
7009: hci_conn_del(conn);
7010:
7011: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7117 SourceSnippet:7115: if (ev->status) {
7116: hci_connect_cfm(conn, ev->status);
7117: hci_conn_del(conn);
7118: continue;
7119: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7127 SourceSnippet:7125: ev->status = HCI_ERROR_UNSPECIFIED;
7126: hci_connect_cfm(conn, ev->status);
7127: hci_conn_del(conn);
7128: continue;
7129: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7141 SourceSnippet:7139: ev->status = HCI_ERROR_UNSPECIFIED;
7140: hci_connect_cfm(conn, ev->status);
7141: hci_conn_del(conn);
7142: continue;
7143: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_conn_sync SourceFile:net/bluetooth/hci_sync.c SourceLine:6605 SourceSnippet:6603: hdev->le_scan_type == LE_SCAN_ACTIVE &&
6604: !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
6605: hci_conn_del(conn);
6606: return -EBUSY;
6607: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_pa_sync_lost_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5929 SourceSnippet:5927: clear_bit(HCI_CONN_PA_SYNC, &conn->flags);
5928: hci_disconn_cfm(conn, HCI_ERROR_REMOTE_USER_TERM);
5929: hci_conn_del(conn);
5930: }
5931:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sco_setup SourceFile:net/bluetooth/hci_conn.c SourceLine:622 SourceSnippet: 620: } else {
621: hci_connect_cfm(link->conn, status);
622: hci_conn_del(link->conn);
623: }
624: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_setup_sync_conn_status SourceFile:net/bluetooth/hci_event.c SourceLine:2596 SourceSnippet:2594:
2595: hci_connect_cfm(link->conn, status);
2596: hci_conn_del(link->conn);
2597: }
2598: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sync_conn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5115 SourceSnippet:5113: hci_connect_cfm(conn, status);
5114: if (status)
5115: hci_conn_del(conn);
5116:
5117: unlock:
]] TruncatedOutput:false]
|
| 58/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 48511
output: 38
thoughts: 42
Thoughts: Okay, let's break this down.
**Potential Logic Flaw in Bluetooth Connection Management**
Hold on a second... this doesn't seem right. I'm looking at `hci_cs_disconnect` and it's calling `hci_conn_del(conn)`. But... it's doing that *without* first calling `hci_disconn_cfm`? That's a red flag. That's unusual behavior in Bluetooth connection management. Usually, you'd expect the disconnection confirmation to happen *before* deleting the connection structure, signaling a clean shutdown to other parts of the system. Let me confirm my suspicion with a careful review of `hci_cs_disconnect` because there might be a race condition. I need to understand the sequencing precisely to be sure I haven't missed something, or worse that there is a fundamental flaw. I'm going to start by tracing the function call and variables, looking for when it happens. Let's see...
|
| 59/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_event.c Name:hci_cs_disconnect]
Results: map[SourceCode:2689: static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
2690: {
2691: struct hci_cp_disconnect *cp;
2692: struct hci_conn_params *params;
2693: struct hci_conn *conn;
2694: bool mgmt_conn;
2695:
2696: bt_dev_dbg(hdev, "status 0x%2.2x", status);
2697:
2698: /* Wait for HCI_EV_DISCONN_COMPLETE if status 0x00 and not suspended
2699: * otherwise cleanup the connection immediately.
2700: */
2701: if (!status && !hdev->suspended)
2702: return;
2703:
2704: cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT);
2705: if (!cp)
2706: return;
2707:
2708: hci_dev_lock(hdev);
2709:
2710: conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
2711: if (!conn)
2712: goto unlock;
2713:
2714: if (status && status != HCI_ERROR_UNKNOWN_CONN_ID) {
2715: mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
2716: conn->dst_type, status);
2717:
2718: if (conn->type == LE_LINK && conn->role == HCI_ROLE_SLAVE) {
2719: hdev->cur_adv_instance = conn->adv_instance;
2720: hci_enable_advertising(hdev);
2721: }
2722:
2723: /* Inform sockets conn is gone before we delete it */
2724: hci_disconn_cfm(conn, HCI_ERROR_UNSPECIFIED);
2725:
2726: goto done;
2727: }
2728:
2729: /* During suspend, mark connection as closed immediately
2730: * since we might not receive HCI_EV_DISCONN_COMPLETE
2731: */
2732: if (hdev->suspended)
2733: conn->state = BT_CLOSED;
2734:
2735: mgmt_conn = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
2736:
2737: if (conn->type == ACL_LINK) {
2738: if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
2739: hci_remove_link_key(hdev, &conn->dst);
2740: }
2741:
2742: params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
2743: if (params) {
2744: switch (params->auto_connect) {
2745: case HCI_AUTO_CONN_LINK_LOSS:
2746: if (cp->reason != HCI_ERROR_CONNECTION_TIMEOUT)
2747: break;
2748: fallthrough;
2749:
2750: case HCI_AUTO_CONN_DIRECT:
2751: case HCI_AUTO_CONN_ALWAYS:
2752: hci_pend_le_list_del_init(params);
2753: hci_pend_le_list_add(params, &hdev->pend_le_conns);
2754: break;
2755:
2756: default:
2757: break;
2758: }
2759: }
2760:
2761: mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
2762: cp->reason, mgmt_conn);
2763:
2764: hci_disconn_cfm(conn, cp->reason);
2765:
2766: done:
2767: /* If the disconnection failed for any reason, the upper layer
2768: * does not retry to disconnect in current implementation.
2769: * Hence, we need to do some basic cleanup here and re-enable
2770: * advertising if necessary.
2771: */
2772: hci_conn_del(conn);
2773: unlock:
2774: hci_dev_unlock(hdev);
2775: }
SourceFile:net/bluetooth/hci_event.c]
|
| 60/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 50105
output: 41
thoughts: 32
Thoughts: Okay, here's my interpretation of those thoughts, summarized from an expert's perspective:
**Connection Teardown Sequencing Concerns**
Right, so I'm looking at this connection teardown sequence, and something's just not sitting right. I'm seeing that the `hci_disconn_cfm` confirmation is being called *before* the `hci_conn_del` function, which is, on the face of it, unusual, and potentially problematic. It raises an immediate question: what about the *disconnection complete event*, the `hci_disconn_complete_evt`? Where does that fit into the picture? We need to be absolutely certain we are handling the events and states in the correct order, and this initial order is throwing a red flag. What are the dependencies? What state information is being relied upon? It warrants a closer look at the interaction of these HCI commands and events to ensure data integrity and avoid any race conditions during connection termination. We need to analyze the implications of that particular order.
|
| 61/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_event.c Name:hci_disconn_complete_evt]
Results: map[SourceCode:3396: static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
3397: struct sk_buff *skb)
3398: {
3399: struct hci_ev_disconn_complete *ev = data;
3400: u8 reason;
3401: struct hci_conn_params *params;
3402: struct hci_conn *conn;
3403: bool mgmt_connected;
3404:
3405: bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
3406:
3407: hci_dev_lock(hdev);
3408:
3409: conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
3410: if (!conn)
3411: goto unlock;
3412:
3413: if (ev->status) {
3414: mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
3415: conn->dst_type, ev->status);
3416: goto unlock;
3417: }
3418:
3419: conn->state = BT_CLOSED;
3420:
3421: mgmt_connected = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
3422:
3423: if (test_bit(HCI_CONN_AUTH_FAILURE, &conn->flags))
3424: reason = MGMT_DEV_DISCONN_AUTH_FAILURE;
3425: else
3426: reason = hci_to_mgmt_reason(ev->reason);
3427:
3428: mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
3429: reason, mgmt_connected);
3430:
3431: if (conn->type == ACL_LINK) {
3432: if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
3433: hci_remove_link_key(hdev, &conn->dst);
3434:
3435: hci_update_scan(hdev);
3436: }
3437:
3438: /* Re-enable passive scanning if disconnected device is marked
3439: * as auto-connectable.
3440: */
3441: if (conn->type == LE_LINK) {
3442: params = hci_conn_params_lookup(hdev, &conn->dst,
3443: conn->dst_type);
3444: if (params) {
3445: switch (params->auto_connect) {
3446: case HCI_AUTO_CONN_LINK_LOSS:
3447: if (ev->reason != HCI_ERROR_CONNECTION_TIMEOUT)
3448: break;
3449: fallthrough;
3450:
3451: case HCI_AUTO_CONN_DIRECT:
3452: case HCI_AUTO_CONN_ALWAYS:
3453: hci_pend_le_list_del_init(params);
3454: hci_pend_le_list_add(params,
3455: &hdev->pend_le_conns);
3456: hci_update_passive_scan(hdev);
3457: break;
3458:
3459: default:
3460: break;
3461: }
3462: }
3463: }
3464:
3465: hci_disconn_cfm(conn, ev->reason);
3466:
3467: /* Re-enable advertising if necessary, since it might
3468: * have been disabled by the connection. From the
3469: * HCI_LE_Set_Advertise_Enable command description in
3470: * the core specification (v4.0):
3471: * "The Controller shall continue advertising until the Host
3472: * issues an LE_Set_Advertise_Enable command with
3473: * Advertising_Enable set to 0x00 (Advertising is disabled)
3474: * or until a connection is created or until the Advertising
3475: * is timed out due to Directed Advertising."
3476: */
3477: if (conn->type == LE_LINK && conn->role == HCI_ROLE_SLAVE) {
3478: hdev->cur_adv_instance = conn->adv_instance;
3479: hci_enable_advertising(hdev);
3480: }
3481:
3482: hci_conn_del(conn);
3483:
3484: unlock:
3485: hci_dev_unlock(hdev);
3486: }
SourceFile:net/bluetooth/hci_event.c]
|
| 62/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 51774
output: 44
thoughts: 113
Thoughts: **Analyzing Potential Race Conditions in L2CAP Connection Deletion**
Okay, so I'm looking closely at the connection teardown sequence here, and a potential race condition has just surfaced in my mind. Specifically, I see that `hci_disconn_cfm` is being called *before* `hci_conn_del`. That's a standard pattern, but it got me thinking. The real question is this: what happens if `l2cap_conn_del` is triggered, but the timer `conn->info_timer` *hasn't* been cancelled yet?
I need to confirm if that's a possibility. Ah, good. I see that `l2cap_conn_del` *does* include a call to `disable_delayed_work_sync(&conn->info_timer)`. But, now I'm thinking, what if `conn->info_timer` gets *scheduled* after `l2cap_conn_del` has already begun to execute? That's the crux of the issue. The timer could still fire after it's been "disabled" causing all sorts of mayhem.
So, I need to understand where `conn->info_timer` is scheduled in the first place, or if it's possible to execute this task after the timer has been disabled. I need to trace this back and map out all possible execution paths. I need to verify how and when it's kicked off. That's the next step.
|
| 63/2 |
2026/06/01 04:35 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:info_timer]
Results: map[References:<nil> TruncatedOutput:false]
|
| 64/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 52264
output: 34
thoughts: 18
Thoughts: Okay, here's my summary, in the first person, as requested, assuming the user is an expert:
**Investigating the Data**
Alright, a new data set. Immediately, my mind goes to the most common starting point for analysis: the core elements. Let's start by trying to identify anything obviously linked to timing, since that's a frequent point of interest and where I suspect we'll find some initial insights. I'm going to conduct a preliminary search specifically for any occurrences of `"info_timer"`. This term is frequently used across projects, so a quick keyword search will help narrow down the field for deeper investigation. This will give me a baseline to work from.
|
| 65/2 |
2026/06/01 04:35 |
tool |
grepper |
0m
Args: map[Expression:info_timer PathPrefix:net/bluetooth/l2cap_core.c]
Results: map[Output:net/bluetooth/l2cap_core.c=1412=static void l2cap_request_info(struct l2cap_conn *conn)
--
net/bluetooth/l2cap_core.c-1423-
net/bluetooth/l2cap_core.c:1424: schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
net/bluetooth/l2cap_core.c-1425-
--
net/bluetooth/l2cap_core.c=1676=static void l2cap_info_timeout(struct work_struct *work)
--
net/bluetooth/l2cap_core.c-1678- struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
net/bluetooth/l2cap_core.c:1679: info_timer.work);
net/bluetooth/l2cap_core.c-1680-
--
net/bluetooth/l2cap_core.c=1764=static void l2cap_conn_del(struct hci_conn *hcon, int err)
--
net/bluetooth/l2cap_core.c-1773-
net/bluetooth/l2cap_core.c:1774: disable_delayed_work_sync(&conn->info_timer);
net/bluetooth/l2cap_core.c-1775- disable_delayed_work_sync(&conn->id_addr_timer);
--
net/bluetooth/l2cap_core.c=3983=static inline int l2cap_command_rej(struct l2cap_conn *conn,
--
net/bluetooth/l2cap_core.c-3996- cmd->ident == conn->info_ident) {
net/bluetooth/l2cap_core.c:3997: cancel_delayed_work(&conn->info_timer);
net/bluetooth/l2cap_core.c-3998-
--
net/bluetooth/l2cap_core.c=4008=static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
--
net/bluetooth/l2cap_core.c-4119-
net/bluetooth/l2cap_core.c:4120: schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
net/bluetooth/l2cap_core.c-4121-
--
net/bluetooth/l2cap_core.c=4608=static inline int l2cap_information_rsp(struct l2cap_conn *conn,
--
net/bluetooth/l2cap_core.c-4627-
net/bluetooth/l2cap_core.c:4628: cancel_delayed_work(&conn->info_timer);
net/bluetooth/l2cap_core.c-4629-
--
net/bluetooth/l2cap_core.c=6989=static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
--
net/bluetooth/l2cap_core.c-7028-
net/bluetooth/l2cap_core.c:7029: INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
net/bluetooth/l2cap_core.c-7030- ida_init(&conn->tx_ida);
]
|
| 66/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 53329
output: 42
thoughts: 39
Thoughts: Here's a summary of my thought process:
**Analyzing the L2CAP Timer Implementation**
Okay, so I'm focusing on the timer management within L2CAP. Specifically, I'm thinking about where the `info_timer` is scheduled. My initial observation is that it's being set up in both the `l2cap_request_info` and `l2cap_connect` functions. I need to understand why it's used in both. The next logical step is to dive into the `l2cap_request_info` function and examine its role. I need to understand what this function is doing and how the timer fits into the data flow, especially within an expert context where these timers are critical for connection stability and protocol efficiency.
|
| 67/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_request_info]
Results: map[SourceCode:1412: static void l2cap_request_info(struct l2cap_conn *conn)
1413: {
1414: struct l2cap_info_req req;
1415:
1416: if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
1417: return;
1418:
1419: req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
1420:
1421: conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
1422: conn->info_ident = l2cap_get_ident(conn);
1423:
1424: schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
1425:
1426: l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
1427: sizeof(req), &req);
1428: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 68/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 53891
output: 48
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:
**L2CAP Request Info: A Question of Timing**
Alright, so the question that's rattling around in my head is: when precisely does `l2cap_request_info` get invoked? I'm trying to pinpoint the critical points in the L2CAP connection establishment and information exchange process. I need to understand the trigger events β is it during the initial connection setup, maybe triggered by an incoming connection request? Or is it more reactive, coming into play when a specific parameter negotiation is needed, a PSM lookup or some other service-level details are required post-connection?
I'm thinking about the different L2CAP signaling messages; is it tied to the `Connection Request`, `Connection Response`, `Configuration Request`, or `Configuration Response` exchanges? Does it get called when the peer sends a `Information Request`? Or is it a more proactive mechanism, initiated by the local stack at specific phases of the connection establishment or data transfer when the stack needs to know some detail or parameter about the remote device or service?
Essentially, I need to chart out the call graph here. The exact context, the relevant handlers, and the relevant data structures I need to check to trace the call to its origins, and determine the exact workflow. It's that fine-grained understanding of the sequence of operations that I'm after. It's a key piece in ensuring robust, efficient L2CAP implementations that I work on.
|
| 69/2 |
2026/06/01 04:35 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:l2cap_request_info]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:l2cap_conn_ready SourceFile:net/bluetooth/l2cap_core.c SourceLine:1635 SourceSnippet:1633:
1634: if (hcon->type == ACL_LINK)
1635: l2cap_request_info(conn);
1636:
1637: mutex_lock(&conn->lock);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:l2cap_do_start SourceFile:net/bluetooth/l2cap_core.c SourceLine:1462 SourceSnippet:1460:
1461: if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
1462: l2cap_request_info(conn);
1463: return;
1464: }
]] TruncatedOutput:false]
|
| 70/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 54550
output: 42
thoughts: 159
Thoughts: **L2CAP Connection Race Condition Concern**
Okay, so I'm digging into this L2CAP connection handling, specifically the potential for a race condition. My primary concern revolves around the sequence of events and whether `l2cap_conn_ready` might be called *after* `l2cap_conn_del`. That would be a serious problem. I see `l2cap_conn_ready` is triggered from `l2cap_connect_cfm` and `l2cap_security_cfm`. If the timing is such that a delete happens *before* the ready, then we could be in trouble.
Wait a second, if `l2cap_conn_ready` were indeed to be called after `l2cap_conn_del`, there is a chance the `info_timer` would be rescheduled, which is exactly the kind of bug I am trying to avoid here. But, `l2cap_conn_del` explicitly sets both `hcon->l2cap_data = NULL;` and `conn->hchan = NULL;`. This means even if the function is called out of sequence, these pointers should be nullified. So, if that's the case, we should be fine. I'm going to quickly check `l2cap_connect_cfm` to make sure there are no other potential pitfalls.
|
| 71/2 |
2026/06/01 04:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_connect_cfm]
Results: map[SourceCode:7361: static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
7362: {
7363: struct hci_dev *hdev = hcon->hdev;
7364: struct l2cap_conn *conn;
7365: struct l2cap_chan *pchan;
7366: u8 dst_type;
7367:
7368: if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
7369: return;
7370:
7371: BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
7372:
7373: if (status) {
7374: l2cap_conn_del(hcon, bt_to_errno(status));
7375: return;
7376: }
7377:
7378: conn = l2cap_conn_add(hcon);
7379: if (!conn)
7380: return;
7381:
7382: dst_type = bdaddr_dst_type(hcon);
7383:
7384: /* If device is blocked, do not create channels for it */
7385: if (hci_bdaddr_list_lookup(&hdev->reject_list, &hcon->dst, dst_type))
7386: return;
7387:
7388: /* Find fixed channels and notify them of the new connection. We
7389: * use multiple individual lookups, continuing each time where
7390: * we left off, because the list lock would prevent calling the
7391: * potentially sleeping l2cap_chan_lock() function.
7392: */
7393: pchan = l2cap_global_fixed_chan(NULL, hcon);
7394: while (pchan) {
7395: struct l2cap_chan *chan, *next;
7396:
7397: /* Client fixed channels should override server ones */
7398: if (__l2cap_get_chan_by_dcid(conn, pchan->scid))
7399: goto next;
7400:
7401: l2cap_chan_lock(pchan);
7402: chan = pchan->ops->new_connection(pchan);
7403: if (chan) {
7404: bacpy(&chan->src, &hcon->src);
7405: bacpy(&chan->dst, &hcon->dst);
7406: chan->src_type = bdaddr_src_type(hcon);
7407: chan->dst_type = dst_type;
7408:
7409: __l2cap_chan_add(conn, chan);
7410: }
7411:
7412: l2cap_chan_unlock(pchan);
7413: next:
7414: next = l2cap_global_fixed_chan(pchan, hcon);
7415: l2cap_chan_put(pchan);
7416: pchan = next;
7417: }
7418:
7419: l2cap_conn_ready(conn);
7420: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 72/2 |
2026/06/01 04:35 |
llm |
expert |
0m
Model:
Tokens:
input: 55982
output: 38
thoughts: 2240
Thoughts: **Analyzing a Potential Race Condition in Bluetooth Connection Handling**
Okay, so here's the situation I'm looking at: we've got a sequence where `l2cap_connect_cfm` is called, and depending on the `status`, it may add or delete a connection. My concern is around concurrency, particularly if `hci_dev_close_sync` is also running simultaneously. This function is important since it orchestrates a clean shutdown of a Bluetooth device. It does things like flushing connection hashes (`hci_conn_hash_flush`) and ultimately cleaning up.
The critical path looks like this: if `l2cap_connect_cfm` runs *after* `l2cap_conn_del` (which is called by `hci_disconn_cfm` during shutdown), a new connection is added, and then the critical `info_timer` is scheduled. However, `hci_dev_close_sync` flushes all connections during shutdown so it seems like the system is getting into a weird state. It seems possible for a new connection to be added after the flush.
The key question is whether a connection event (like `HCI_EV_CONN_COMPLETE`) can come in *after* `hci_conn_hash_flush` but before the device is fully shut down. `hci_dev_close_sync` flushes queues. Initially, I thought `rx_work` could still run but then I had to re-evaluate after looking at the code. `hci_dev_close_sync` explicitly flushes `rx_work` and `tx_work` *before* the hash is flushed, which seems like a big red flag. However, the driver is still open at this point. After all queues are flushed, `hdev->close(hdev)` is called to close the driver!
So, the driver might still be running and able to handle a connection event which calls `hci_recv_frame`, that would then queue the skb to the receive queue and schedule `hdev->rx_work`. Then, `rx_work` would be processed, and this would call `l2cap_connect_cfm` *after* the hash has been flushed. This would add a connection and schedule that `info_timer`.
If `hci_dev_close_sync` finishes, and eventually `hci_unregister_dev` is called, and *then* the `info_timer` fires, it calls `l2cap_info_timeout`, which queues `tx_work` on `hdev->workqueue`. However, `hdev->workqueue` is destroyed much later in the process, during `hci_release_dev`. It seems like a classic use-after-free situation where the worker queue is destroyed and then a work item is queued on it. This is a use-after-destroy!
So, I had to confirm if `hdev->rx_work` can run *after* `hci_conn_hash_flush` is called and discovered the answer is no. `hci_unregister_dev` calls `disable_work_sync(&hdev->rx_work)` *before* the driver is closed which prevents the work from being queued.
This seems to eliminate a path to the vulnerability!
Now I need to check other paths. What about the connection timeout worker? That must be disabled too. Good! Then what about `l2cap_info_timeout`?
So `l2cap_info_timeout` should be okay, and not deadlock. Okay, but what if `l2cap_info_timeout` tries to send a packet? It would queue work on `hdev->workqueue`. If that happens, then we could get the use-after-free issue. That's a possibility. `hdev->workqueue` will be destroyed after `hci_unregister_dev` has finished, after `l2cap_info_timeout` finishes, so that should be okay.
And then I saw the message, `workqueue: cannot queue hci_tx_work on wq hci0`. Then the issue became clear: `hci_release_dev` is running and the workqueue has been destroyed. This confirms the race condition where `l2cap_info_timeout` can run after the workqueue is destroyed! And finally I understand why the `hci_tx_work` is failing.
|
| 73/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_add]
Results: map[SourceCode:1160: struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
1161: u8 dst_type, u8 role, u16 handle)
1162: {
1163: if (handle > HCI_CONN_HANDLE_MAX)
1164: return ERR_PTR(-EINVAL);
1165:
1166: return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
1167: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 74/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 59363
output: 38
thoughts: 85
Thoughts: Okay, here's my breakdown of what's happening in this Bluetooth connection management code, speaking as someone familiar with the HCI layer:
**Understanding HCI Connection Reference Counting**
Alright, I'm diving into this `__hci_conn_add` function. First, I see `atomic_set(&conn->refcnt, 0);`. That's standard initialization of a reference count for the connection object itself. Makes sense, we're tracking how many users are currently interested in this `hci_conn`. Then comes the crucial bit: `hci_dev_hold(hdev);`. Ah, there it is! The `hci_conn` is increasing the reference count of the `hdev`, meaning the connection now considers the HCI device held in relation to itself. And finally, we hash the connection in the device table with `hci_conn_hash_add(hdev, conn)`.
So, the key takeaway is that the `hci_conn` struct directly increases the reference count on the associated `hdev`. This setup ensures that the `hdev` object is held as long as any connection uses it. This is normal practice, this way the connection object knows the HCI device is still there.
Now the question is where is the reference released? I'm betting that's handled in the `hci_conn_del` function, and that's the next place I need to investigate. That's where the decrementing of the `hdev` refcount will occur in the case the connection object is deleted and the reference should be released. I expect to see `hci_dev_put(hdev)` at that point.
|
| 75/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_del]
Results: map[SourceCode:1237: void hci_conn_del(struct hci_conn *conn)
1238: {
1239: struct hci_dev *hdev = conn->hdev;
1240:
1241: BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
1242:
1243: hci_conn_unlink(conn);
1244:
1245: disable_delayed_work_sync(&conn->disc_work);
1246: disable_delayed_work_sync(&conn->auto_accept_work);
1247: disable_delayed_work_sync(&conn->idle_work);
1248:
1249: /* Remove the connection from the list so unacked logic can detect when
1250: * a certain pool is not being utilized.
1251: */
1252: hci_conn_hash_del(hdev, conn);
1253:
1254: /* Handle unacked frames:
1255: *
1256: * - In case there are no connection, or if restoring the buffers
1257: * considered in transist would overflow, restore all buffers to the
1258: * pool.
1259: * - Otherwise restore just the buffers considered in transit for the
1260: * hci_conn
1261: */
1262: switch (conn->type) {
1263: case ACL_LINK:
1264: if (!hci_conn_num(hdev, ACL_LINK) ||
1265: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1266: hdev->acl_cnt = hdev->acl_pkts;
1267: else
1268: hdev->acl_cnt += conn->sent;
1269: break;
1270: case LE_LINK:
1271: cancel_delayed_work(&conn->le_conn_timeout);
1272:
1273: if (hdev->le_pkts) {
1274: if (!hci_conn_num(hdev, LE_LINK) ||
1275: hdev->le_cnt + conn->sent > hdev->le_pkts)
1276: hdev->le_cnt = hdev->le_pkts;
1277: else
1278: hdev->le_cnt += conn->sent;
1279: } else {
1280: if ((!hci_conn_num(hdev, LE_LINK) &&
1281: !hci_conn_num(hdev, ACL_LINK)) ||
1282: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1283: hdev->acl_cnt = hdev->acl_pkts;
1284: else
1285: hdev->acl_cnt += conn->sent;
1286: }
1287: break;
1288: case CIS_LINK:
1289: case BIS_LINK:
1290: case PA_LINK:
1291: if (!hci_iso_count(hdev) ||
1292: hdev->iso_cnt + conn->sent > hdev->iso_pkts)
1293: hdev->iso_cnt = hdev->iso_pkts;
1294: else
1295: hdev->iso_cnt += conn->sent;
1296: break;
1297: }
1298:
1299: skb_queue_purge(&conn->data_q);
1300: skb_queue_purge(&conn->tx_q.queue);
1301:
1302: /* Remove the connection from the list and cleanup its remaining
1303: * state. This is a separate function since for some cases like
1304: * BT_CONNECT_SCAN we *only* want the cleanup part without the
1305: * rest of hci_conn_del.
1306: */
1307: hci_conn_cleanup(conn);
1308:
1309: /* Dequeue callbacks using connection pointer as data */
1310: hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
1311: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 76/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 61041
output: 38
thoughts: 21
Thoughts: Here's my take on the thought process, as if it were my own:
**Connecting the Dots: hci_conn_cleanup and hci_dev_put**
Okay, hold on a second. I'm following the logic here, tracing the connection cleanup procedure... and there it is. The `hci_conn_cleanup` function β that's the key. I was just thinking about the cleanup process for Bluetooth connections, and it's popping into view. Now, I see that this function, during its operation, directly calls `hci_dev_put(hdev)`. This is a crucial link, and it immediately makes me think about the reference counting for the HCI device itself.
|
| 77/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_cleanup]
Results: map[SourceCode: 140: static void hci_conn_cleanup(struct hci_conn *conn)
141: {
142: struct hci_dev *hdev = conn->hdev;
143:
144: if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
145: hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
146:
147: if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
148: hci_remove_link_key(hdev, &conn->dst);
149:
150: hci_chan_list_flush(conn);
151:
152: if (HCI_CONN_HANDLE_UNSET(conn->handle))
153: ida_free(&hdev->unset_handle_ida, conn->handle);
154:
155: if (conn->cleanup)
156: conn->cleanup(conn);
157:
158: if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
159: switch (conn->setting & SCO_AIRMODE_MASK) {
160: case SCO_AIRMODE_CVSD:
161: case SCO_AIRMODE_TRANSP:
162: if (hdev->notify)
163: hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
164: break;
165: }
166: } else {
167: if (hdev->notify)
168: hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
169: }
170:
171: debugfs_remove_recursive(conn->debugfs);
172:
173: hci_conn_del_sysfs(conn);
174:
175: hci_dev_put(hdev);
176: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 78/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 61850
output: 42
thoughts: 358
Thoughts: **Debugging a Concurrency Issue in Bluetooth Connection Cleanup**
Okay, so `hci_conn_cleanup` calls `hci_dev_put(hdev)`. Right, that makes sense. Therefore, `hci_conn` is holding a reference to `hdev`. If those connections are *not* deleted, `hdev` can't be released; consequently, `hdev->workqueue` can't be destroyed.
But wait a minute⦠if the workqueue *is* being destroyed, that means `hci_release_dev` is running. And if `hci_release_dev` is running, then the `hdev`'s reference count must be zero! If the reference count is zero, that means *all* `hci_conn` objects have been deleted. So, `hci_conn_del` must have been called on every one of them. And if `hci_conn_del` was called on all of them, `hci_conn_cleanup` *must* have been called.
Hold on a second... isn't `l2cap_conn_del` called from `hci_disconn_cfm`, and that happens *before* `hci_conn_del`? So, `l2cap_conn_del` *has* been called, and the `disable_delayed_work_sync(&conn->info_timer)` call has run. This implies the `info_timer` *must* have finished.
So, how can `l2cap_info_timeout` *still* be running if the `info_timer` is finished and the connection `conn` is already freed?
Wait, what *is* `conn`? Ah, right, `conn` is a `struct l2cap_conn`. Is the `conn` *freed*? `l2cap_conn_del` calls `l2cap_conn_put(conn)`. I need to check `l2cap_conn_put`. Let's see what that does.
|
| 79/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_conn_put]
Results: map[SourceCode:1833: void l2cap_conn_put(struct l2cap_conn *conn)
1834: {
1835: kref_put(&conn->ref, l2cap_conn_free);
1836: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 80/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 62778
output: 42
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my thought process on what we're looking at, summarized in the first person, assuming I'm already pretty familiar with this area:
**Analyzing `l2cap_conn_free`**
Alright, *`l2cap_conn_free`*. That's the function we're focusing on. My initial thought is to figure out *why* we're examining this specific function. Is this related to a memory leak investigation? Are we debugging a connection teardown issue in L2CAP? Or maybe we're revisiting some old code for optimization or refactoring? The name itself screams "cleanup," so I'm expecting this is where resources associated with an L2CAP connection are being released. I'll need to figure out what those resources are, how they are being managed, and what the potential points of failure might be. I'll probably want to mentally trace through the call stack to see where this function is being called from, and what the overall context is. I want to look for potential race conditions, or anything that could cause a connection to be prematurely or incorrectly cleaned up. This feels like an important piece of the L2CAP infrastructure and I need to fully understand its function.
|
| 81/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c Name:l2cap_conn_free]
Results: map[SourceCode:1818: static void l2cap_conn_free(struct kref *ref)
1819: {
1820: struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
1821:
1822: hci_conn_put(conn->hcon);
1823: kfree(conn);
1824: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 82/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 63237
output: 38
thoughts: 56
Thoughts: **Realizing the Connection Lifecycle**
Hold on a second! I'm tracing through this Bluetooth connection teardown, and I just noticed something critical in `l2cap_conn_free`. It calls `hci_conn_put(conn->hcon)`. Aha! So, `l2cap_conn` actually *holds a reference* to the underlying `hci_conn`! This is key to understanding how these objects are managed and how resources are released. I need to make sure I understand the reference counting properly. Let's dig into the implementation of `hci_conn_put` to confirm my understanding and see exactly what it's doing with that `hci_conn` object. This is likely the crucial step where the Bluetooth connection is truly released.
|
| 83/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_put]
Results: map[SourceCode:1671: static inline void hci_conn_put(struct hci_conn *conn)
1672: {
1673: put_device(&conn->dev);
1674: }
SourceFile:include/net/bluetooth/hci_core.h]
|
| 84/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 63579
output: 41
thoughts: 63
Thoughts: Okay, let me break this down.
**Understanding the Bluetooth HCI Connection Lifecycle**
Alright, so I'm looking at this code and need to understand the connection teardown process. The function `hci_conn_put` is defined in `include/net/bluetooth/hci_core.h`, which makes sense; this is the core Bluetooth HCI functionality. Now, the key is what happens inside. I see that `hci_conn_put` calls `put_device(&conn->dev)`. This is where the magic probably happens in terms of resource management, especially when the connection object is no longer needed. The call to `put_device()` implies some sort of reference counting mechanism for the device associated with this connection, `conn->dev`. This is fairly standard.
When the reference count of `conn->dev` reaches zero, that's when things get really interesting. It triggers a call to `hci_conn_dev_release`. That would be where the associated resources are actually freed, the device structure is deallocated, and the connection finally disappears. I need to dig deeper into `hci_conn_dev_release`, but this gives me a good start on the connection lifecycle and release logic.
|
| 85/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/bluetooth/hci_sysfs.c Name:hci_conn_dev_release]
Results: map[SourceCode: SourceFile:]
|
| 86/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 63957
output: 43
thoughts: 17
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Debugging Strategy: Identifying Connection Release**
Okay, so I'm knee-deep in this, and it seems like a connection isn't being properly released. My immediate instinct is to start digging into the connection management code. I need to pinpoint the function responsible for actually closing or releasing a connection, which is likely where the issue resides. Let's start with a focused search. I'll begin by looking for a function with a name that suggests connection device release, something along the lines of `hci_conn_dev_release`. It's possible the naming scheme is slightly different depending on the specific codebase, so I'll also try variations, or at least similar keywords. The goal is to rapidly locate the relevant function within the layers of abstraction, then from there I can assess the call paths and relevant variables.
|
| 87/2 |
2026/06/01 04:36 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/hci_sysfs.c IncludeSnippetLines:2 Name:put_device]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__add_memory_block SourceFile:drivers/base/memory.c SourceLine:704 SourceSnippet: 702: ret = device_register(&memory->dev);
703: if (ret) {
704: put_device(&memory->dev);
705: return ret;
706: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__cpu_device_create SourceFile:drivers/base/cpu.c SourceLine:491 SourceSnippet: 489:
490: error:
491: put_device(dev);
492: return ERR_PTR(retval);
493: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__devcd_del SourceFile:drivers/base/devcoredump.c SourceLine:98 SourceSnippet: 96: devcd->deleted = true;
97: device_del(&devcd->devcd_dev);
98: put_device(&devcd->devcd_dev);
99: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__device_attach_async_helper SourceFile:drivers/base/dd.c SourceLine:1068 SourceSnippet:1066: device_unlock(dev);
1067:
1068: put_device(dev);
1069: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__devm_create_dev_dax SourceFile:drivers/dax/bus.c SourceLine:1539 SourceSnippet:1537: if (rc) {
1538: kill_dev_dax(dev_dax);
1539: put_device(dev);
1540: return ERR_PTR(rc);
1541: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__dpm_async SourceFile:drivers/base/power/main.c SourceLine:644 SourceSnippet: 642: return true;
643:
644: put_device(dev);
645:
646: return false;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__driver_attach_async_helper SourceFile:drivers/base/dd.c SourceLine:1232 SourceSnippet:1230: dev_dbg(dev, "driver %s async attach completed: %d\n", drv->name, ret);
1231:
1232: put_device(dev);
1233: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__free_put_device SourceFile:include/linux/device.h SourceLine:1310 SourceSnippet:1310: DEFINE_FREE(put_device, struct device *, if (_T) put_device(_T))
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__fw_devlink_link_to_consumers SourceFile:drivers/base/core.c SourceLine:2246 SourceSnippet:2244: if (con_dev &&
2245: fwnode_is_ancestor_of(con_dev->fwnode, fwnode)) {
2246: put_device(con_dev);
2247: con_dev = NULL;
2248: } else {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__fw_devlink_link_to_consumers SourceFile:drivers/base/core.c SourceLine:2257 SourceSnippet:2255:
2256: ret = fw_devlink_create_devlink(con_dev, fwnode, link);
2257: put_device(con_dev);
2258: if (!own_link || ret == -EAGAIN)
2259: continue;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__fw_devlink_relax_cycles SourceFile:drivers/base/core.c SourceLine:2078 SourceSnippet:2076: out:
2077: fwnode_clear_flag(sup_handle, FWNODE_FLAG_VISITED);
2078: put_device(sup_dev);
2079: put_device(con_dev);
2080: put_device(par_dev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__fw_devlink_relax_cycles SourceFile:drivers/base/core.c SourceLine:2079 SourceSnippet:2077: fwnode_clear_flag(sup_handle, FWNODE_FLAG_VISITED);
2078: put_device(sup_dev);
2079: put_device(con_dev);
2080: put_device(par_dev);
2081: return ret;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__fw_devlink_relax_cycles SourceFile:drivers/base/core.c SourceLine:2080 SourceSnippet:2078: put_device(sup_dev);
2079: put_device(con_dev);
2080: put_device(par_dev);
2081: return ret;
2082: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__hwmon_device_register SourceFile:drivers/hwmon/hwmon.c SourceLine:987 SourceSnippet: 985: err = device_register(hdev);
986: if (err) {
987: put_device(hdev);
988: goto ida_remove;
989: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__intel_scu_ipc_register SourceFile:drivers/platform/x86/intel_scu_ipc.c SourceLine:615 SourceSnippet: 613: err = device_register(&scu->dev);
614: if (err) {
615: put_device(&scu->dev);
616: return ERR_PTR(err);
617: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__nd_btt_create SourceFile:drivers/nvdimm/btt_devs.c SourceLine:207 SourceSnippet: 205: dev_dbg(&ndns->dev, "failed, already claimed by %s\n",
206: dev_name(ndns->claim));
207: put_device(dev);
208: return NULL;
209: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__nd_detach_ndns SourceFile:drivers/nvdimm/claim.c SourceLine:27 SourceSnippet: 25: ndns->claim = NULL;
26: *_ndns = NULL;
27: put_device(&ndns->dev);
28: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__nvdimm_security_overwrite_query SourceFile:drivers/nvdimm/security.c SourceLine:482 SourceSnippet: 480: if (nvdimm->sec.overwrite_state)
481: sysfs_notify_dirent(nvdimm->sec.overwrite_state);
482: put_device(&nvdimm->dev);
483: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__nvmem_device_get SourceFile:drivers/nvmem/core.c SourceLine:1130 SourceSnippet:1128: nvmem_dev_name(nvmem));
1129:
1130: put_device(&nvmem->dev);
1131: return ERR_PTR(-EINVAL);
1132: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__nvmem_device_put SourceFile:drivers/nvmem/core.c SourceLine:1141 SourceSnippet:1139: static void __nvmem_device_put(struct nvmem_device *nvmem)
1140: {
1141: put_device(&nvmem->dev);
1142: module_put(nvmem->owner);
1143: kref_put(&nvmem->refcnt, nvmem_device_release);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__pci_epc_create SourceFile:drivers/pci/endpoint/pci-epc-core.c SourceLine:1025 SourceSnippet:1023:
1024: put_dev:
1025: put_device(&epc->dev);
1026:
1027: err_ret:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__power_supply_register SourceFile:drivers/power/supply/power_supply_core.c SourceLine:1682 SourceSnippet:1680: check_supplies_failed:
1681: dev_set_name_failed:
1682: put_device(dev);
1683: return ERR_PTR(rc);
1684: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__root_device_register SourceFile:drivers/base/core.c SourceLine:4312 SourceSnippet:4310: err = device_register(&root->dev);
4311: if (err) {
4312: put_device(&root->dev);
4313: return ERR_PTR(err);
4314: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__scsi_add_device SourceFile:drivers/scsi/scsi_scan.c SourceLine:1644 SourceSnippet:1642: */
1643: scsi_target_reap(starget);
1644: put_device(&starget->dev);
1645:
1646: return sdev;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__scsi_remove_device SourceFile:drivers/scsi/scsi_sysfs.c SourceLine:1500 SourceSnippet:1498: device_del(dev);
1499: } else
1500: put_device(&sdev->sdev_dev);
1501:
1502: /*
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__scsi_remove_device SourceFile:drivers/scsi/scsi_sysfs.c SourceLine:1526 SourceSnippet:1524: scsi_target_reap(scsi_target(sdev));
1525:
1526: put_device(dev);
1527: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__scsi_remove_target SourceFile:drivers/scsi/scsi_sysfs.c SourceLine:1567 SourceSnippet:1565: spin_unlock_irqrestore(shost->host_lock, flags);
1566: scsi_remove_device(sdev);
1567: put_device(&sdev->sdev_gendev);
1568: spin_lock_irqsave(shost->host_lock, flags);
1569: goto restart;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__scsi_scan_target SourceFile:drivers/scsi/scsi_scan.c SourceLine:1805 SourceSnippet:1803: scsi_target_reap(starget);
1804:
1805: put_device(&starget->dev);
1806: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__smcr_link_clear SourceFile:net/smc/smc_core.c SourceLine:1356 SourceSnippet:1354: smc_wr_free_link_mem(lnk);
1355: smc_ibdev_cnt_dec(lnk);
1356: put_device(&lnk->smcibdev->ibdev->dev);
1357: smcibdev = lnk->smcibdev;
1358: memset(lnk, 0, sizeof(struct smc_link));
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__thermal_cooling_device_register SourceFile:drivers/thermal/thermal_core.c SourceLine:1123 SourceSnippet:1121: if (ret) {
1122: /* thermal_release() handles rest of the cleanup */
1123: put_device(&cdev->device);
1124: return ERR_PTR(ret);
1125: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__vdpa_register_device SourceFile:drivers/vdpa/vdpa.c SourceLine:197 SourceSnippet: 195: dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match);
196: if (dev) {
197: put_device(dev);
198: return -EEXIST;
199: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__video_register_device SourceFile:drivers/media/v4l2-core/v4l2-dev.c SourceLine:1080 SourceSnippet:1078: mutex_unlock(&videodev_lock);
1079: pr_err("%s: device_register failed\n", __func__);
1080: put_device(&vdev->dev);
1081: return ret;
1082: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__wwan_port_dev_assign_name SourceFile:drivers/net/wwan/wwan_core.c SourceLine:447 SourceSnippet: 445: dev = device_find_child_by_name(&wwandev->dev, buf);
446: if (dev) {
447: put_device(dev);
448: return -ENFILE;
449: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_edac_mc_free SourceFile:drivers/edac/edac_mc.c SourceLine:175 SourceSnippet: 173: static void _edac_mc_free(struct mem_ctl_info *mci)
174: {
175: put_device(&mci->dev);
176: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2456 SourceSnippet:2454: if (rdev->exclusive) {
2455: regulator = ERR_PTR(-EPERM);
2456: put_device(&rdev->dev);
2457: return regulator;
2458: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2462 SourceSnippet:2460: if (get_type == EXCLUSIVE_GET && rdev->open_count) {
2461: regulator = ERR_PTR(-EBUSY);
2462: put_device(&rdev->dev);
2463: return regulator;
2464: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2472 SourceSnippet:2470: if (ret != 0) {
2471: regulator = ERR_PTR(-EPROBE_DEFER);
2472: put_device(&rdev->dev);
2473: return regulator;
2474: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2479 SourceSnippet:2477: if (ret < 0) {
2478: regulator = ERR_PTR(ret);
2479: put_device(&rdev->dev);
2480: return regulator;
2481: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2485 SourceSnippet:2483: if (!try_module_get(rdev->owner)) {
2484: regulator = ERR_PTR(-EPROBE_DEFER);
2485: put_device(&rdev->dev);
2486: return regulator;
2487: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2495 SourceSnippet:2493: regulator = ERR_PTR(-ENOMEM);
2494: module_put(rdev->owner);
2495: put_device(&rdev->dev);
2496: return regulator;
2497: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_get_common SourceFile:drivers/regulator/core.c SourceLine:2516 SourceSnippet:2514: destroy_regulator(regulator);
2515: module_put(rdev->owner);
2516: put_device(&rdev->dev);
2517: return ERR_PTR(ret);
2518: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_regulator_put SourceFile:drivers/regulator/core.c SourceLine:2664 SourceSnippet:2662:
2663: module_put(rdev->owner);
2664: put_device(&rdev->dev);
2665: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_bind_one SourceFile:drivers/acpi/glue.c SourceLine:273 SourceSnippet: 271: goto err;
272:
273: put_device(dev);
274: acpi_dev_put(acpi_dev);
275: return 0;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_bind_one SourceFile:drivers/acpi/glue.c SourceLine:313 SourceSnippet: 311: err:
312: ACPI_COMPANION_SET(dev, NULL);
313: put_device(dev);
314: acpi_dev_put(acpi_dev);
315: return retval;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_dev_present SourceFile:drivers/acpi/utils.c SourceLine:966 SourceSnippet: 964:
965: dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
966: put_device(dev);
967: return !!dev;
968: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_dev_put SourceFile:include/acpi/acpi_bus.h SourceLine:980 SourceSnippet: 978: {
979: if (adev)
980: put_device(&adev->dev);
981: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_device_remove SourceFile:drivers/acpi/bus.c SourceLine:1167 SourceSnippet:1165: acpi_dev->driver_data = NULL;
1166:
1167: put_device(dev);
1168: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_platform_device_remove_notify SourceFile:drivers/acpi/acpi_platform.c SourceLine:63 SourceSnippet: 61:
62: platform_device_unregister(pdev);
63: put_device(&pdev->dev);
64: break;
65: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_spi_notify SourceFile:drivers/spi/spi.c SourceLine:5094 SourceSnippet:5092:
5093: acpi_register_spi_device(ctlr, adev);
5094: put_device(&ctlr->dev);
5095: break;
5096: case ACPI_RECONFIG_DEVICE_REMOVE:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_spi_notify SourceFile:drivers/spi/spi.c SourceLine:5105 SourceSnippet:5103:
5104: spi_unregister_device(spi);
5105: put_device(&spi->dev);
5106: break;
5107: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_system_wakeup_device_seq_show SourceFile:drivers/acpi/proc.c SourceLine:61 SourceSnippet: 59: ldev->bus ? ldev->bus->name :
60: "no-bus", dev_name(ldev));
61: put_device(ldev);
62: }
63: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acpi_unbind_one SourceFile:drivers/acpi/glue.c SourceLine:341 SourceSnippet: 339: ACPI_COMPANION_SET(dev, NULL);
340: /* Drop references taken by acpi_bind_one(). */
341: put_device(dev);
342: acpi_dev_put(acpi_dev);
343: kfree(entry);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:add_memory_block SourceFile:drivers/base/memory.c SourceLine:800 SourceSnippet: 798: mem = find_memory_block_by_id(block_id);
799: if (mem) {
800: put_device(&mem->dev);
801: return -EEXIST;
802: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:add_mtd_device SourceFile:drivers/mtd/mtdcore.c SourceLine:803 SourceSnippet: 801: error = device_register(&mtd->dev);
802: if (error) {
803: put_device(&mtd->dev);
804: goto fail_added;
805: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:add_one_compat_dev SourceFile:drivers/infiniband/core/device.c SourceLine:995 SourceSnippet: 993: device_del(&cdev->dev);
994: add_err:
995: put_device(&cdev->dev);
996: cdev_err:
997: xa_release(&device->compat_devs, rnet->id);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:add_partition SourceFile:block/partitions/core.c SourceLine:401 SourceSnippet: 399: device_del(pdev);
400: out_put:
401: put_device(pdev);
402: return ERR_PTR(err);
403: out_put_disk:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:add_pdo SourceFile:drivers/usb/typec/pd.c SourceLine:523 SourceSnippet: 521: ret = device_register(&p->dev);
522: if (ret) {
523: put_device(&p->dev);
524: return ret;
525: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:alloc_clt SourceFile:drivers/infiniband/ulp/rtrs/rtrs-clt.c SourceLine:2791 SourceSnippet:2789: err_put:
2790: free_percpu(clt->pcpu_path);
2791: put_device(&clt->dev);
2792: return ERR_PTR(err);
2793: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:alsa_timer_exit SourceFile:sound/core/timer.c SourceLine:2518 SourceSnippet:2516: snd_unregister_device(timer_dev);
2517: snd_timer_free_all();
2518: put_device(timer_dev);
2519: snd_timer_proc_done();
2520: #ifdef SNDRV_OSS_INFO_DEV_TIMERS
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:alsa_timer_init SourceFile:sound/core/timer.c SourceLine:2510 SourceSnippet:2508:
2509: put_timer:
2510: put_device(timer_dev);
2511: return err;
2512: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:amd_pstate_init SourceFile:drivers/cpufreq/amd-pstate.c SourceLine:2300 SourceSnippet:2298: if (dev_root) {
2299: ret = sysfs_create_group(&dev_root->kobj, &amd_pstate_global_attr_group);
2300: put_device(dev_root);
2301: if (ret) {
2302: pr_err("sysfs attribute export failed with error %d.\n", ret);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_resume SourceFile:drivers/base/power/main.c SourceLine:1145 SourceSnippet:1143:
1144: device_resume(dev, pm_transition, true);
1145: put_device(dev);
1146: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_resume_early SourceFile:drivers/base/power/main.c SourceLine:969 SourceSnippet: 967:
968: device_resume_early(dev, pm_transition, true);
969: put_device(dev);
970: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_resume_noirq SourceFile:drivers/base/power/main.c SourceLine:823 SourceSnippet: 821:
822: device_resume_noirq(dev, pm_transition, true);
823: put_device(dev);
824: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_suspend SourceFile:drivers/base/power/main.c SourceLine:2004 SourceSnippet:2002:
2003: device_suspend(dev, pm_transition, true);
2004: put_device(dev);
2005: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_suspend_late SourceFile:drivers/base/power/main.c SourceLine:1715 SourceSnippet:1713:
1714: device_suspend_late(dev, pm_transition, true);
1715: put_device(dev);
1716: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:async_suspend_noirq SourceFile:drivers/base/power/main.c SourceLine:1516 SourceSnippet:1514:
1515: device_suspend_noirq(dev, pm_transition, true);
1516: put_device(dev);
1517: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_tdev_free SourceFile:drivers/ata/libata-transport.c SourceLine:529 SourceSnippet: 527: {
528: transport_destroy_device(&dev->tdev);
529: put_device(&dev->tdev);
530: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_tlink_add SourceFile:drivers/ata/libata-transport.c SourceLine:718 SourceSnippet: 716: tlink_err:
717: transport_destroy_device(dev);
718: put_device(dev);
719: return error;
720: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_tlink_delete SourceFile:drivers/ata/libata-transport.c SourceLine:666 SourceSnippet: 664: device_del(dev);
665: transport_destroy_device(dev);
666: put_device(dev);
667: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_tport_add SourceFile:drivers/ata/libata-transport.c SourceLine:306 SourceSnippet: 304: tport_err:
305: transport_destroy_device(dev);
306: put_device(dev);
307: return error;
308: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ata_tport_delete SourceFile:drivers/ata/libata-transport.c SourceLine:242 SourceSnippet: 240: device_del(dev);
241: transport_destroy_device(dev);
242: put_device(dev);
243: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atm_dev_put SourceFile:include/linux/atmdev.h SourceLine:290 SourceSnippet: 288: if (dev->ops->dev_close)
289: dev->ops->dev_close(dev);
290: put_device(&dev->class_dev);
291: }
292: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atm_dev_register SourceFile:net/atm/resources.c SourceLine:133 SourceSnippet: 131:
132: out_fail:
133: put_device(&dev->class_dev);
134: dev = NULL;
135: goto out;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:attribute_container_release SourceFile:drivers/base/attribute_container.c SourceLine:117 SourceSnippet: 115:
116: kfree(ic);
117: put_device(dev);
118: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:auxiliary_device_uninit SourceFile:include/linux/auxiliary_bus.h SourceLine:242 SourceSnippet: 240: {
241: mutex_destroy(&auxdev->sysfs.lock);
242: put_device(&auxdev->dev);
243: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:backlight_device_register SourceFile:drivers/video/backlight/backlight.c SourceLine:401 SourceSnippet: 399: rc = device_register(&new_bd->dev);
400: if (rc) {
401: put_device(&new_bd->dev);
402: return ERR_PTR(rc);
403: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcma_device_probe SourceFile:drivers/bcma/main.c SourceLine:621 SourceSnippet: 619: err = adrv->probe(core);
620: if (err)
621: put_device(dev);
622:
623: return err;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcma_device_remove SourceFile:drivers/bcma/main.c SourceLine:634 SourceSnippet: 632: if (adrv->remove)
633: adrv->remove(core);
634: put_device(dev);
635: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcma_unregister_cores SourceFile:drivers/bcma/main.c SourceLine:384 SourceSnippet: 382: list_for_each_entry_safe(core, tmp, &bus->cores, list) {
383: list_del(&core->list);
384: put_device(&core->dev);
385: }
386: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bdi_unregister SourceFile:mm/backing-dev.c SourceLine:1178 SourceSnippet:1176:
1177: if (bdi->owner) {
1178: put_device(bdi->owner);
1179: bdi->owner = NULL;
1180: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bind_store SourceFile:drivers/base/bus.c SourceLine:273 SourceSnippet: 271: }
272: }
273: put_device(dev);
274: bus_put(bus);
275: return err;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:blk_report_disk_dead SourceFile:block/genhd.c SourceLine:651 SourceSnippet: 649: bdev_mark_dead(bdev, surprise);
650:
651: put_device(&bdev->bd_device);
652: rcu_read_lock();
653: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:blkdev_put_no_open SourceFile:block/bdev.c SourceLine:852 SourceSnippet: 850: void blkdev_put_no_open(struct block_device *bdev)
851: {
852: put_device(&bdev->bd_device);
853: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bsg_queue_rq SourceFile:block/bsg-lib.c SourceLine:296 SourceSnippet: 294:
295: out:
296: put_device(dev);
297: return sts;
298: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bsg_register_queue SourceFile:block/bsg.c SourceLine:266 SourceSnippet: 264: cdev_device_del(&bd->cdev, &bd->device);
265: out_put_device:
266: put_device(&bd->device);
267: return ERR_PTR(ret);
268: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bsg_teardown_job SourceFile:block/bsg-lib.c SourceLine:161 SourceSnippet: 159: struct request *rq = blk_mq_rq_from_pdu(job);
160:
161: put_device(job->dev); /* release reference for the request */
162:
163: kfree(job->request_payload.sg_list);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bsg_unregister_queue SourceFile:block/bsg.c SourceLine:214 SourceSnippet: 212: sysfs_remove_link(&disk->queue_kobj, "bsg");
213: cdev_device_del(&bd->cdev, &bd->device);
214: put_device(&bd->device);
215: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:byt_serial_exit SourceFile:drivers/tty/serial/8250/8250_lpss.c SourceLine:161 SourceSnippet: 159:
160: /* Paired with pci_get_slot() in the byt_serial_setup() above */
161: put_device(param->dma_dev);
162: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cec_devnode_unregister SourceFile:drivers/media/cec/core/cec-core.c SourceLine:171 SourceSnippet: 169:
170: cdev_device_del(&devnode->cdev, &devnode->dev);
171: put_device(&devnode->dev);
172: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cec_put_device SourceFile:include/media/cec.h SourceLine:328 SourceSnippet: 326: static inline void cec_put_device(struct cec_adapter *adap)
327: {
328: put_device(&adap->devnode.dev);
329: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:class_dev_create SourceFile:drivers/media/usb/pvrusb2/pvrusb2-sysfs.c SourceLine:636 SourceSnippet: 634: pvr2_trace(PVR2_TRACE_ERROR_LEGS,
635: "device_register failed");
636: put_device(class_dev);
637: return;
638: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:class_dev_destroy SourceFile:drivers/media/usb/pvrusb2/pvrusb2-sysfs.c SourceLine:535 SourceSnippet: 533: dev = sfp->class_dev->parent;
534: sfp->class_dev->parent = NULL;
535: put_device(dev);
536: device_unregister(sfp->class_dev);
537: sfp->class_dev = NULL;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:class_thermal_zone_get_by_id_destructor SourceFile:drivers/thermal/thermal_core.h SourceLine:216 SourceSnippet: 216: DEFINE_CLASS(thermal_zone_get_by_id, struct thermal_zone_device *,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:comedi_buf_map_kref_release SourceFile:drivers/comedi/comedi_buf.c SourceLine:46 SourceSnippet: 44: }
45: if (bm->dma_dir != DMA_NONE)
46: put_device(bm->dma_hw_dev);
47: kfree(bm);
48: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:comedi_clear_hw_dev SourceFile:drivers/comedi/drivers.c SourceLine:62 SourceSnippet: 60: static void comedi_clear_hw_dev(struct comedi_device *dev)
61: {
62: put_device(dev->hw_dev);
63: dev->hw_dev = NULL;
64: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:comedi_dev_kref_release SourceFile:drivers/comedi/comedi_fops.c SourceLine:120 SourceSnippet: 118:
119: mutex_destroy(&dev->mutex);
120: put_device(dev->class_dev);
121: kfree(dev);
122: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:container_device_attach SourceFile:drivers/acpi/container.c SourceLine:67 SourceSnippet: 65: ret = device_register(dev);
66: if (ret) {
67: put_device(dev);
68: return ret;
69: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:coreboot_table_populate SourceFile:drivers/firmware/google/coreboot_table.c SourceLine:153 SourceSnippet: 151: ret = device_register(&device->dev);
152: if (ret) {
153: put_device(&device->dev);
154: return ret;
155: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:counter_chrdev_release SourceFile:drivers/counter/counter-chrdev.c SourceLine:450 SourceSnippet: 448: mutex_unlock(&counter->ops_exist_lock);
449:
450: put_device(&counter->dev);
451:
452: return ret;
]] TruncatedOutput:true]
|
| 88/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 76457
output: 41
thoughts: 100
Thoughts: **Delving into Bluetooth Connection Management**
Okay, so I'm tracing through this Bluetooth connection handling, and I see `hci_conn_put`. My initial thought is to figure out where that function resides. Ah, it's right there in `include/net/bluetooth/hci_core.h`. Let's take a look:
```c
static inline void hci_conn_put(struct hci_conn *conn)
{
put_device(&conn->dev);
}
```
Makes sense. It looks like it's a simple inline function. It just calls `put_device` on `conn->dev`. Now, I'm thinking about the implications. `put_device`, like any reference counting mechanism, is going to decrement the reference count of the device represented by `conn->dev`. And the important part, of course, is what happens when that reference count hits zero. That's when `hci_conn_dev_release` gets called, and that's exactly what I need to understand next. Let's dig into that `hci_conn_dev_release` function and see what it does.
|
| 89/2 |
2026/06/01 04:36 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/bluetooth/hci_sysfs.c Name:hci_conn_dev_release]
Results: map[SourceCode: SourceFile:]
|
| 90/2 |
2026/06/01 04:36 |
llm |
expert |
0m
Model:
Tokens:
input: 76863
output: 31
thoughts: 21
Thoughts: Okay, here's my summary, taking the role of the thinker:
**Diving into the Bluetooth HCI Sysfs Implementation**
Alright, time to get to work. I need to find something in the Bluetooth stack, specifically related to how it handles releases. Given my understanding of the system, the most logical place to start is `net/bluetooth/hci_sysfs.c`. I know that file handles a lot of the interface between the Bluetooth stack and the userspace through the sysfs. Iβm hoping to find the code that manages resource release or freeing resources. Let's see what we can dig up there.
|
| 91/2 |
2026/06/01 04:37 |
tool |
grepper |
0m
Args: map[Expression:release PathPrefix:net/bluetooth/hci_sysfs.c]
Results: map[Output:net/bluetooth/hci_sysfs.c=9=static const struct class bt_class = {
--
net/bluetooth/hci_sysfs.c-12-
net/bluetooth/hci_sysfs.c:13:static void bt_link_release(struct device *dev)
net/bluetooth/hci_sysfs.c-14-{
--
net/bluetooth/hci_sysfs.c=19=static const struct device_type bt_link = {
net/bluetooth/hci_sysfs.c-20- .name = "link",
net/bluetooth/hci_sysfs.c:21: .release = bt_link_release,
net/bluetooth/hci_sysfs.c-22-};
--
net/bluetooth/hci_sysfs.c=52=void hci_conn_del_sysfs(struct hci_conn *conn)
--
net/bluetooth/hci_sysfs.c-81-
net/bluetooth/hci_sysfs.c:82:static void bt_host_release(struct device *dev)
net/bluetooth/hci_sysfs.c-83-{
--
net/bluetooth/hci_sysfs.c-86- if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
net/bluetooth/hci_sysfs.c:87: hci_release_dev(hdev);
net/bluetooth/hci_sysfs.c-88- else
--
net/bluetooth/hci_sysfs.c=111=static const struct device_type bt_host = {
net/bluetooth/hci_sysfs.c-112- .name = "host",
net/bluetooth/hci_sysfs.c:113: .release = bt_host_release,
net/bluetooth/hci_sysfs.c-114- .groups = bt_host_groups,
]
|
| 92/2 |
2026/06/01 04:37 |
llm |
expert |
1m
Model:
Tokens:
input: 77510
output: 39
thoughts: 31
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Working Through a Bluetooth Resource Management Task**
Right, so I'm looking at `bt_link_release`. My immediate thought is, "Ah, that's the cleanup function specifically for the `hci_conn` structure." Knowing this is half the battle, the other half is understanding the implementation of that function, so I'll need to dive into it for more information. Let's dig into `bt_link_release` and see what's happening under the hood. Hopefully, this routine is handling proper resource deallocation and signaling the right events, but I'll need to confirm.
|
| 93/2 |
2026/06/01 04:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sysfs.c Name:bt_link_release]
Results: map[SourceCode: 13: static void bt_link_release(struct device *dev)
14: {
15: struct hci_conn *conn = to_hci_conn(dev);
16: kfree(conn);
17: }
SourceFile:net/bluetooth/hci_sysfs.c]
|
| 94/2 |
2026/06/01 04:38 |
llm |
expert |
0m
Model:
Tokens:
input: 77831
output: 44
thoughts: 838
Thoughts: **Analyzing a Potential Race Condition in Bluetooth Connection Teardown**
Okay, so I'm looking at this Bluetooth connection teardown sequence and something's bugging me. Specifically, I'm trying to wrap my head around the timing between releasing resources and potential issues with timers.
First off, `bt_link_release` just frees the `conn` structure, right? Fine. But where is the reference count on the `hdev` (the HCI device) actually *dropped* for this connection? Ah, it's in `hci_conn_cleanup`:
```c
hci_dev_put(hdev);
```
Okay, so `hci_conn_cleanup` decrements the `hdev` refcount. And that function is called from `hci_conn_del`. So the reference to `hdev` is dropped in `hci_conn_del`, NOT when the connection memory is freed. This is important. It means `hci_release_dev` could potentially be called immediately after `hci_conn_del`, assuming the refcount hits zero.
But wait, `l2cap_conn_del` is called *before* `hci_conn_del` in `hci_conn_hash_flush`. So, `l2cap_conn_del` completes, then `hci_conn_del` gets called, which drops the `hdev` reference. If the refcount goes to zero, we'll hit `hci_release_dev`, which tears down the `hdev->workqueue`. Okay.
The crucial point is that `l2cap_conn_del` calls `disable_delayed_work_sync(&conn->info_timer)`. This means it *should* wait for `info_timer` to complete before proceeding. So, the `info_timer` should be done before the `hci_conn_del`. Then, if the `info_timer` is finished, how could `l2cap_info_timeout` still be running?
Wait... What if the timer is *re-scheduled* after `l2cap_conn_del` somehow? Is there any path where that could happen without checking if the connection is already deleted?
Okay, let's think about this. In `l2cap_connect_cfm`, it calls `l2cap_conn_ready(conn)`. `l2cap_conn_ready` then calls `l2cap_request_info(conn)`, which then *schedules* `info_timer`. Can `l2cap_connect_cfm` be called *after* `hci_conn_del`? Nope. `hci_conn_del` removes the connection from `hdev->conn_hash`. So, any incoming events will cause `hci_conn_hash_lookup_handle` to return NULL, and it won't find the connection. So, no new connections could be created for the same handle that was previously deleted.
What about `l2cap_connect_cfm` being called from `hci_connect_cfm`? `hci_connect_cfm` is called from `hci_cs_create_conn`, `hci_le_create_conn_sync`, etc. So if `hci_connect_cfm` is called, it calls `l2cap_connect_cfm`.
What if `l2cap_info_timeout` is running, then calls `l2cap_conn_start`, which in turn queues `tx_work`? If `l2cap_info_timeout` is running, the `info_timer` *must* have been scheduled. But could the timer be scheduled on a different workqueue?
Let's see... `schedule_delayed_work` queues it on `system_wq`. So `l2cap_info_timeout` runs on `system_wq`. If `l2cap_conn_del` calls `disable_delayed_work_sync(&conn->info_timer)`, it should wait for that work to finish. But, just to be absolutely sure, does it actually *wait* for the timer to finish? Yes, `disable_delayed_work_sync` does indeed wait.
Wait, the whole thing hinges on whether `l2cap_conn_del` is *always* called. Is there a path where `hci_conn_del` is called, but `l2cap_conn_del` is *skipped*? I need to go back and check `hci_conn_del` again carefully... This is the critical point.
|
| 95/2 |
2026/06/01 04:38 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeSnippetLines:2 Name:hci_conn_del]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_big_complete SourceFile:net/bluetooth/hci_conn.c SourceLine:2288 SourceSnippet:2286: bt_dev_err(hdev, "Unable to create BIG: %d", err);
2287: hci_connect_cfm(conn, err);
2288: hci_conn_del(conn);
2289: }
2290:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_abort_conn_sync SourceFile:net/bluetooth/hci_sync.c SourceLine:5761 SourceSnippet:5759: conn->state = BT_CLOSED;
5760: hci_disconn_cfm(conn, reason);
5761: hci_conn_del(conn);
5762: } else {
5763: hci_conn_failed(conn, reason);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cc_le_setup_iso_path SourceFile:net/bluetooth/hci_event.c SourceLine:3915 SourceSnippet:3913: if (rp->status) {
3914: hci_connect_cfm(conn, rp->status);
3915: hci_conn_del(conn);
3916: goto unlock;
3917: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_failed SourceFile:net/bluetooth/hci_conn.c SourceLine:1410 SourceSnippet:1408: conn->state = BT_CLOSED;
1409: hci_connect_cfm(conn, status);
1410: hci_conn_del(conn);
1411: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_hash_flush SourceFile:net/bluetooth/hci_conn.c SourceLine:2735 SourceSnippet:2733: conn->state = BT_CLOSED;
2734: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
2735: hci_conn_del(conn);
2736: }
2737: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_acl SourceFile:net/bluetooth/hci_conn.c SourceLine:1787 SourceSnippet:1785: err = hci_connect_acl_sync(hdev, acl);
1786: if (err) {
1787: hci_conn_del(acl);
1788: return ERR_PTR(err);
1789: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_le SourceFile:net/bluetooth/hci_conn.c SourceLine:1512 SourceSnippet:1510: err = hci_connect_le_sync(hdev, conn);
1511: if (err) {
1512: hci_conn_del(conn);
1513: return ERR_PTR(err);
1514: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_connect_le_scan SourceFile:net/bluetooth/hci_conn.c SourceLine:1726 SourceSnippet:1724:
1725: if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0) {
1726: hci_conn_del(conn);
1727: return ERR_PTR(-EBUSY);
1728: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_add_sco SourceFile:net/bluetooth/hci_event.c SourceLine:2313 SourceSnippet:2311:
2312: hci_connect_cfm(link->conn, status);
2313: hci_conn_del(link->conn);
2314: }
2315: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_create_conn SourceFile:net/bluetooth/hci_event.c SourceLine:2269 SourceSnippet:2267: conn->state = BT_CLOSED;
2268: hci_connect_cfm(conn, status);
2269: hci_conn_del(conn);
2270: }
2271: } else {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_disconnect SourceFile:net/bluetooth/hci_event.c SourceLine:2772 SourceSnippet:2770: * advertising if necessary.
2771: */
2772: hci_conn_del(conn);
2773: unlock:
2774: hci_dev_unlock(hdev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_le_create_cis SourceFile:net/bluetooth/hci_event.c SourceLine:4353 SourceSnippet:4351: conn->state = BT_CLOSED;
4352: hci_connect_cfm(conn, status);
4353: hci_conn_del(conn);
4354: }
4355: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_disconn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3482 SourceSnippet:3480: }
3481:
3482: hci_conn_del(conn);
3483:
3484: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_big_sync_lost_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7278 SourceSnippet:7276: clear_bit(HCI_CONN_BIG_SYNC, &bis->flags);
7277: hci_disconn_cfm(bis, ev->reason);
7278: hci_conn_del(bis);
7279: }
7280:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_cis_established_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7009 SourceSnippet:7007: conn->state = BT_CLOSED;
7008: hci_connect_cfm(conn, ev->status);
7009: hci_conn_del(conn);
7010:
7011: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7117 SourceSnippet:7115: if (ev->status) {
7116: hci_connect_cfm(conn, ev->status);
7117: hci_conn_del(conn);
7118: continue;
7119: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7127 SourceSnippet:7125: ev->status = HCI_ERROR_UNSPECIFIED;
7126: hci_connect_cfm(conn, ev->status);
7127: hci_conn_del(conn);
7128: continue;
7129: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7141 SourceSnippet:7139: ev->status = HCI_ERROR_UNSPECIFIED;
7140: hci_connect_cfm(conn, ev->status);
7141: hci_conn_del(conn);
7142: continue;
7143: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_conn_sync SourceFile:net/bluetooth/hci_sync.c SourceLine:6605 SourceSnippet:6603: hdev->le_scan_type == LE_SCAN_ACTIVE &&
6604: !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
6605: hci_conn_del(conn);
6606: return -EBUSY;
6607: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_pa_sync_lost_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5929 SourceSnippet:5927: clear_bit(HCI_CONN_PA_SYNC, &conn->flags);
5928: hci_disconn_cfm(conn, HCI_ERROR_REMOTE_USER_TERM);
5929: hci_conn_del(conn);
5930: }
5931:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sco_setup SourceFile:net/bluetooth/hci_conn.c SourceLine:622 SourceSnippet: 620: } else {
621: hci_connect_cfm(link->conn, status);
622: hci_conn_del(link->conn);
623: }
624: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_setup_sync_conn_status SourceFile:net/bluetooth/hci_event.c SourceLine:2596 SourceSnippet:2594:
2595: hci_connect_cfm(link->conn, status);
2596: hci_conn_del(link->conn);
2597: }
2598: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sync_conn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5115 SourceSnippet:5113: hci_connect_cfm(conn, status);
5114: if (status)
5115: hci_conn_del(conn);
5116:
5117: unlock:
]] TruncatedOutput:false]
|
| 96/2 |
2026/06/01 04:38 |
llm |
expert |
0m
Model:
Tokens:
input: 82656
output: 38
thoughts: 1831
Thoughts: **Analyzing Bluetooth Connection Disconnection and Cleanup**
Okay, let's break this down. My initial concern was whether `hci_cs_disconnect` was correctly cleaning up, specifically if `hci_disconn_cfm` was called before `hci_conn_del`. I see that it *does* call `hci_disconn_cfm`, so that's good. The same pattern holds for `hci_disconn_complete_evt`.
Now, `hci_connect_le_scan` is a potential edge case. Here, `hci_conn_del` is called *before* `l2cap_conn_add`, so `conn->l2cap_data` is null. Makes sense β no L2CAP connection yet.
`hci_conn_failed` is another path. It calls `hci_connect_cfm`, which in turn calls `l2cap_connect_cfm`. A non-zero status in `l2cap_connect_cfm` leads to `l2cap_conn_del`, so that connection is properly cleaned.
My next concern stems from `l2cap_conn_del` and the timing of `disable_delayed_work_sync(&conn->info_timer)`. If `l2cap_info_timeout` is scheduled *after* `l2cap_conn_del` but before `hci_conn_del`, there could be a race condition. It looks like it *can't* happen. `l2cap_conn_del` sets `hcon->l2cap_data = NULL`, so the timer can't be re-scheduled. Moreover, `disable_delayed_work_sync` waits for completion before returning. And, the call to `disable_delayed_work_sync` happens *before* the lock is acquired, eliminating the possibility of a deadlock. Therefore, `l2cap_info_timeout` *must* finish before `hci_conn_del` is called.
Okay, let's think about `hdev->workqueue`. If `l2cap_info_timeout` calls `hci_send_acl` and queues work on `hdev->workqueue`, and if that's happening after we called `l2cap_conn_del`, we've got a problem. However, the logic looks sound; `disable_delayed_work_sync` ensures `l2cap_info_timeout` finishes before destruction of `hdev->workqueue`. It looks like that means the destruction is blocked until all outstanding work on the queue is finished.
The problem, though, is the warning about the workqueue being destroyed.
But here's a crucial point: `hci_release_dev` destroys `hdev->workqueue`, but that's only called when `hdev`'s reference count drops to zero. `hci_conn` *holds* a reference to `hdev` while it exists. And the code ensures that the reference is released during `hci_conn_cleanup` called from `hci_conn_del`. Therefore, the workqueue *cannot* be destroyed while an `hci_conn` exists. That's a fundamental invariant.
However, the workqueue is being destroyed, which indicates there's a problem with the reference counting. How can the workqueue be destroyed if `hci_conn` is still alive? I'm missing something here. Is it possible that `hci_conn_cleanup` is called *before* `l2cap_info_timeout` completes?
`hci_conn_cleanup` is called from `hci_conn_del`, and `hci_conn_del` is called from `hci_conn_hash_flush`. `hci_conn_hash_flush` calls `hci_disconn_cfm` BEFORE calling `hci_conn_del`. `hci_disconn_cfm` then calls `l2cap_disconn_cfm`, which calls `l2cap_conn_del`. `l2cap_conn_del` *also* calls `disable_delayed_work_sync(&conn->info_timer)`. So `info_timer` *must* have finished before `hci_conn_cleanup`!
There's something very strange going on here, let me go back to the top of the code to trace the call paths again.
Could there be a situation where `hci_conn_del` is called, but `hci_disconn_cfm` is NOT? Checking `hci_conn_timeout` is the next step.
|
| 97/2 |
2026/06/01 04:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c Name:hci_conn_timeout]
Results: map[SourceCode: 626: static void hci_conn_timeout(struct work_struct *work)
627: {
628: struct hci_conn *conn = container_of(work, struct hci_conn,
629: disc_work.work);
630: int refcnt = atomic_read(&conn->refcnt);
631:
632: BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
633:
634: WARN_ON(refcnt < 0);
635:
636: /* FIXME: It was observed that in pairing failed scenario, refcnt
637: * drops below 0. Probably this is because l2cap_conn_del calls
638: * l2cap_chan_del for each channel, and inside l2cap_chan_del conn is
639: * dropped. After that loop hci_chan_del is called which also drops
640: * conn. For now make sure that ACL is alive if refcnt is higher then 0,
641: * otherwise drop it.
642: */
643: if (refcnt > 0)
644: return;
645:
646: hci_abort_conn(conn, hci_proto_disconn_ind(conn));
647: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 98/2 |
2026/06/01 04:38 |
llm |
expert |
0m
Model:
Tokens:
input: 85887
output: 40
thoughts: 62
Thoughts: Here's my understanding of the connection timeout handling, from a Bluetooth HCI perspective:
**Connection Abort Sequence - Dissected**
Okay, so the `hci_conn_timeout` function triggers the connection abort process. Makes sense; that's the core of handling inactivity. I see that it then directly calls `hci_abort_conn`. This function is the actual workhorse for the abort request. Now, `hci_abort_conn` utilizes a synchronous command queuing mechanism, specifically calling `hci_cmd_sync_queue`. The parameters are crucial here. We're passing the HCI device handle (`hdev`), the specific abort connection synchronization function `abort_conn_sync`, and likely some context data. This tells the system to execute this abort command synchronously, ensuring a controlled flow. Then, the `abort_conn_sync` function is invoked. Finally, `hci_abort_conn_sync` is called which must do the heavy lifting. I need to look closer at what those functions are doing but I'm on the right track!
|
| 99/2 |
2026/06/01 04:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c Name:hci_abort_conn_sync]
Results: map[SourceCode:5718: int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
5719: {
5720: int err = 0;
5721: u16 handle = conn->handle;
5722: bool disconnect = false;
5723: struct hci_conn *c;
5724:
5725: switch (conn->state) {
5726: case BT_CONNECTED:
5727: case BT_CONFIG:
5728: err = hci_disconnect_sync(hdev, conn, reason);
5729: break;
5730: case BT_CONNECT:
5731: err = hci_connect_cancel_sync(hdev, conn, reason);
5732: break;
5733: case BT_CONNECT2:
5734: err = hci_reject_conn_sync(hdev, conn, reason);
5735: break;
5736: case BT_OPEN:
5737: case BT_BOUND:
5738: break;
5739: default:
5740: disconnect = true;
5741: break;
5742: }
5743:
5744: hci_dev_lock(hdev);
5745:
5746: /* Check if the connection has been cleaned up concurrently */
5747: c = hci_conn_hash_lookup_handle(hdev, handle);
5748: if (!c || c != conn) {
5749: err = 0;
5750: goto unlock;
5751: }
5752:
5753: /* Cleanup hci_conn object if it cannot be cancelled as it
5754: * likely means the controller and host stack are out of sync
5755: * or in case of LE it was still scanning so it can be cleanup
5756: * safely.
5757: */
5758: if (disconnect) {
5759: conn->state = BT_CLOSED;
5760: hci_disconn_cfm(conn, reason);
5761: hci_conn_del(conn);
5762: } else {
5763: hci_conn_failed(conn, reason);
5764: }
5765:
5766: unlock:
5767: hci_dev_unlock(hdev);
5768: return err;
5769: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 100/2 |
2026/06/01 04:38 |
llm |
expert |
0m
Model:
Tokens:
input: 86925
output: 38
thoughts: 2407
Thoughts: **Race Condition Analysis: The Case of the Leaked Timer**
Okay, so the initial flow seems straightforward: `hci_disconn_cfm` or `hci_conn_failed` triggers either `hci_connect_cfm` or `hci_disconn_cfm`, and they both lead to `l2cap_conn_del`. Simple. But, I started to dig deeper, and realized a potential issue if `l2cap_conn_del` gets skipped under specific circumstances.
If `l2cap_conn_del` gets called with a NULL `conn` (because it's not set up yet), it returns immediately. That's fine β it's a null check, after all. But then what about if it gets called twice? Seems harmless too. The first time will clean up, and second time it does nothing. Safety is built in here.
However, the question then becomes, what if `info_timer` is *scheduled* after `l2cap_conn_del` has already run? How could that even happen? `info_timer` is scheduled from `l2cap_conn_ready` which is only called from `l2cap_connect_cfm` and `l2cap_security_cfm`. If `l2cap_security_cfm` is called after the connection is deleted, the lookup should return NULL, and so that's safe.
The real problem lies in the window between `l2cap_conn_del` and `hci_conn_del` - when `hci_conn_del` is called *after* `l2cap_conn_del`, but *before* the HCI event processing is completed. Is there any window between these?
Turns out, yes. `hci_conn_hash_flush` could be called at the *same time* as `rx_work`. That's a problem. `hci_conn_hash_flush` flushes the connections when the device is closing. It calls `hci_disconn_cfm`, which leads to `l2cap_conn_del`. But, crucially, `hci_conn_hash_flush` *doesn't* hold the `hci_dev_lock(hdev)`! So it runs concurrently with `rx_work` which *does* hold that lock.
So the timeline is: `hci_dev_close_sync` kicks off and calls `hci_conn_hash_flush`, which then calls `hci_disconn_cfm`. This then calls `l2cap_conn_del`.
Meanwhile, `rx_work` can *also* be triggered (e.g. from an incoming event). And this could schedule the timer!
I looked at the code again, if `l2cap_security_cfm` is called, it checks for a NULL `conn`. But, if `HCI_EV_CONN_COMPLETE` is the incoming event and is processed by `rx_work`, it could call `l2cap_conn_add`, which allocates a *new* `l2cap_conn` and schedules `info_timer`.
So, we have: `hci_disconn_cfm` (called by `hci_conn_hash_flush`) clears `hcon->l2cap_data`. Then `hci_conn_del` deletes the connection from the hash.
If `HCI_EV_CONN_COMPLETE` comes in, it calls `hci_connect_cfm` then `l2cap_connect_cfm`, then `l2cap_conn_add` which creates a *new* `l2cap_conn` *and* schedules the timer! And then when `hci_conn_del` is called, it destroys the *original* connection. But crucially, `hci_conn_del` *doesn't* call `l2cap_conn_del`! So the newly created `l2cap_conn` is leaked, but its `info_timer` is still scheduled. Then when the timer fires, the program tries to use a destroyed workqueue and crashes.
So, let's break down the scenario:
- **Thread 1:** `hci_dev_close_sync`
- Calls `hci_conn_hash_flush`
- Finds an existing connection (`hcon`).
- Calls `hci_disconn_cfm`, which calls `l2cap_conn_del`, setting `hcon->l2cap_data = NULL`.
- **Thread 2:** `rx_work` (processing `HCI_EV_CONN_COMPLETE`)
- Finds `hcon` in the hash and updates the state.
- Calls `hci_connect_cfm`, which calls `l2cap_connect_cfm`, then `l2cap_conn_add` which
|
| 101/2 |
2026/06/01 04:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_unregister_dev]
Results: map[SourceCode:2690: /* Unregister HCI device */
2691: void hci_unregister_dev(struct hci_dev *hdev)
2692: {
2693: BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
2694:
2695: mutex_lock(&hdev->unregister_lock);
2696: hci_dev_set_flag(hdev, HCI_UNREGISTER);
2697: mutex_unlock(&hdev->unregister_lock);
2698:
2699: write_lock(&hci_dev_list_lock);
2700: list_del(&hdev->list);
2701: write_unlock(&hci_dev_list_lock);
2702:
2703: synchronize_srcu(&hdev->srcu);
2704: cleanup_srcu_struct(&hdev->srcu);
2705:
2706: disable_work_sync(&hdev->rx_work);
2707: disable_work_sync(&hdev->cmd_work);
2708: disable_work_sync(&hdev->tx_work);
2709: disable_work_sync(&hdev->power_on);
2710: disable_work_sync(&hdev->error_reset);
2711:
2712: hci_cmd_sync_clear(hdev);
2713:
2714: hci_unregister_suspend_notifier(hdev);
2715:
2716: hci_dev_do_close(hdev);
2717:
2718: if (!test_bit(HCI_INIT, &hdev->flags) &&
2719: !hci_dev_test_flag(hdev, HCI_SETUP) &&
2720: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
2721: hci_dev_lock(hdev);
2722: mgmt_index_removed(hdev);
2723: hci_dev_unlock(hdev);
2724: }
2725:
2726: /* mgmt_index_removed should take care of emptying the
2727: * pending list */
2728: BUG_ON(!list_empty(&hdev->mgmt_pending));
2729:
2730: hci_sock_dev_event(hdev, HCI_DEV_UNREG);
2731:
2732: if (hdev->rfkill) {
2733: rfkill_unregister(hdev->rfkill);
2734: rfkill_destroy(hdev->rfkill);
2735: }
2736:
2737: device_del(&hdev->dev);
2738: /* Actual cleanup is deferred until hci_release_dev(). */
2739: hci_dev_put(hdev);
2740: }
SourceFile:net/bluetooth/hci_core.c]
|
| 102/2 |
2026/06/01 04:39 |
llm |
expert |
0m
Model:
Tokens:
input: 91222
output: 48
thoughts: 497
Thoughts: **Analyzing Potential Race Conditions During Device Closure**
Okay, let's break this down. My initial concern was a potential race condition involving `l2cap_connect_cfm` during device closure. The key observation is that `disable_work_sync(&hdev->rx_work)` gets called *before* `hci_dev_do_close(hdev)`. That's important! This means the `rx_work` queue, which handles incoming events, *cannot* be running concurrently with the device closure process itself. Therefore, the "race" I was worried about with `rx_work` is impossible. The work will be disabled and drained before closure commences.
But, I can't be complacent. I need to make sure I haven't missed any other potential pathways that could call `l2cap_connect_cfm`. So, the next question is where else could `l2cap_connect_cfm` be called? Let's trace it through: `hci_le_create_conn_sync` seems like a possible entry point. It goes through `hci_connect_le_sync`, then `hci_connect_le`, and finally, `l2cap_connect`.
Now, where does `hci_le_create_conn_sync` run? Ah! It uses either `cmd_work`, `req_workqueue`, or `hdev->workqueue`. Okay, but wait, `hci_cmd_sync_queue` queues work on `hdev->cmd_work`. Crucially, `disable_work_sync(&hdev->cmd_work)` is *also* called before `hci_dev_do_close(hdev)`. Therefore, the `cmd_work` queue also cannot be running at the same time as `hci_dev_do_close(hdev)`. So, that potential path is also safe.
Alright, moving on. What about the connection timeout handler, `hci_conn_timeout`? It's a delayed work item on `conn->disc_work`. Does the device unregistration (part of closing) disable that? No, it's disabled during connection deletion in `hci_conn_del`. However, `hci_conn_timeout` *does* call `hci_abort_conn`, which queues work on `hdev->cmd_work`. But since `cmd_work` is disabled, `hci_cmd_sync_queue` will return an error, or the work will simply not execute. So, the connection timeout path is also blocked from calling `l2cap_connect_cfm`.
Hmm, this is good. The analysis is starting to look solid. But I need to be 100% sure. So, to be thorough, I have to go back and check every single caller of `l2cap_connect_cfm`. This will need a complete review to ensure I haven't missed a single path.
|
| 103/2 |
2026/06/01 04:39 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:l2cap_connect_cfm]
Results: map[References:<nil> TruncatedOutput:false]
|
| 104/2 |
2026/06/01 04:39 |
llm |
expert |
0m
Model:
Tokens:
input: 92401
output: 44
thoughts: 45
Thoughts: Here's my take on those thoughts:
**Analyzing the L2CAP Connection Confirmation**
Okay, so I'm looking at `l2cap_connect_cfm` and immediately it hits me that this is defined as a static function within `l2cap_core.c`. Nothing too surprising there. But wait, then I remember β or perhaps I should say, I *know* β that this specific function, `l2cap_connect_cfm`, gets assigned to `cb->connect_cfm` within the `l2cap_cb` structure, doesnβt it? That connection is key. It's how the core L2CAP functionality actually gets the confirmation callback. Knowing the callback assignment is going to be incredibly important in tracing how connection confirmations are handled within the L2CAP state machine. It is the core of how the underlying code knows what to do after the connection process. It's time to dig deeper into the logic connected to that assignment.
|
| 105/2 |
2026/06/01 04:39 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeSnippetLines:2 Name:connect_cfm]
Results: map[References:<nil> TruncatedOutput:false]
|
| 106/2 |
2026/06/01 04:39 |
llm |
expert |
0m
Model:
Tokens:
input: 92714
output: 34
thoughts: 24
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating `connect_cfm` in `net/bluetooth/l2cap_core.c`**
Alright, time to get to the bottom of this. My initial thought is to dive right into the code. Given the context, I'm specifically looking for the `connect_cfm` function. This suggests I'm trying to trace the confirmation path, likely within the Lower Layer Control Protocol (L2CAP) layer of the Bluetooth stack. The `net/bluetooth/l2cap_core.c` file is where I'd expect to find core L2CAP functionality, including handling connection confirmations, or `cfm` messages. I'm focusing on that specific filename and function because I want to understand *how* connection confirmations are handled within L2CAP. I'm expecting to find the implementation that either successfully acknowledges an incoming connection request or, conversely, handles the case where it's rejected. Knowing this function's location and functionality is critical for understanding the connection lifecycle at the L2CAP level and helps to quickly locate the root cause of any connection-related problems, especially when debugging. I know that function is essential for understanding flow control, retransmission logic, or even potential security vulnerabilities.
|
| 107/2 |
2026/06/01 04:39 |
tool |
grepper |
0m
Args: map[Expression:connect_cfm PathPrefix:net/bluetooth/l2cap_core.c]
Results: map[Output:net/bluetooth/l2cap_core.c=7329=static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
--
net/bluetooth/l2cap_core.c-7360-
net/bluetooth/l2cap_core.c:7361:static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
net/bluetooth/l2cap_core.c-7362-{
--
net/bluetooth/l2cap_core.c=7775=static struct hci_cb l2cap_cb = {
net/bluetooth/l2cap_core.c-7776- .name = "L2CAP",
net/bluetooth/l2cap_core.c:7777: .connect_cfm = l2cap_connect_cfm,
net/bluetooth/l2cap_core.c-7778- .disconn_cfm = l2cap_disconn_cfm,
]
|
| 108/2 |
2026/06/01 04:39 |
llm |
expert |
0m
Model:
Tokens:
input: 93317
output: 44
thoughts: 23
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating the `hci_connect_cfm` Call**
Alright, so the system is calling me from `hci_connect_cfm`. That's the entry point, the critical juncture I need to understand. My immediate question is: *where* is `hci_connect_cfm` itself being called from? I need to trace the call stack to figure out the initiating process and understand the context in which I'm being invoked. Knowing the caller is fundamental to comprehending the entire process flow and how my role fits into the bigger picture. This contextual understanding is the key to identifying potential issues or optimization opportunities. Time to start digging!
|
| 109/2 |
2026/06/01 04:39 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/bluetooth/hci_event.c IncludeSnippetLines:2 Name:hci_connect_cfm]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_big_complete SourceFile:net/bluetooth/hci_conn.c SourceLine:2287 SourceSnippet:2285: if (err) {
2286: bt_dev_err(hdev, "Unable to create BIG: %d", err);
2287: hci_connect_cfm(conn, err);
2288: hci_conn_del(conn);
2289: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_pa_complete SourceFile:net/bluetooth/hci_sync.c SourceLine:7082 SourceSnippet:7080:
7081: /* Notify iso layer */
7082: hci_connect_cfm(pa_sync, bt_status(err));
7083:
7084: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_auth_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3524 SourceSnippet:3522: } else {
3523: conn->state = BT_CONNECTED;
3524: hci_connect_cfm(conn, ev->status);
3525: hci_conn_drop(conn);
3526: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cc_le_setup_iso_path SourceFile:net/bluetooth/hci_event.c SourceLine:3914 SourceSnippet:3912:
3913: if (rp->status) {
3914: hci_connect_cfm(conn, rp->status);
3915: hci_conn_del(conn);
3916: goto unlock;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cc_le_setup_iso_path SourceFile:net/bluetooth/hci_event.c SourceLine:3924 SourceSnippet:3922: /* Only confirm connection if output only */
3923: if (conn->iso_qos.ucast.out.sdu && !conn->iso_qos.ucast.in.sdu)
3924: hci_connect_cfm(conn, rp->status);
3925: break;
3926: /* Output (Controller to Host) */
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cc_le_setup_iso_path SourceFile:net/bluetooth/hci_event.c SourceLine:3931 SourceSnippet:3929: * last.
3930: */
3931: hci_connect_cfm(conn, rp->status);
3932:
3933: /* Notify device connected in case it is a BIG Sync */
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3255 SourceSnippet:3253: }
3254:
3255: hci_connect_cfm(conn, status);
3256: }
3257:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_failed SourceFile:net/bluetooth/hci_conn.c SourceLine:1409 SourceSnippet:1407:
1408: conn->state = BT_CLOSED;
1409: hci_connect_cfm(conn, status);
1410: hci_conn_del(conn);
1411: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_conn_request_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3373 SourceSnippet:3371: } else {
3372: conn->state = BT_CONNECT2;
3373: hci_connect_cfm(conn, 0);
3374: }
3375:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_add_sco SourceFile:net/bluetooth/hci_event.c SourceLine:2312 SourceSnippet:2310: link->conn->state = BT_CLOSED;
2311:
2312: hci_connect_cfm(link->conn, status);
2313: hci_conn_del(link->conn);
2314: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_auth_requested SourceFile:net/bluetooth/hci_event.c SourceLine:2339 SourceSnippet:2337: if (conn) {
2338: if (conn->state == BT_CONFIG) {
2339: hci_connect_cfm(conn, status);
2340: hci_conn_drop(conn);
2341: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_create_conn SourceFile:net/bluetooth/hci_event.c SourceLine:2268 SourceSnippet:2266: if (conn && conn->state == BT_CONNECT) {
2267: conn->state = BT_CLOSED;
2268: hci_connect_cfm(conn, status);
2269: hci_conn_del(conn);
2270: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_le_create_cis SourceFile:net/bluetooth/hci_event.c SourceLine:4352 SourceSnippet:4350: pending = true;
4351: conn->state = BT_CLOSED;
4352: hci_connect_cfm(conn, status);
4353: hci_conn_del(conn);
4354: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_le_read_all_remote_features SourceFile:net/bluetooth/hci_event.c SourceLine:3983 SourceSnippet:3981: conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
3982: if (conn && conn->state == BT_CONFIG)
3983: hci_connect_cfm(conn, status);
3984:
3985: hci_dev_unlock(hdev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_le_read_remote_features SourceFile:net/bluetooth/hci_event.c SourceLine:2919 SourceSnippet:2917: conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
2918: if (conn && conn->state == BT_CONFIG)
2919: hci_connect_cfm(conn, status);
2920:
2921: hci_dev_unlock(hdev);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_read_remote_ext_features SourceFile:net/bluetooth/hci_event.c SourceLine:2570 SourceSnippet:2568: if (conn) {
2569: if (conn->state == BT_CONFIG) {
2570: hci_connect_cfm(conn, status);
2571: hci_conn_drop(conn);
2572: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_read_remote_features SourceFile:net/bluetooth/hci_event.c SourceLine:2543 SourceSnippet:2541: if (conn) {
2542: if (conn->state == BT_CONFIG) {
2543: hci_connect_cfm(conn, status);
2544: hci_conn_drop(conn);
2545: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_cs_set_conn_encrypt SourceFile:net/bluetooth/hci_event.c SourceLine:2366 SourceSnippet:2364: if (conn) {
2365: if (conn->state == BT_CONFIG) {
2366: hci_connect_cfm(conn, status);
2367: hci_conn_drop(conn);
2368: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_encrypt_cfm SourceFile:include/net/bluetooth/hci_core.h SourceLine:2192 SourceSnippet:2190: conn->state = BT_CONNECTED;
2191:
2192: hci_connect_cfm(conn, status);
2193: hci_conn_drop(conn);
2194: return;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_key_refresh_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5229 SourceSnippet:5227: conn->state = BT_CONNECTED;
5228:
5229: hci_connect_cfm(conn, ev->status);
5230: hci_conn_drop(conn);
5231: } else {
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_big_info_adv_report_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7313 SourceSnippet:7311:
7312: /* Notify iso layer */
7313: hci_connect_cfm(pa_sync, 0);
7314:
7315: unlock:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_big_sync_established_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7246 SourceSnippet:7244:
7245: set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
7246: hci_connect_cfm(bis, ev->status);
7247: }
7248:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_cis_established_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7008 SourceSnippet:7006:
7007: conn->state = BT_CLOSED;
7008: hci_connect_cfm(conn, ev->status);
7009: hci_conn_del(conn);
7010:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_cis_req_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7081 SourceSnippet:7079: } else {
7080: cis->state = BT_CONNECT2;
7081: hci_connect_cfm(cis, 0);
7082: }
7083:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7116 SourceSnippet:7114: HCI_ROLE_MASTER))) {
7115: if (ev->status) {
7116: hci_connect_cfm(conn, ev->status);
7117: hci_conn_del(conn);
7118: continue;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7126 SourceSnippet:7124: ev->handle);
7125: ev->status = HCI_ERROR_UNSPECIFIED;
7126: hci_connect_cfm(conn, ev->status);
7127: hci_conn_del(conn);
7128: continue;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_create_big_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7140 SourceSnippet:7138: */
7139: ev->status = HCI_ERROR_UNSPECIFIED;
7140: hci_connect_cfm(conn, ev->status);
7141: hci_conn_del(conn);
7142: continue;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_pa_sync_established_evt SourceFile:net/bluetooth/hci_event.c SourceLine:6602 SourceSnippet:6600:
6601: /* Notify iso layer */
6602: hci_connect_cfm(pa_sync, ev->status);
6603: }
6604:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_past_received_evt SourceFile:net/bluetooth/hci_event.c SourceLine:6064 SourceSnippet:6062:
6063: /* Notify iso layer */
6064: hci_connect_cfm(pa_sync, ev->status);
6065: }
6066:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_per_adv_report_evt SourceFile:net/bluetooth/hci_event.c SourceLine:6638 SourceSnippet:6636: !test_and_set_bit(HCI_CONN_PA_SYNC, &pa_sync->flags)) {
6637: /* Notify iso layer */
6638: hci_connect_cfm(pa_sync, 0);
6639:
6640: /* Notify MGMT layer */
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_read_all_remote_features_evt SourceFile:net/bluetooth/hci_event.c SourceLine:7368 SourceSnippet:7366:
7367: conn->state = BT_CONNECTED;
7368: hci_connect_cfm(conn, status);
7369: }
7370:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_le_remote_feat_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:6694 SourceSnippet:6692:
6693: conn->state = BT_CONNECTED;
6694: hci_connect_cfm(conn, status);
6695: }
6696: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_remote_ext_features_evt SourceFile:net/bluetooth/hci_event.c SourceLine:4999 SourceSnippet:4997: if (!hci_outgoing_auth_needed(hdev, conn)) {
4998: conn->state = BT_CONNECTED;
4999: hci_connect_cfm(conn, ev->status);
5000: hci_conn_drop(conn);
5001: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_remote_features_evt SourceFile:net/bluetooth/hci_event.c SourceLine:3760 SourceSnippet:3758: if (!hci_outgoing_auth_needed(hdev, conn)) {
3759: conn->state = BT_CONNECTED;
3760: hci_connect_cfm(conn, ev->status);
3761: hci_conn_drop(conn);
3762: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sco_setup SourceFile:net/bluetooth/hci_conn.c SourceLine:621 SourceSnippet: 619: hci_add_sco(link->conn, conn->handle);
620: } else {
621: hci_connect_cfm(link->conn, status);
622: hci_conn_del(link->conn);
623: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_setup_sync_conn_status SourceFile:net/bluetooth/hci_event.c SourceLine:2595 SourceSnippet:2593: link->conn->state = BT_CLOSED;
2594:
2595: hci_connect_cfm(link->conn, status);
2596: hci_conn_del(link->conn);
2597: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hci_sync_conn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5113 SourceSnippet:5111: }
5112:
5113: hci_connect_cfm(conn, status);
5114: if (status)
5115: hci_conn_del(conn);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:le_conn_complete_evt SourceFile:net/bluetooth/hci_event.c SourceLine:5865 SourceSnippet:5863: if (err) {
5864: conn->state = BT_CONNECTED;
5865: hci_connect_cfm(conn, status);
5866: }
5867:
]] TruncatedOutput:false]
|
| 110/2 |
2026/06/01 04:39 |
llm |
expert |
0m
Model:
Tokens:
input: 98504
output: 34
thoughts: 2647
Thoughts: **Race Condition Analysis in Bluetooth Device Shutdown**
Okay, here's the breakdown of my thinking. I'm seeing a potential race condition when a Bluetooth device is being closed down, specifically in the interplay between connection handling and device shutdown.
Initially, I spotted the potential that `hci_conn_complete_evt` might run concurrently with `hci_dev_close_sync`, leading to a situation where a connection is added *after* the connection hash is flushed but *before* its entry is removed. But a closer look at the code corrected this initial line of thinking: `hci_conn_hash_flush` calls `hci_conn_del`, so that potential timing wasn't a problem. Then I noted that `hci_conn_complete_evt` acquires the `hci_dev_lock(hdev)` which `hci_conn_hash_flush` does not. This is a potential issue.
I traced a scenario: Thread 1 runs `hci_conn_hash_flush`. Thread 2 runs `hci_conn_complete_evt` at an unfortunate time, so this became the focus. In Thread 1, `hci_disconn_cfm` is called. `l2cap_disconn_cfm` is called. It finds `hcon->l2cap_data` is NULL, so it returns. In Thread 2, with the lock taken, it finds the connection and proceeds to call `l2cap_connect_cfm`. This thread proceeds to allocate memory. Thread 1 continues and removes the `conn` from the hash. Importantly, `l2cap_conn_del` isn't called! That means `l2cap_conn` is leaked, and `info_timer` is scheduled with a timer ID that is no longer valid. This is the first concern I have.
My concern is that after the disconnect, the device eventually goes through `hci_dev_close_sync`, which calls `hci_unregister_dev`, and then `hci_release_dev`. It is at that time that `destroy_workqueue` gets called. The `info_timer` will then fire, with its work using a workqueue that is now invalid. Boom.
But, I then realize that `hci_conn_complete_evt` is actually called from `hci_rx_work`, which is a work that itself is scheduled. The `hci_dev_close_sync` routine calls `flush_work(&hdev->rx_work)` before `hci_conn_hash_flush`, which *should* prevent this race.
But, can `rx_work` be scheduled again after `flush_work` finishes? Yes! The line `disable_work_sync(&hdev->rx_work)` comes *after* `hci_dev_do_close`.
So it looks like I need to see what other ways could cause `hci_connect_cfm` to be called at the wrong time. This lead me to consider L2CAP connection attempts from user-space, initiated through `l2cap_sock_connect`. If a user-space process calls `connect()` on an L2CAP socket, that kicks off a sequence of calls that might overlap with shutdown. But, I see that these are subject to workqueue disabling, so it is unlikely.
But, what about `hci_conn_timeout`? No, this just queues something to `cmd_work`, which is also disabled. What about `l2cap_info_timeout`? No, `l2cap_conn_ready` is scheduled from a received packet from `rx_work`. This won't work either. Are there *any* workqueues that are not disabled?
I go through all the works, and I think I see the issue. I look at all the works. `conn->info_timer` and `conn->disc_work` are both using `system_wq`. The problem is that, it seems I did not consider the fact that there is a call to `drain_workqueue` which sets the bit `__WQ_DRAINING`, which causes the "cannot queue hci_tx_work on wq hci0" warning. This must be the ultimate problem.
So, `l2cap_info_timeout` could be running when `hci_dev_do_close` is happening. However, if that is the case, `disable_delayed_work_sync(&conn->info_timer)` would block. And so `hci_dev_do_close` would not return, but wait. This is safe. The same applies for all other workers. But I am still left with the warning about `__WQ_DRAINING` when a `tx_work` that is supposed to happen after `l2cap_info_timeout` is queued.
The warning is triggered by `wq->flags &
|
| 111/2 |
2026/06/01 04:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c Name:drain_workqueue]
Results: map[SourceCode:4209: /**
4210: * drain_workqueue - drain a workqueue
4211: * @wq: workqueue to drain
4212: *
4213: * Wait until the workqueue becomes empty. While draining is in progress,
4214: * only chain queueing is allowed. IOW, only currently pending or running
4215: * work items on @wq can queue further work items on it. @wq is flushed
4216: * repeatedly until it becomes empty. The number of flushing is determined
4217: * by the depth of chaining and should be relatively short. Whine if it
4218: * takes too long.
4219: */
4220: void drain_workqueue(struct workqueue_struct *wq)
4221: {
4222: unsigned int flush_cnt = 0;
4223: struct pool_workqueue *pwq;
4224:
4225: /*
4226: * __queue_work() needs to test whether there are drainers, is much
4227: * hotter than drain_workqueue() and already looks at @wq->flags.
4228: * Use __WQ_DRAINING so that queue doesn't have to check nr_drainers.
4229: */
4230: mutex_lock(&wq->mutex);
4231: if (!wq->nr_drainers++)
4232: wq->flags |= __WQ_DRAINING;
4233: mutex_unlock(&wq->mutex);
4234: reflush:
4235: __flush_workqueue(wq);
4236:
4237: mutex_lock(&wq->mutex);
4238:
4239: for_each_pwq(pwq, wq) {
4240: bool drained;
4241:
4242: raw_spin_lock_irq(&pwq->pool->lock);
4243: drained = pwq_is_empty(pwq);
4244: raw_spin_unlock_irq(&pwq->pool->lock);
4245:
4246: if (drained)
4247: continue;
4248:
4249: if (++flush_cnt == 10 ||
4250: (flush_cnt % 100 == 0 && flush_cnt <= 1000))
4251: pr_warn("workqueue %s: %s() isn't complete after %u tries\n",
4252: wq->name, __func__, flush_cnt);
4253:
4254: mutex_unlock(&wq->mutex);
4255: goto reflush;
4256: }
4257:
4258: if (!--wq->nr_drainers)
4259: wq->flags &= ~__WQ_DRAINING;
4260: mutex_unlock(&wq->mutex);
4261: }
SourceFile:kernel/workqueue.c]
|
| 112/2 |
2026/06/01 04:40 |
llm |
expert |
0m
Model:
Tokens:
input: 103043
output: 38
thoughts: 5158
Thoughts: **Analysis of the Workqueue Warning in Bluetooth HCI Device Shutdown**
Okay, so the `drain_workqueue` function sets the `__WQ_DRAINING` flag. I see it's called in `hci_dev_close_sync`! The core of this seems to be about timing, where `drain_workqueue` sets `__WQ_DRAINING`, flushes the workqueue, then *clears* `__WQ_DRAINING`. This means `__WQ_DRAINING` is only set *during* the execution of `drain_workqueue`. Got it. If `l2cap_info_timeout` queues `tx_work` while `drain_workqueue` is running, we get that warning.
Let's look at `hci_dev_close_sync`. `drain_workqueue(hdev->workqueue)` is called *before* things like `hci_conn_hash_flush`. That's the critical bit. So, while `drain_workqueue` is running, `hci_conn_hash_flush` hasn't happened. Hence, `l2cap_conn_del` has not been called, and the `info_timer` is still active, meaning it can fire exactly while `drain_workqueue` is running. If it does, and then `l2cap_info_timeout` queues `hdev->tx_work`, then it'll see `__WQ_DRAINING` and the warning is triggered. Bingo!
Essentially, the warning comes from a race between `l2cap_info_timeout` queuing work and `hci_dev_close_sync` draining the workqueue. Let's verify that. If they run concurrently: `drain_workqueue` sets `__WQ_DRAINING`, then `info_timer` fires, `l2cap_info_timeout` runs on the `system_wq`. `l2cap_info_timeout` calls `l2cap_conn_start`, which goes to `l2cap_send_cmd` and then `hci_send_acl`. That calls `queue_work`. `queue_work` checks the workqueue flags and trips the warning. Because `l2cap_info_timeout` runs on `system_wq`, it isn't chained, which triggers the warning. That's it!
Is this exploitable? Not really. It is a `WARN_ONCE`, and `queue_work` safely aborts when it sees the draining flag. No memory corruption, no use-after-free, nothing. The work isn't queued, so the packet isn't sent. The connection will close anyway. All safe. No leak, as `chan->data_q` is purged.
Is there any other timer that might trigger this? Yup. Any timer that queues work on `hdev->workqueue` and is cancelled *after* `drain_workqueue`. Like `conn->disc_work` (from `hci_conn_timeout`), since it queues `abort_conn_sync`. Ah, wait... does that go on `hdev->req_workqueue` or `hdev->workqueue`? Turns out, it's on `hdev->workqueue`! So `hci_conn_timeout` could trigger this, too.
So this isn't a *security* issue, it's a shutdown order issue. The sequence should be: `drain_workqueue`, then `hci_conn_hash_flush`. Because the timers are still active during `drain_workqueue`, they can queue work and get rejected. It's noisy, but not dangerous.
Let's double-check about this potentially leading to a UAF. Okay, if `l2cap_info_timeout` runs *after* `hci_conn_hash_flush`, then `disable_delayed_work_sync` would have been called. Therefore, it *cannot* run after. And, the timer cannot be scheduled after `hci_conn_hash_flush` either. And, if the timer *is* running, it accesses `conn` and `hdev`, then `l2cap_conn_del` would wait until that timer function has returned. Therefore there's no UAF. Also, since there is a reference to `hcon`, that in turn references `hdev`, even if `conn` is released. There is no UAF.
What about `hdev->workqueue` being destroyed? That happens in `hci_release_dev` after the reference count drops to zero. That's not the case here. It's just draining, not destroyed. Okay, so the problem is simply that work is queued on a draining workqueue, which causes a `WARN_ONCE`. Safe.
Exploitable? No
|
| 113/2 |
2026/06/01 04:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c Name:hci_dev_close]
Results: map[SourceCode: 509: int hci_dev_close(__u16 dev)
510: {
511: struct hci_dev *hdev;
512: int err;
513:
514: hdev = hci_dev_get(dev);
515: if (!hdev)
516: return -ENODEV;
517:
518: if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
519: err = -EBUSY;
520: goto done;
521: }
522:
523: cancel_work_sync(&hdev->power_on);
524: if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF))
525: cancel_delayed_work(&hdev->power_off);
526:
527: err = hci_dev_do_close(hdev);
528:
529: done:
530: hci_dev_put(hdev);
531: return err;
532: }
SourceFile:net/bluetooth/hci_core.c]
|
| 114/2 |
2026/06/01 04:40 |
llm |
expert |
0m
Model:
Tokens:
input: 109618
output: 66
thoughts: 167
Thoughts: **Analyzing the Potential Vulnerability**
Okay, so `hci_dev_close` calls `hci_dev_do_close(hdev)`. That much is clear. And thinking about it, an administrator, running `hciconfig hci0 down` or even `ip link set hci0 down`, can definitely trigger this code path. But, and this is important, this requires `CAP_NET_ADMIN`, so it's not a privilege escalation issue. That's good news.
Now, the crucial question: can a *remote* device somehow force this `hci_dev_do_close` to run? No, from my understanding of the code, it's not possible. The execution of `hci_dev_do_close` appears to be restricted to local administrative actions or situations like device removal. So a remote attack vector is unlikely here.
Therefore, this appears to be a benign warning at most. It looks like the "bug" is the result of a race condition between `hci_dev_do_close` and `l2cap_info_timeout`. Given the circumstances, it is not exploitable and can't lead to a DoS condition. The race is unfortunate, but not harmful.
Alright, I'm satisfied. Let's document the results.
|
| 115/2 |
2026/06/01 04:40 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 116/2 |
2026/06/01 04:40 |
llm |
expert |
0m
Model:
Tokens:
input: 110188
output: 783
thoughts: 731
Thoughts: **Analyzing the Workqueue Warning**
Okay, the results are in, time to synthesize this into a final response. This `workqueue: cannot queue hci_tx_work on wq hci0` warning is pretty specific, and it looks like a race condition is at play here. Essentially, the warning gets thrown within `__queue_work()` when a work item is being queued onto a workqueue that is either in the process of being destroyed or drained. The crux of the issue boils down to the timing of the `l2cap_info_timeout()` callback and the HCI device shutdown procedure.
`l2cap_info_timeout()` is what triggers the problem. It's associated with a timer (`conn->info_timer`) used when the host sends an `L2CAP_INFO_REQ`. If there's no response within the specified timeframe, the timer fires, and its callback, `l2cap_info_timeout()`, attempts to schedule `hdev->tx_work` on `hdev->workqueue` to send subsequent commands.
The race condition comes into view when the HCI device is being closed or unregistered, which can happen through commands like `hciconfig hci0 down` or even unplugging a USB Bluetooth dongle. The shutdown sequence calls `hci_dev_do_close()`, which then calls `hci_dev_close_sync()`. Now, *within* `hci_dev_close_sync()`, the code first drains the workqueue, setting the `__WQ_DRAINING` flag. However, the connection flushing and timer cancellation happen *after* the workqueue is drained. This means there's a window where `conn->info_timer` can fire *precisely* while `drain_workqueue()` is running. So when `l2cap_info_timeout()` executes, it attempts to queue its work, but the workqueue is in a draining state, and the warning gets thrown as a result.
The security implications are actually quite benign. The workqueue subsystem is designed to handle this kind of situation. As shown in the `__queue_work()` function, when it detects the destroying/draining flags, it simply returns early without queueing the work. The packet is dropped, which is the correct and safe behavior considering the device is shutting down. There's no memory leak or use-after-free, since associated resources are properly cleaned up later during connection cleanup.
From a threat perspective, this isn't an exploitable issue for standard users since bringing down the HCI interface requires `CAP_NET_ADMIN` privileges. While it's *technically* possible for a malicious USB peripheral to trigger the condition with a well-timed disconnect, the outcome is just a harmless kernel warning. There's no denial-of-service vulnerability here.
|
| 117/1 |
2026/06/01 04:41 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the kernel source code and the provided crash report,
here is the assessment of the bug:
### Root Cause Analysis
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered in
`__queue_work()` when a work item is queued on a workqueue that is currently
being destroyed or drained (`wq->flags & (__WQ_DESTROYING | __WQ_DRAINING)`).
In this specific report, the warning is triggered by `l2cap_info_timeout()`,
which is the callback for the `conn->info_timer` delayed work. This timer is
scheduled when the host sends an `L2CAP_INFO_REQ` to a remote device. If the
remote device does not respond in time, the timer fires, and
`l2cap_info_timeout()` attempts to queue `hdev->tx_work` on `hdev->workqueue` to
send further commands.
The race condition occurs when the HCI device is being closed or unregistered
(e.g., via `hciconfig hci0 down`, closing a `/dev/vhci` file descriptor, or
physically unplugging a USB Bluetooth dongle). The teardown process calls
`hci_dev_do_close()`, which in turn calls `hci_dev_close_sync()`.
Inside `hci_dev_close_sync()`, the code first drains the workqueue:
```c
/* Avoid potential lockdep warnings from the *_flush() calls by
* ensuring the workqueue is empty up front.
*/
drain_workqueue(hdev->workqueue);
```
This sets the `__WQ_DRAINING` flag on the workqueue. Later in the same function,
it flushes the connections and cancels their timers:
```c
hci_conn_hash_flush(hdev);
```
Because the timers (like `conn->info_timer`) are not cancelled until *after*
`drain_workqueue()` is called, it is possible for `conn->info_timer` to fire
exactly while `drain_workqueue()` is running. When `l2cap_info_timeout()`
executes, it calls `queue_work(hdev->workqueue, &hdev->tx_work)`. Since the
workqueue has the `__WQ_DRAINING` flag set, the workqueue subsystem rejects the
work and emits the `WARN_ONCE`.
### Security Impact
**Not Exploitable / No Denial of Service**
This is a benign warning. The workqueue subsystem safely handles this condition
by returning early without queueing the work (as seen in `__queue_work()`):
```c
if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue
%ps on wq %s\n", ...))) {
...
return;
}
```
Since the work is not queued, the packet is simply dropped. This is a perfectly
safe behavior, as the device is in the process of being shut down anyway. The
associated `skb` is safely purged later during the connection cleanup
(`hci_chan_list_flush()`), so there are no memory leaks, use-after-free
vulnerabilities, or deadlocks.
The bug cannot be triggered by unprivileged users, as bringing down the HCI
interface requires `CAP_NET_ADMIN`, and `/dev/vhci` is restricted to root. It
can technically be triggered by a malicious USB peripheral that simulates a
disconnect at the exact right time, but it only results in a harmless kernel
warning.]
|