Bluetooth: L2CAP: annotate locking for l2cap_chan_del()

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@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Pauli Virtanen 2026-09-02 00:04:34 +03:00 committed by Luiz Augusto von Dentz
parent dc1ddc65f0
commit 0e4d951a2f
3 changed files with 74 additions and 33 deletions

View File

@ -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);
@ -983,7 +986,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);

View File

@ -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;

View File

@ -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
@ -1471,6 +1476,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;
@ -1492,6 +1498,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) {
@ -1542,6 +1549,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;
@ -1893,6 +1901,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);
@ -4218,6 +4228,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);
@ -4419,6 +4431,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)) {
@ -4520,6 +4534,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,
@ -4634,6 +4650,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);
@ -4743,6 +4761,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);
@ -4783,6 +4803,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);
@ -4995,6 +5017,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)) {
@ -5213,6 +5237,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);
@ -5444,6 +5470,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);
@ -5533,6 +5561,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);
@ -5760,6 +5790,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);
@ -5789,6 +5821,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);
@ -7184,6 +7217,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