Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context"

This reverts commit 028a68886e which is
commit 4d94f05558271654670d18c26c912da0c1c15549 upstream.

It breaks the Android kernel abi and can be brought back in the future
in an abi-safe way if it is really needed.

Bug: 161946584
Change-Id: I91f4d72a6801ba1db6767c86df36365144715a29
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2025-01-11 07:48:03 +00:00
parent 6a643e39f7
commit e280ed497f
6 changed files with 57 additions and 97 deletions

View File

@@ -830,6 +830,7 @@ struct hci_conn_params {
extern struct list_head hci_dev_list; extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list; extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock; extern rwlock_t hci_dev_list_lock;
extern struct mutex hci_cb_list_lock;
#define hci_dev_set_flag(hdev, nr) set_bit((nr), (hdev)->dev_flags) #define hci_dev_set_flag(hdev, nr) set_bit((nr), (hdev)->dev_flags)
#define hci_dev_clear_flag(hdev, nr) clear_bit((nr), (hdev)->dev_flags) #define hci_dev_clear_flag(hdev, nr) clear_bit((nr), (hdev)->dev_flags)
@@ -1783,49 +1784,26 @@ struct hci_cb {
char *name; char *name;
bool (*match) (struct hci_conn *conn);
void (*connect_cfm) (struct hci_conn *conn, __u8 status); void (*connect_cfm) (struct hci_conn *conn, __u8 status);
void (*disconn_cfm) (struct hci_conn *conn, __u8 status); void (*disconn_cfm) (struct hci_conn *conn, __u8 status);
void (*security_cfm) (struct hci_conn *conn, __u8 status, void (*security_cfm) (struct hci_conn *conn, __u8 status,
__u8 encrypt); __u8 encrypt);
void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(1);
}; };
static inline void hci_cb_lookup(struct hci_conn *conn, struct list_head *list)
{
struct hci_cb *cb, *cpy;
rcu_read_lock();
list_for_each_entry_rcu(cb, &hci_cb_list, list) {
if (cb->match && cb->match(conn)) {
cpy = kmalloc(sizeof(*cpy), GFP_ATOMIC);
if (!cpy)
break;
*cpy = *cb;
INIT_LIST_HEAD(&cpy->list);
list_add_rcu(&cpy->list, list);
}
}
rcu_read_unlock();
}
static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status) static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
{ {
struct list_head list; struct hci_cb *cb;
struct hci_cb *cb, *tmp;
INIT_LIST_HEAD(&list); mutex_lock(&hci_cb_list_lock);
hci_cb_lookup(conn, &list); list_for_each_entry(cb, &hci_cb_list, list) {
list_for_each_entry_safe(cb, tmp, &list, list) {
if (cb->connect_cfm) if (cb->connect_cfm)
cb->connect_cfm(conn, status); cb->connect_cfm(conn, status);
kfree(cb);
} }
mutex_unlock(&hci_cb_list_lock);
if (conn->connect_cfm_cb) if (conn->connect_cfm_cb)
conn->connect_cfm_cb(conn, status); conn->connect_cfm_cb(conn, status);
@@ -1833,43 +1811,22 @@ static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason) static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
{ {
struct list_head list; struct hci_cb *cb;
struct hci_cb *cb, *tmp;
INIT_LIST_HEAD(&list); mutex_lock(&hci_cb_list_lock);
hci_cb_lookup(conn, &list); list_for_each_entry(cb, &hci_cb_list, list) {
list_for_each_entry_safe(cb, tmp, &list, list) {
if (cb->disconn_cfm) if (cb->disconn_cfm)
cb->disconn_cfm(conn, reason); cb->disconn_cfm(conn, reason);
kfree(cb);
} }
mutex_unlock(&hci_cb_list_lock);
if (conn->disconn_cfm_cb) if (conn->disconn_cfm_cb)
conn->disconn_cfm_cb(conn, reason); conn->disconn_cfm_cb(conn, reason);
} }
static inline void hci_security_cfm(struct hci_conn *conn, __u8 status,
__u8 encrypt)
{
struct list_head list;
struct hci_cb *cb, *tmp;
INIT_LIST_HEAD(&list);
hci_cb_lookup(conn, &list);
list_for_each_entry_safe(cb, tmp, &list, list) {
if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt);
kfree(cb);
}
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
}
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{ {
struct hci_cb *cb;
__u8 encrypt; __u8 encrypt;
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
@@ -1877,11 +1834,20 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
hci_security_cfm(conn, status, encrypt); mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
} }
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status) static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
{ {
struct hci_cb *cb;
__u8 encrypt; __u8 encrypt;
if (conn->state == BT_CONFIG) { if (conn->state == BT_CONFIG) {
@@ -1908,38 +1874,40 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
conn->sec_level = conn->pending_sec_level; conn->sec_level = conn->pending_sec_level;
} }
hci_security_cfm(conn, status, encrypt); mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
} }
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{ {
struct list_head list; struct hci_cb *cb;
struct hci_cb *cb, *tmp;
INIT_LIST_HEAD(&list); mutex_lock(&hci_cb_list_lock);
hci_cb_lookup(conn, &list); list_for_each_entry(cb, &hci_cb_list, list) {
list_for_each_entry_safe(cb, tmp, &list, list) {
if (cb->key_change_cfm) if (cb->key_change_cfm)
cb->key_change_cfm(conn, status); cb->key_change_cfm(conn, status);
kfree(cb);
} }
mutex_unlock(&hci_cb_list_lock);
} }
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
__u8 role) __u8 role)
{ {
struct list_head list; struct hci_cb *cb;
struct hci_cb *cb, *tmp;
INIT_LIST_HEAD(&list); mutex_lock(&hci_cb_list_lock);
hci_cb_lookup(conn, &list); list_for_each_entry(cb, &hci_cb_list, list) {
list_for_each_entry_safe(cb, tmp, &list, list) {
if (cb->role_switch_cfm) if (cb->role_switch_cfm)
cb->role_switch_cfm(conn, status, role); cb->role_switch_cfm(conn, status, role);
kfree(cb);
} }
mutex_unlock(&hci_cb_list_lock);
} }
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)

