[PATCH 12/16] Bluetooth: L2CAP: annotate locking for l2cap_chan_del()
From: Pauli Virtanen
Date: Sat Aug 29 2026 - 10:27:27 EST
Add context analysis annotations for chan->lock and chan->conn->lock
involving l2cap_chan_del() usage.
Add necessary annotations and related lockdep_assert_held to callers.
Move struct l2cap_ops definition after struct l2cap_conn, so that the
callbacks can be annotated.
In l2cap_chan_close_unlocked() we consider chan->conn->lock as locked
even if chan->conn == NULL, to avoid needing to define separate
__l2cap_chan_close/del for this NULL case.
Signed-off-by: Pauli Virtanen <pav@xxxxxx>
---
include/net/bluetooth/l2cap.h | 56 +++++++++++++++++++----------------
net/bluetooth/6lowpan.c | 2 ++
net/bluetooth/l2cap_core.c | 49 +++++++++++++++++++++++++-----
3 files changed, 74 insertions(+), 33 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index efd59b6f8afd..f612c9f884d1 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -614,31 +614,6 @@ struct l2cap_chan {
struct mutex lock;
};
-struct l2cap_ops {
- char *name;
-
- int (*new_connection)(struct l2cap_chan *chan,
- struct l2cap_chan *new_chan);
- int (*recv) (struct l2cap_chan * chan,
- struct sk_buff *skb);
- void (*teardown) (struct l2cap_chan *chan, int err);
- void (*close) (struct l2cap_chan *chan);
- void (*state_change) (struct l2cap_chan *chan,
- int state, int err);
- void (*ready) (struct l2cap_chan *chan);
- void (*defer) (struct l2cap_chan *chan);
- void (*resume) (struct l2cap_chan *chan);
- void (*suspend) (struct l2cap_chan *chan);
- void (*set_shutdown) (struct l2cap_chan *chan);
- long (*get_sndtimeo) (struct l2cap_chan *chan);
- struct pid *(*get_peer_pid) (struct l2cap_chan *chan);
- struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
- unsigned long hdr_len,
- unsigned long len, int nb);
- int (*filter) (struct l2cap_chan * chan,
- struct sk_buff *skb);
-};
-
struct l2cap_conn {
struct hci_conn *hcon;
struct hci_chan *hchan;
@@ -674,6 +649,34 @@ struct l2cap_conn {
struct list_head users;
};
+struct l2cap_ops {
+ char *name;
+
+ int (*new_connection)(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan);
+ int (*recv) (struct l2cap_chan * chan,
+ struct sk_buff *skb);
+ void (*teardown) (struct l2cap_chan *chan, int err)
+ __must_hold(&chan->lock);
+ void (*close) (struct l2cap_chan *chan);
+ void (*state_change) (struct l2cap_chan *chan,
+ int state, int err);
+ void (*ready) (struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
+ __must_hold(&chan->conn->lock);
+ void (*defer) (struct l2cap_chan *chan);
+ void (*resume) (struct l2cap_chan *chan);
+ void (*suspend) (struct l2cap_chan *chan);
+ void (*set_shutdown) (struct l2cap_chan *chan);
+ long (*get_sndtimeo) (struct l2cap_chan *chan);
+ struct pid *(*get_peer_pid) (struct l2cap_chan *chan);
+ struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
+ unsigned long hdr_len,
+ unsigned long len, int nb);
+ int (*filter) (struct l2cap_chan * chan,
+ struct sk_buff *skb);
+};
+
struct l2cap_user {
struct list_head list;
int (*probe) (struct l2cap_conn *conn, struct l2cap_user *user);
@@ -984,7 +987,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
typedef void (*l2cap_chan_func_t)(struct l2cap_chan *chan, void *data);
void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
void *data);
-void l2cap_chan_del(struct l2cap_chan *chan, int err);
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
+ __must_hold(&chan->lock) __must_hold(&chan->conn->lock);
void l2cap_send_conn_req(struct l2cap_chan *chan);
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn);
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index ddcdd2aff91f..836add41f5d1 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -722,6 +722,8 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
}
static inline void chan_ready_cb(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
+ __must_hold(&chan->conn->lock)
{
struct lowpan_btle_dev *dev;
bool new_netdev = false;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cc86399ef0f5..88a9596e4801 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -59,7 +59,8 @@ static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static void l2cap_retrans_timeout(struct work_struct *work);
static void l2cap_monitor_timeout(struct work_struct *work);
static void l2cap_ack_timeout(struct work_struct *work);
-static void __l2cap_chan_close(struct l2cap_chan *chan, int reason);
+static void __l2cap_chan_close(struct l2cap_chan *chan, int reason)
+ __must_hold(&chan->lock) __must_hold(&chan->conn->lock);
static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
{
@@ -681,6 +682,8 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
+ lockdep_assert(!chan->conn || lockdep_is_held(&chan->conn->lock));
+
__clear_chan_timer(chan);
BT_DBG("chan %p, err %d, state %s", chan, err,
@@ -812,12 +815,11 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
}
static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
+ __must_hold(&chan->conn->lock)
{
l2cap_state_change(chan, BT_DISCONN);
- lockdep_assert_held(&chan->lock);
- lockdep_assert_held(&chan->conn->lock);
-
__l2cap_ecred_conn_rsp_defer(chan);
}
@@ -848,8 +850,6 @@ static void __l2cap_chan_close(struct l2cap_chan *chan, int reason)
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
- lockdep_assert_held(&chan->lock);
-
switch (chan->state) {
case BT_LISTEN:
chan->ops->teardown(chan, 0);
@@ -934,7 +934,10 @@ void l2cap_chan_close_unlocked(struct l2cap_chan *chan, int reason)
bool have_conn;
have_conn = l2cap_chan_lock_conn(chan);
- __l2cap_chan_close(chan, reason);
+
+ /* Context analysis: consider chan->conn->lock held also if conn NULL */
+ context_unsafe(__l2cap_chan_close(chan, reason));
+
l2cap_chan_unlock_conn(chan, have_conn);
}
EXPORT_SYMBOL(l2cap_chan_close_unlocked);
@@ -1336,6 +1339,8 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
}
static void l2cap_chan_ready(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
+ __must_hold(&chan->conn->lock)
{
/* The channel may have already been flagged as connected in
* case of receiving data before the L2CAP info req/rsp
@@ -1464,6 +1469,7 @@ static void l2cap_ecred_connect(struct l2cap_chan *chan)
}
static void l2cap_le_start(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
__must_hold(&chan->conn->lock)
{
struct l2cap_conn *conn = chan->conn;
@@ -1485,6 +1491,7 @@ static void l2cap_le_start(struct l2cap_chan *chan)
}
static void l2cap_start_connection(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
__must_hold(&chan->conn->lock)
{
if (chan->conn->hcon->type == LE_LINK) {
@@ -1535,6 +1542,7 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
}
static void l2cap_do_start(struct l2cap_chan *chan)
+ __must_hold(&chan->lock)
__must_hold(&chan->conn->lock)
{
struct l2cap_conn *conn = chan->conn;
@@ -1886,6 +1894,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
l2cap_chan_hold(chan);
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
l2cap_chan_del(chan, err);
chan->ops->close(chan);
@@ -4204,6 +4214,8 @@ static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
__l2cap_chan_add(conn, chan);
+ lockdep_assert_held(&chan->conn->lock);
+
if (pchan->ops->new_connection &&
pchan->ops->new_connection(pchan, chan) < 0) {
l2cap_chan_del(chan, 0);
@@ -4403,6 +4415,8 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
switch (result) {
case L2CAP_CR_SUCCESS:
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
@@ -4504,6 +4518,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2 &&
chan->state != BT_CONNECTED) {
cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
@@ -4618,6 +4634,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
switch (result) {
case L2CAP_CONF_SUCCESS:
l2cap_conf_rfc_get(chan, rsp->data, len);
@@ -4727,6 +4745,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
@@ -4767,6 +4787,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
if (chan->state != BT_DISCONN) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
@@ -4979,6 +5001,8 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
switch (result) {
case L2CAP_CR_LE_SUCCESS:
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
@@ -5197,6 +5221,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
@@ -5427,6 +5453,8 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
@@ -5516,6 +5544,8 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
l2cap_chan_hold(chan);
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
/* Check that there is a dcid for each pending channel */
if (cmd_len < sizeof(dcid)) {
l2cap_chan_del(chan, ECONNREFUSED);
@@ -5743,6 +5773,8 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
continue;
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
l2cap_chan_del(chan, ECONNRESET);
l2cap_chan_unlock(chan);
@@ -5772,6 +5804,7 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
goto done;
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
l2cap_chan_del(chan, ECONNREFUSED);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
@@ -7159,6 +7192,8 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
l2cap_chan_lock(chan);
+ lockdep_assert_held(&chan->conn->lock);
+
BT_DBG("chan %p, len %d", chan, skb->len);
/* If we receive data on a fixed channel before the info req/rsp
--
2.55.0