Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
780267e
workflow: Add workflow files for ci
tedd-an Nov 5, 2020
0665eae
workflows: Make use bluez/action-ci action
Vudentz Apr 9, 2026
131b04f
workflow/sync: Attempt to sync every 5 minutes
Vudentz Apr 9, 2026
68ea8ec
workflow/sync: Set workflow to use worflow branch
Vudentz Apr 9, 2026
616126a
workflow/ci: Add checks:write permission and explicit reopened trigger
Vudentz Apr 13, 2026
5ecbd93
Bluetooth: L2CAP: take chan->lock for l2cap_chan_add/ready/del
pv Aug 29, 2026
dfd5936
Bluetooth: L2CAP: add l2cap_chan_close_unlocked() and locking helpers
pv Aug 29, 2026
1467821
Bluetooth: L2CAP: fix race condition in l2cap_sock_shutdown()
pv Aug 29, 2026
e9ca93e
Bluetooth: 6lowpan: use l2cap_chan_close_unlocked()
pv Aug 29, 2026
bff182b
Bluetooth: L2CAP: remove unused l2cap_chan_close()
pv Aug 29, 2026
487bff8
Bluetooth: 6lowpan: avoid concurrent peer_del() in bt_6lowpan_disconnect
pv Aug 29, 2026
413cb38
Bluetooth: L2CAP: hold conn->lock for __l2cap_ecred_conn_rsp_defer
pv Aug 29, 2026
f1bd36e
Bluetooth: L2CAP: hold l2cap_conn::lock in l2cap_connect_cfm()
pv Aug 29, 2026
48be065
Bluetooth: L2CAP: add annotations for l2cap_chan list locking
pv Aug 29, 2026
3a338ca
Bluetooth: L2CAP: take lock for l2cap_chan_del in l2cap_ecred_rsp_defer
pv Aug 29, 2026
c8ee09d
Bluetooth: L2CAP: hold chan in l2cap_ecred_conn_rsp()
pv Aug 29, 2026
2c853d4
Bluetooth: L2CAP: annotate locking for l2cap_chan_del()
pv Aug 29, 2026
6edadbd
Bluetooth: L2CAP: annotate locking for l2cap_ops callbacks
pv Aug 29, 2026
34507b5
Bluetooth: L2CAP: make concurrent l2cap_set_timer() refcounting safe
pv Aug 29, 2026
985804c
Bluetooth: L2CAP: remove conditional locking from l2cap_connect()
pv Aug 29, 2026
3f686ce
Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn
pv Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
checks: write
pull-requests: write

jobs:
ci:
runs-on: ubuntu-latest
name: CI for Pull Request
steps:
- name: Checkout the source code
uses: actions/checkout@v3
with:
path: src/src

- name: CI
uses: bluez/action-ci@main
with:
task: ci
base_folder: src
space: kernel
github_token: ${{ secrets.GITHUB_TOKEN }}
email_token: ${{ secrets.EMAIL_TOKEN }}
patchwork_token: ${{ secrets.PATCHWORK_TOKEN }}
patchwork_user: ${{ secrets.PATCHWORK_USER }}

44 changes: 44 additions & 0 deletions .github/workflows/sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Sync

on:
schedule:
- cron: "*/5 * * * *"

jobs:
sync_repo:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: master

- name: Sync Repo
uses: bluez/action-ci@main
with:
task: sync
workflow: workflow
upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git'
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Cleanup PR
uses: bluez/action-ci@main
with:
task: cleanup
github_token: ${{ secrets.ACTION_TOKEN }}

sync_patchwork:
needs: sync_repo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Sync Patchwork
uses: bluez/action-ci@main
with:
task: patchwork
workflow: workflow
space: kernel
github_token: ${{ secrets.ACTION_TOKEN }}
email_token: ${{ secrets.EMAIL_TOKEN }}
patchwork_token: ${{ secrets.PATCHWORK_TOKEN }}
patchwork_user: ${{ secrets.PATCHWORK_USER }}
100 changes: 65 additions & 35 deletions include/net/bluetooth/l2cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -847,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,
Expand Down Expand Up @@ -954,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);
Expand All @@ -973,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);
Expand All @@ -987,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 */
44 changes: 24 additions & 20 deletions net/bluetooth/6lowpan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -912,18 +914,27 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
{
struct lowpan_peer *peer;
struct l2cap_chan *chan;

BT_DBG("conn %p dst type %u", conn, dst_type);

spin_lock(&devices_lock);

peer = lookup_peer(conn);
if (!peer)
if (!peer) {
spin_unlock(&devices_lock);
return -ENOENT;
}

chan = peer->chan;
l2cap_chan_hold(chan);

spin_unlock(&devices_lock);

BT_DBG("peer %p chan %p", peer, peer->chan);
BT_DBG("peer %p chan %p", peer, chan);

l2cap_chan_lock(peer->chan);
l2cap_chan_close(peer->chan, ENOENT);
l2cap_chan_unlock(peer->chan);
l2cap_chan_close_unlocked(chan, ENOENT);
l2cap_chan_put(chan);

return 0;
}
Expand Down Expand Up @@ -1025,9 +1036,9 @@ static void disconnect_all_peers(void)
struct lowpan_peer *peer;
int nchans;

/* l2cap_chan_close() cannot be called from RCU, and lock ordering
* chan->lock > devices_lock prevents taking write side lock, so copy
* then close.
/* l2cap_chan_close_unlocked() cannot be called from RCU, and lock
* ordering chan->lock > devices_lock prevents taking write side lock,
* so copy then close.
*/

rcu_read_lock();
Expand Down Expand Up @@ -1062,9 +1073,7 @@ static void disconnect_all_peers(void)
spin_unlock(&devices_lock);

for (i = 0; i < nchans; ++i) {
l2cap_chan_lock(chans[i]);
l2cap_chan_close(chans[i], ENOENT);
l2cap_chan_unlock(chans[i]);
l2cap_chan_close_unlocked(chans[i], ENOENT);
l2cap_chan_put(chans[i]);
}
} while (nchans);
Expand All @@ -1082,9 +1091,7 @@ static void do_enable_set(bool flag)

mutex_lock(&set_lock);
if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_close_unlocked(listen_chan, 0);
l2cap_chan_put(listen_chan);
}

Expand Down Expand Up @@ -1132,9 +1139,7 @@ static ssize_t lowpan_control_write(struct file *fp,

mutex_lock(&set_lock);
if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_close_unlocked(listen_chan, 0);
l2cap_chan_put(listen_chan);
listen_chan = NULL;
}
Expand Down Expand Up @@ -1303,10 +1308,9 @@ static void __exit bt_6lowpan_exit(void)
debugfs_remove(lowpan_control_debugfs);

if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_close_unlocked(listen_chan, 0);
l2cap_chan_put(listen_chan);
listen_chan = NULL;
}

disconnect_devices();
Expand Down
Loading
Loading