[PATCH AUTOSEL 6.19-5.10] Bluetooth: hci_conn: use mod_delayed_work for active mode timeout

Sasha Levin
2026-02-14T21:27:35Z

From: Stefan Sørensen <ssorensen@roku.com>

[ Upstream commit 49d0901e260739de2fcc90c0c29f9e31e39a2d9b ]

hci_conn_enter_active_mode() uses queue_delayed_work() with the
intention that the work will run after the given timeout. However,
queue_delayed_work() does nothing if the work is already queued, so
depending on the link policy we may end up putting the connection
into idle mode every hdev->idle_timeout ms.

Use mod_delayed_work() instead so the work is queued if not already
queued, and the timeout is updated otherwise.

Signed-off-by: Stefan Sørensen <ssorensen@roku.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

## Analysis of Bluetooth: hci_conn: use mod_delayed_work for active mode
timeout

### 1. COMMIT MESSAGE ANALYSIS

The commit message clearly describes a **behavioral bug**:

- `queue_delayed_work()` does nothing if the work is already queued
- This means the timeout is never refreshed/reset when
`hci_conn_enter_active_mode()` is called while work is already pending
- The consequence: the connection may enter idle mode prematurely (every
`hdev->idle_timeout` ms from the *first* queuing, not from the *last*
activity)
- `mod_delayed_work()` fixes this by resetting the timer if already
queued, or queuing if not

The commit message is well-written and explains the mechanism clearly.
The author (Stefan Sørensen) understood the semantic difference between
the two APIs.

### 2. CODE CHANGE ANALYSIS

The change is **minimal** - a two-line change replacing one function
call with another:

```c
- queue_delayed_work(hdev->workqueue, &conn->idle_work,
- msecs_to_jiffies(hdev->idle_timeout));
+ mod_delayed_work(hdev->workqueue, &conn->idle_work,
+ msecs_to_jiffies(hdev->idle_timeout));
```

- `queue_delayed_work()` → queues work only if not already queued;
returns false and does nothing if already queued
- `mod_delayed_work()` → if work is pending, cancels and re-queues with
the new delay; if not pending, queues it fresh

This is exactly the right fix. The function
`hci_conn_enter_active_mode()` is called whenever there's activity on
the connection, and the idle timeout should be reset from the last
activity, not remain fixed from the first.

### 3. BUG CLASSIFICATION

This is a **real behavioral bug** affecting Bluetooth connection power
management:

- **Without the fix**: A Bluetooth connection may be put into sniff/idle
mode too early if `hci_conn_enter_active_mode()` is called multiple
times. The first call sets the timer, and subsequent calls (which
indicate continued activity) fail to extend it. The connection gets
idled based on the *first* activity, not the *last*.
- **User impact**: Bluetooth connections may experience unexpected power
state transitions, potentially causing latency issues, dropped
connections, or degraded performance for active Bluetooth devices
(audio, input devices, etc.).

### 4. SCOPE AND RISK ASSESSMENT

- **Lines changed**: 2 (just the function name and indentation)
- **Files touched**: 1 (`net/bluetooth/hci_conn.c`)
- **Risk**: Very low. `mod_delayed_work()` is a well-established kernel
API that has been available for many years. The semantic change is
exactly what the code intended.
- **Could it break something?** Extremely unlikely. The only behavioral
change is that the idle timeout now properly resets on each call,
which is the correct behavior. If anything, the old behavior was
silently broken.

### 5. USER IMPACT

Bluetooth is widely used:
- Audio devices (headphones, speakers)
- Input devices (keyboards, mice)
- Phone tethering
- IoT devices

Premature idle mode transitions could affect any of these use cases.
While this may not cause crashes, it affects the reliability of
Bluetooth connections, which is important for stable kernel users.

### 6. STABLE TREE CRITERIA CHECK

- **Obviously correct?** Yes - the commit message explains the API
difference clearly, and `mod_delayed_work()` is the standard pattern
for "reset this timer"
- **Fixes a real bug?** Yes - incorrect idle timeout behavior for
Bluetooth connections
- **Small and contained?** Yes - 2-line change in a single file
- **No new features?** Correct - this is purely a bug fix
- **Tested?** Accepted by Bluetooth maintainer (Luiz Augusto von Dentz),
merged to mainline

### 7. DEPENDENCY CHECK

This change has no dependencies. `mod_delayed_work()` has been in the
kernel since 3.x era. The surrounding code in
`hci_conn_enter_active_mode()` is stable and has existed for a long
time.

### Conclusion

This is a clean, minimal, obvious bug fix. It corrects a long-standing
behavioral issue in Bluetooth power management where the idle timeout
was not being properly reset. The fix is exactly two lines, uses a well-
established API, has zero risk of regression, and improves Bluetooth
connection reliability for all users. It perfectly meets stable kernel
criteria.

**YES**

net/bluetooth/hci_conn.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b5e345fa6c344..1214216c7818e 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2615,8 +2615,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)

timer:
if (hdev->idle_timeout > 0)
- queue_delayed_work(hdev->workqueue, &conn->idle_work,
- msecs_to_jiffies(hdev->idle_timeout));
+ mod_delayed_work(hdev->workqueue, &conn->idle_work,
+ msecs_to_jiffies(hdev->idle_timeout));
}

/* Drop all connection on the device */
--
2.51.0