View File

@@ -58,6 +58,7 @@ DEFINE_RWLOCK(hci_dev_list_lock);
/* HCI callback list */ /* HCI callback list */
LIST_HEAD(hci_cb_list); LIST_HEAD(hci_cb_list);
DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */ /* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida); static DEFINE_IDA(hci_index_ida);
@@ -2957,7 +2958,9 @@ int hci_register_cb(struct hci_cb *cb)
{ {
BT_DBG("%p name %s", cb, cb->name); BT_DBG("%p name %s", cb, cb->name);
list_add_tail_rcu(&cb->list, &hci_cb_list); mutex_lock(&hci_cb_list_lock);
list_add_tail(&cb->list, &hci_cb_list);
mutex_unlock(&hci_cb_list_lock);
return 0; return 0;
} }
@@ -2967,8 +2970,9 @@ int hci_unregister_cb(struct hci_cb *cb)
{ {
BT_DBG("%p name %s", cb, cb->name); BT_DBG("%p name %s", cb, cb->name);
list_del_rcu(&cb->list); mutex_lock(&hci_cb_list_lock);
synchronize_rcu(); list_del(&cb->list);
mutex_unlock(&hci_cb_list_lock);
return 0; return 0;
} }

View File

@@ -1579,11 +1579,6 @@ done:
return lm; return lm;
} }
static bool iso_match(struct hci_conn *hcon)
{
return hcon->type == ISO_LINK || hcon->type == LE_LINK;
}
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
{ {
if (hcon->type != ISO_LINK) { if (hcon->type != ISO_LINK) {
@@ -1753,7 +1748,6 @@ drop:
static struct hci_cb iso_cb = { static struct hci_cb iso_cb = {
.name = "ISO", .name = "ISO",
.match = iso_match,
.connect_cfm = iso_connect_cfm, .connect_cfm = iso_connect_cfm,
.disconn_cfm = iso_disconn_cfm, .disconn_cfm = iso_disconn_cfm,
}; };

View File

@@ -8231,11 +8231,6 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
return NULL; return NULL;
} }
static bool l2cap_match(struct hci_conn *hcon)
{
return hcon->type == ACL_LINK || hcon->type == LE_LINK;
}
static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{ {
struct hci_dev *hdev = hcon->hdev; struct hci_dev *hdev = hcon->hdev;
@@ -8243,6 +8238,9 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
struct l2cap_chan *pchan; struct l2cap_chan *pchan;
u8 dst_type; u8 dst_type;
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (status) { if (status) {
@@ -8307,6 +8305,9 @@ int l2cap_disconn_ind(struct hci_conn *hcon)
static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{ {
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason); BT_DBG("hcon %p reason %d", hcon, reason);
l2cap_conn_del(hcon, bt_to_errno(reason)); l2cap_conn_del(hcon, bt_to_errno(reason));
@@ -8594,7 +8595,6 @@ drop:
static struct hci_cb l2cap_cb = { static struct hci_cb l2cap_cb = {
.name = "L2CAP", .name = "L2CAP",
.match = l2cap_match,
.connect_cfm = l2cap_connect_cfm, .connect_cfm = l2cap_connect_cfm,
.disconn_cfm = l2cap_disconn_cfm, .disconn_cfm = l2cap_disconn_cfm,
.security_cfm = l2cap_security_cfm, .security_cfm = l2cap_security_cfm,

View File

@@ -2130,11 +2130,6 @@ static int rfcomm_run(void *unused)
return 0; return 0;
} }
static bool rfcomm_match(struct hci_conn *hcon)
{
return hcon->type == ACL_LINK;
}
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
{ {
struct rfcomm_session *s; struct rfcomm_session *s;
@@ -2181,7 +2176,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
static struct hci_cb rfcomm_cb = { static struct hci_cb rfcomm_cb = {
.name = "RFCOMM", .name = "RFCOMM",
.match = rfcomm_match,
.security_cfm = rfcomm_security_cfm .security_cfm = rfcomm_security_cfm
}; };

View File

@@ -1367,13 +1367,11 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
return lm; return lm;
} }
static bool sco_match(struct hci_conn *hcon)
{
return hcon->type == SCO_LINK || hcon->type == ESCO_LINK;
}
static void sco_connect_cfm(struct hci_conn *hcon, __u8 status) static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{ {
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return;
BT_DBG("hcon %p bdaddr %pMR status %u", hcon, &hcon->dst, status); BT_DBG("hcon %p bdaddr %pMR status %u", hcon, &hcon->dst, status);
if (!status) { if (!status) {
@@ -1388,6 +1386,9 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{ {
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason); BT_DBG("hcon %p reason %d", hcon, reason);
sco_conn_del(hcon, bt_to_errno(reason)); sco_conn_del(hcon, bt_to_errno(reason));
@@ -1413,7 +1414,6 @@ drop:
static struct hci_cb sco_cb = { static struct hci_cb sco_cb = {
.name = "SCO", .name = "SCO",
.match = sco_match,
.connect_cfm = sco_connect_cfm, .connect_cfm = sco_connect_cfm,
.disconn_cfm = sco_disconn_cfm, .disconn_cfm = sco_disconn_cfm,
}; };