--- y/net/bluetooth/sco.c +++ x/net/bluetooth/sco.c @@ -49,6 +49,8 @@ struct sco_conn { struct sock *sk; struct delayed_work timeout_work; + struct work_struct free_work; + int err; unsigned int mtu; }; @@ -176,6 +178,23 @@ static void sco_chan_del(struct sock *sk sock_set_flag(sk, SOCK_ZAPPED); } +static void sco_conn_free_workfn(struct work_struct *work) +{ + struct sco_conn *conn = container_of(work, struct sco_conn, free_work); + struct sock *sk = conn->sk; + + lock_sock(sk); + sco_sock_clear_timer(sk); + sco_chan_del(sk, conn->err); + release_sock(sk); + /* pair with sock_hold(sk) in sco_conn_del() */ + sock_put(sk); + + /* Ensure no more work items will run before freeing conn */ + cancel_delayed_work_sync(&conn->timeout_work); + kfree(conn); +} + static void sco_conn_del(struct hci_conn *hcon, int err) { struct sco_conn *conn = hcon->sco_data; @@ -194,11 +213,12 @@ static void sco_conn_del(struct hci_conn sco_conn_unlock(conn); if (sk) { - lock_sock(sk); - sco_sock_clear_timer(sk); - sco_chan_del(sk, err); - release_sock(sk); - sock_put(sk); + /* kill sk in workqueue just to avoid lock_sock() with hci_cb_list_lock held */ + conn->err = err; + INIT_WORK(&conn->free_work, sco_conn_free_workfn); + queue_work(system_unbound_wq, &conn->free_work); + hcon->sco_data = NULL; + return; } /* Ensure no more work items will run before freeing conn. */