diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-08 16:20:43 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-08 16:20:43 +0100 |
| commit | 3887f7f8a1f1bfac2d41ea59cca2bc66aeb60fdf (patch) | |
| tree | 49fb9e72dd0c2f891a7435fad726716fcea18cd0 /include | |
| parent | 7a98dc3cfd2ea8962279797a39d96237bb927fc7 (diff) | |
| parent | 755cf7adf8dd2d12627cb7de223d35b12228e2f5 (diff) | |
| download | linux-next-3887f7f8a1f1bfac2d41ea59cca2bc66aeb60fdf.tar.gz linux-next-3887f7f8a1f1bfac2d41ea59cca2bc66aeb60fdf.zip | |
Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
# Conflicts:
# net/bluetooth/hci_sync.c
# net/bluetooth/l2cap_core.c
Diffstat (limited to 'include')
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 44 | ||||
| -rw-r--r-- | include/net/bluetooth/hci_h4.h | 60 | ||||
| -rw-r--r-- | include/net/bluetooth/l2cap.h | 102 |
3 files changed, 171 insertions, 35 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4105c446ca98..c12cd6873f65 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1030,6 +1030,9 @@ static inline bool hci_conn_sc_enabled(struct hci_conn *conn) static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; + + lockdep_assert_held(&hdev->lock); + list_add_tail_rcu(&c->list, &h->list); switch (c->type) { case ACL_LINK: @@ -1060,6 +1063,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; + lockdep_assert_held(&hdev->lock); + list_del_rcu(&c->list); synchronize_rcu(); @@ -1088,6 +1093,15 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) } } +#ifdef CONFIG_PROVE_RCU +#define HCI_CONN_HASH_LOCKDEP_CHECK(hdev) \ + RCU_LOCKDEP_WARN(!lockdep_is_held(&(hdev)->lock) && \ + !rcu_read_lock_held(), \ + "suspicious hci_conn locking") +#else +#define HCI_CONN_HASH_LOCKDEP_CHECK(hdev) do { } while (0 && (hdev)) +#endif + static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; @@ -1169,6 +1183,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1191,6 +1207,8 @@ hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev) struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1217,6 +1235,8 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1241,6 +1261,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1260,6 +1282,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1281,6 +1305,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_role(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1302,6 +1328,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1328,6 +1356,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1360,6 +1390,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1383,6 +1415,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1407,6 +1441,8 @@ hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1431,6 +1467,8 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state, struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1454,6 +1492,8 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big) struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1477,6 +1517,8 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle) struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -1546,6 +1588,8 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev) struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; + HCI_CONN_HASH_LOCKDEP_CHECK(hdev); + rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { diff --git a/include/net/bluetooth/hci_h4.h b/include/net/bluetooth/hci_h4.h new file mode 100644 index 000000000000..a37e7df8c9ce --- /dev/null +++ b/include/net/bluetooth/hci_h4.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Bluetooth HCI H:4 packet reassembly + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> + * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> + */ + +#ifndef __HCI_H4_H +#define __HCI_H4_H + +#include <linux/skbuff.h> +#include <linux/types.h> + +struct hci_dev; + +struct h4_recv_pkt { + u8 type; /* Packet type */ + u8 hlen; /* Header length */ + u8 loff; /* Data length offset in header */ + u8 lsize; /* Data length field size */ + u16 maxlen; /* Max overall packet length */ + int (*recv)(struct hci_dev *hdev, struct sk_buff *skb); +}; + +#define H4_RECV_ACL \ + .type = HCI_ACLDATA_PKT, \ + .hlen = HCI_ACL_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + +#define H4_RECV_SCO \ + .type = HCI_SCODATA_PKT, \ + .hlen = HCI_SCO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 1, \ + .maxlen = HCI_MAX_SCO_SIZE + +#define H4_RECV_EVENT \ + .type = HCI_EVENT_PKT, \ + .hlen = HCI_EVENT_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_MAX_EVENT_SIZE + +#define H4_RECV_ISO \ + .type = HCI_ISODATA_PKT, \ + .hlen = HCI_ISO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + +struct sk_buff *h4_recv_skb(struct hci_dev *hdev, u8 *alignment, u8 *padding, + struct sk_buff *skb, const unsigned char *buffer, + int count, const struct h4_recv_pkt *pkts, + int pkts_count); + +#endif /* __HCI_H4_H */ diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3d9a32094347..efb9b7f422d1 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; @@ -668,12 +643,45 @@ struct l2cap_conn { struct l2cap_chan *smp; - struct list_head chan_l; + struct list_head chan_l __guarded_by(&lock); struct mutex lock; struct kref ref; struct list_head users; }; +struct l2cap_ops { + char *name; + + int (*new_connection)(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) + __must_hold(&chan->lock) + __must_hold(&new_chan->lock); + 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) + __must_hold(&chan->lock); + 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) + __must_hold(&chan->lock); + void (*suspend) (struct l2cap_chan *chan); + void (*set_shutdown) (struct l2cap_chan *chan) + __must_hold(&chan->lock); + 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); @@ -758,6 +766,10 @@ enum { * otherwise considers all channels equal and will e.g. complain about a * connection oriented channel triggering SMP procedures or a listening * channel creating and locking a child channel. + * + * Lock nesting of channels at the same nesting level is allowed if the channels + * have the same l2cap_chan::conn and l2cap_chan::conn.lock is taken before the + * nested locks. l2cap_chan_try_sibling_lock() must be used. */ enum { L2CAP_NESTING_SMP, @@ -830,11 +842,13 @@ struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c); void l2cap_chan_put(struct l2cap_chan *c); static inline void l2cap_chan_lock(struct l2cap_chan *chan) + __acquires(&chan->lock) { mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting)); } static inline void l2cap_chan_unlock(struct l2cap_chan *chan) + __releases(&chan->lock) { mutex_unlock(&chan->lock); } @@ -845,12 +859,11 @@ static inline void l2cap_set_timer(struct l2cap_chan *chan, BT_DBG("chan %p state %s timeout %ld", chan, state_to_string(chan->state), timeout); - /* If delayed work cancelled do not hold(chan) - since it is already done with previous set_timer */ - if (!cancel_delayed_work(work)) - l2cap_chan_hold(chan); + l2cap_chan_hold(chan); - schedule_delayed_work(work, timeout); + /* put(chan) if timer was already queued so it already has a ref */ + if (mod_delayed_work(system_percpu_wq, work, timeout)) + l2cap_chan_put(chan); } static inline bool l2cap_clear_timer(struct l2cap_chan *chan, @@ -952,14 +965,16 @@ void l2cap_cleanup_sockets(void); bool l2cap_is_socket(struct socket *sock); void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan); -void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan); +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan) + __must_hold(&chan->lock) __must_hold(&chan->conn->lock); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); struct l2cap_chan *l2cap_chan_create(void); -void l2cap_chan_close(struct l2cap_chan *chan, int reason); +void l2cap_chan_close_unlocked(struct l2cap_chan *chan, int reason) + __must_not_hold(&chan->lock); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type, u16 timeout); int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu); @@ -971,11 +986,13 @@ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); -void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) + __must_hold(&conn->lock) __must_hold(&chan->lock); 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); @@ -985,4 +1002,19 @@ void l2cap_conn_put(struct l2cap_conn *conn); int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user); void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user); +bool l2cap_chan_lock_conn(struct l2cap_chan *chan) + __acquires(&chan->lock) __cond_acquires(true, &chan->conn->lock); + +/* Release macro for l2cap_chan_lock_conn, so context analysis understands it */ +#define l2cap_chan_unlock_conn(chan, conn_locked) \ + ({ \ + struct l2cap_chan *__chan = (chan); \ + struct l2cap_conn *__conn = __chan->conn; \ + l2cap_chan_unlock(__chan); \ + if (conn_locked) { \ + mutex_unlock(&__conn->lock); \ + l2cap_conn_put(__conn); \ + } \ + }) + #endif /* __L2CAP_H */ |
