* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-07-28 16:26 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-07-28 16:26 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pauli Virtanen, Siwei Zhang
[-- Attachment #1: Type: text/plain, Size: 16223 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/hci_sync.c
between commit:
12917f591cea1 ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
from the origin tree and commits:
76c2d047410ba ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
73b6871b261f3 ("Bluetooth: hci_sync: remove unnecessary hci_conn_get in create_conn_sync")
423e5fc465eb1 ("Bluetooth: hci_sync: fix hci_conn_del() use in hci_le_create_conn_sync")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined net/bluetooth/hci_sync.c
index c0b1fc293b496,7779d9d1663a4..0000000000000
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@@ -860,32 -860,6 +860,6 @@@ void hci_cmd_sync_cancel_entry(struct h
}
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);
- /* Dequeue one HCI command entry:
- *
- * - Lookup and cancel first entry that matches.
- */
- bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
- hci_cmd_sync_work_func_t func,
- void *data, hci_cmd_sync_work_destroy_t destroy)
- {
- struct hci_cmd_sync_work_entry *entry;
-
- mutex_lock(&hdev->cmd_sync_work_lock);
-
- entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
- if (!entry) {
- mutex_unlock(&hdev->cmd_sync_work_lock);
- return false;
- }
-
- _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
-
- mutex_unlock(&hdev->cmd_sync_work_lock);
-
- return true;
- }
- EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);
-
/* Dequeue HCI command entry:
*
* - Lookup and cancel any entry that matches by function callback or data or
@@@ -1233,10 -1207,11 +1207,11 @@@ static int hci_set_adv_set_random_addr_
}
static int
- hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
+ hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance,
const struct hci_cp_le_set_ext_adv_params *cp,
struct hci_rp_le_set_ext_adv_params *rp)
{
+ struct adv_info *adv;
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@@ -1264,11 -1239,15 +1239,15 @@@
if (!rp->status) {
hdev->adv_addr_type = cp->own_addr_type;
- if (!cp->handle) {
+ if (!instance) {
/* Store in hdev for instance 0 */
hdev->adv_tx_power = rp->tx_power;
- } else if (adv) {
- adv->tx_power = rp->tx_power;
+ } else {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->tx_power = rp->tx_power;
+ hci_dev_unlock(hdev);
}
}
@@@ -1284,9 -1263,13 +1263,13 @@@ static int hci_set_ext_adv_data_sync(st
int err;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->adv_data_changed)
+ if (!adv || !adv->adv_data_changed) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_adv_data(hdev, instance, pdu->data,
@@@ -1297,16 -1280,27 +1280,27 @@@
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
+ if (adv) {
+ adv->adv_data_changed = false;
+ hci_dev_unlock(hdev);
+ }
+
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
- if (err)
- return err;
+ if (err) {
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->adv_data_changed = true;
+ hci_dev_unlock(hdev);
+ }
- /* Update data if the command succeed */
- if (adv) {
- adv->adv_data_changed = false;
- } else {
+ return err;
+ }
+
+ if (!instance) {
memcpy(hdev->adv_data, pdu->data, len);
hdev->adv_data_len = len;
}
@@@ -1360,22 -1354,22 +1354,22 @@@ int hci_setup_ext_adv_instance_sync(str
struct adv_info *adv;
bool secondary_adv;
- if (instance > 0) {
- adv = hci_find_adv_instance(hdev, instance);
- if (!adv)
- return -EINVAL;
- } else {
- adv = NULL;
- }
-
/* Updating parameters of an active instance will return a
- * Command Disallowed error, so we must first disable the
- * instance if it is active.
+ * Command Disallowed error, so disable it before taking a snapshot.
*/
- if (adv) {
+ if (instance > 0) {
err = hci_disable_ext_adv_instance_sync(hdev, instance);
if (err)
return err;
+
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (!adv) {
+ hci_dev_unlock(hdev);
+ return -EINVAL;
+ }
+ } else {
+ adv = NULL;
}
flags = hci_adv_instance_flags(hdev, instance);
@@@ -1386,8 -1380,11 +1380,11 @@@
connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
mgmt_get_connectable(hdev);
- if (!is_advertising_allowed(hdev, connectable))
+ if (!is_advertising_allowed(hdev, connectable)) {
+ if (instance)
+ hci_dev_unlock(hdev);
return -EPERM;
+ }
/* Set require_privacy to true only when non-connectable
* advertising is used and it is not periodic.
@@@ -1398,8 -1395,11 +1395,11 @@@
err = hci_get_random_address(hdev, require_privacy,
adv_use_rpa(hdev, flags), adv,
&own_addr_type, &random_addr);
- if (err < 0)
+ if (err < 0) {
+ if (instance)
+ hci_dev_unlock(hdev);
return err;
+ }
memset(&cp, 0, sizeof(cp));
@@@ -1450,6 -1450,9 +1450,9 @@@
cp.channel_map = hdev->le_adv_channel_map;
cp.handle = adv ? adv->handle : instance;
+ if (instance)
+ hci_dev_unlock(hdev);
+
if (flags & MGMT_ADV_FLAG_SEC_2M) {
cp.primary_phy = HCI_ADV_PHY_1M;
cp.secondary_phy = HCI_ADV_PHY_2M;
@@@ -1462,12 -1465,12 +1465,12 @@@
cp.secondary_phy = HCI_ADV_PHY_1M;
}
- err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
+ err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp);
if (err)
return err;
/* Update adv data as tx power is known now */
- err = hci_set_ext_adv_data_sync(hdev, cp.handle);
+ err = hci_set_ext_adv_data_sync(hdev, instance);
if (err)
return err;
@@@ -1475,9 -1478,14 +1478,14 @@@
own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
bacmp(&random_addr, BDADDR_ANY)) {
/* Check if random address need to be updated */
- if (adv) {
- if (!bacmp(&random_addr, &adv->random_addr))
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (!adv || !bacmp(&random_addr, &adv->random_addr)) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+ hci_dev_unlock(hdev);
} else {
if (!bacmp(&random_addr, &hdev->random_addr))
return 0;
@@@ -1499,9 -1507,13 +1507,13 @@@ static int hci_set_ext_scan_rsp_data_sy
int err;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->scan_rsp_changed)
+ if (!adv || !adv->scan_rsp_changed) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_scan_rsp(hdev, instance, pdu->data);
@@@ -1511,15 -1523,27 +1523,27 @@@
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
+ if (adv) {
+ adv->scan_rsp_changed = false;
+ hci_dev_unlock(hdev);
+ }
+
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
- if (err)
- return err;
+ if (err) {
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->scan_rsp_changed = true;
+ hci_dev_unlock(hdev);
+ }
- if (adv) {
- adv->scan_rsp_changed = false;
- } else {
+ return err;
+ }
+
+ if (!instance) {
memcpy(hdev->scan_rsp_data, pdu->data, len);
hdev->scan_rsp_data_len = len;
}
@@@ -1534,8 -1558,14 +1558,14 @@@ static int __hci_set_scan_rsp_data_sync
memset(&cp, 0, sizeof(cp));
+ if (instance)
+ hci_dev_lock(hdev);
+
len = eir_create_scan_rsp(hdev, instance, cp.data);
+ if (instance)
+ hci_dev_unlock(hdev);
+
if (hdev->scan_rsp_data_len == len &&
!memcmp(cp.data, hdev->scan_rsp_data, len))
return 0;
@@@ -1670,9 -1700,13 +1700,13 @@@ static int hci_set_per_adv_data_sync(st
struct adv_info *adv = NULL;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->periodic)
+ if (!adv || !adv->periodic) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_per_adv_data(hdev, instance, pdu->data);
@@@ -1681,6 -1715,9 +1715,9 @@@
pdu->handle = adv ? adv->handle : instance;
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
+ if (adv)
+ hci_dev_unlock(hdev);
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
@@@ -3717,18 -3754,20 +3754,20 @@@ static const struct hci_init_stage hci_
int hci_reset_sync(struct hci_dev *hdev)
{
- int err;
-
set_bit(HCI_RESET, &hdev->flags);
- err = __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
- HCI_CMD_TIMEOUT);
- if (err)
- return err;
-
- return 0;
+ return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
+ HCI_CMD_TIMEOUT);
}
+ /* Send a raw HCI reset for use by vendor drivers */
+ int __hci_reset_sync(struct hci_dev *hdev)
+ {
+ return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ }
+ EXPORT_SYMBOL(__hci_reset_sync);
+
static int hci_init0_sync(struct hci_dev *hdev)
{
int err;
@@@ -6523,7 -6562,7 +6562,7 @@@ static int hci_le_ext_directed_advertis
if (err)
return err;
- err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
+ err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp);
if (err)
return err;
@@@ -6678,11 -6717,6 +6717,11 @@@ static int hci_le_create_conn_sync(stru
bt_dev_dbg(hdev, "conn %p", conn);
+ /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+ * clear_bit() at done.
+ */
+ hci_conn_get(conn);
+
clear_bit(HCI_CONN_SCANNING, &conn->flags);
conn->state = BT_CONNECT;
@@@ -6694,8 -6728,9 +6733,9 @@@
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
hdev->le_scan_type == LE_SCAN_ACTIVE &&
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
- hci_conn_del(conn);
- hci_conn_put(conn);
+ conn->state = BT_OPEN;
+ hci_abort_conn_sync(hdev, conn,
+ HCI_ERROR_REJ_LIMITED_RESOURCES);
return -EBUSY;
}
@@@ -6793,7 -6828,6 +6833,7 @@@ done
/* Re-enable advertising after the connection attempt is finished. */
hci_resume_advertising_sync(hdev);
+ hci_conn_put(conn);
return err;
}
@@@ -7068,11 -7102,6 +7108,6 @@@ static int hci_acl_create_conn_sync(str
else
cp.role_switch = 0x00;
- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
- * clear_bit() below.
- */
- hci_conn_get(conn);
-
/* Mark create connection in flight so hci_cancel_connect_sync() can
* cancel it while blocking on the connection complete event.
*/
@@@ -7084,17 -7113,27 +7119,27 @@@
conn->conn_timeout, NULL);
clear_bit(HCI_CONN_CREATE, &conn->flags);
- hci_conn_put(conn);
return err;
}
+ static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
+ int err)
+ {
+ struct hci_conn *conn = data;
+
+ hci_conn_put(conn);
+ }
+
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
- NULL);
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
+ hci_conn_get(conn),
+ hci_acl_create_conn_sync_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@@ -7105,36 -7144,41 +7150,41 @@@ static void create_le_conn_complete(str
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, conn))
- goto done;
+ goto unlock;
if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00);
- goto done;
+ goto unlock;
}
/* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev))
- goto done;
+ goto unlock;
/* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err));
- done:
+ unlock:
hci_dev_unlock(hdev);
+ done:
+ hci_conn_put(conn);
}
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
+ hci_conn_get(conn),
create_le_conn_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@@ -7257,7 -7301,7 +7307,7 @@@ static void create_pa_complete(struct h
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
@@@ -7281,6 -7325,8 +7331,8 @@@
unlock:
hci_dev_unlock(hdev);
+ done:
+ hci_conn_put(conn);
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
@@@ -7431,8 -7477,11 +7483,11 @@@ int hci_connect_pa_sync(struct hci_dev
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
+ hci_conn_get(conn),
create_pa_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@@ -7443,10 -7492,12 +7498,12 @@@ static void create_big_complete(struct
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
- if (hci_conn_valid(hdev, conn))
- clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+
+ done:
+ hci_conn_put(conn);
}
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
@@@ -7498,8 -7549,14 +7555,14 @@@ int hci_connect_big_sync(struct hci_de
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
+ if (!conn)
+ return 0;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
+ hci_conn_get(conn),
create_big_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@@ -7514,6 -7571,8 +7577,8 @@@ static void past_complete(struct hci_de
bt_dev_dbg(hdev, "err %d", err);
+ hci_conn_put(past->conn);
+ hci_conn_put(past->le);
kfree(past);
}
@@@ -7578,8 -7637,8 +7643,8 @@@ int hci_past_sync(struct hci_conn *conn
if (!data)
return -ENOMEM;
- data->conn = conn;
- data->le = le;
+ data->conn = hci_conn_get(conn);
+ data->le = hci_conn_get(le);
if (conn->role == HCI_ROLE_MASTER)
err = hci_cmd_sync_queue_once(conn->hdev,
@@@ -7589,8 -7648,11 +7654,11 @@@
err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
data, past_complete);
- if (err)
+ if (err) {
+ hci_conn_put(data->conn);
+ hci_conn_put(data->le);
kfree(data);
+ }
return (err == -EEXIST) ? 0 : err;
}
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-08-05 14:36 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-08-05 14:36 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Cen Zhang, Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pauli Virtanen
[-- Attachment #1: Type: text/plain, Size: 1256 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/6lowpan.c
between commit:
518aa9505fa10 ("Bluetooth: 6lowpan: hold L2CAP conn across debugfs control")
from the origin tree and commits:
d740cf77461a4 ("Bluetooth: add annotations for l2cap_data locking context")
d138ff8690c8e ("Bluetooth: 6lowpan: hold L2CAP conn across debugfs control")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined net/bluetooth/6lowpan.c
index d504a363a30f3,30f4afa18bc8f..0000000000000
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@@ -1007,6 -1007,8 +1007,8 @@@ static int get_l2cap_conn(char *buf, bd
return -ENOENT;
}
+ lockdep_assert_held(&hcon->hdev->lock);
+
*conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-08-05 14:35 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-08-05 14:35 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pauli Virtanen
[-- Attachment #1: Type: text/plain, Size: 4299 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
include/net/bluetooth/hci_core.h
between commit:
af24e338bf5da ("Bluetooth: ISO: fix race of kfree vs kref_get_unless_zero")
from the origin tree and commit:
d740cf77461a4 ("Bluetooth: add annotations for l2cap_data locking context")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined include/net/bluetooth/hci_core.h
index 3df59849dcbea,e07418a5adce7..0000000000000
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@@ -416,6 -416,7 +416,7 @@@ struct hci_dev
__u16 le_conn_max_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
+ __u16 le_min_rate_interval;
__u16 le_def_tx_len;
__u16 le_def_tx_time;
__u16 le_max_tx_len;
@@@ -645,6 -646,8 +646,8 @@@
int (*setup)(struct hci_dev *hdev);
int (*shutdown)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
+ /* Handle HCI_EV_VENDOR; return true if handled, false otherwise */
+ bool (*handle_ev_vendor)(struct hci_dev *hdev, struct sk_buff *skb);
void (*notify)(struct hci_dev *hdev, unsigned int evt);
void (*hw_error)(struct hci_dev *hdev, u8 code);
int (*post_init)(struct hci_dev *hdev);
@@@ -720,6 -723,11 +723,11 @@@ struct hci_conn
__u16 le_conn_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
+ __u16 le_rate_interval;
+ __u16 le_subrate;
+ __u16 le_rate_latency;
+ __u16 le_cont_num;
+ __u16 le_rate_supv_timeout;
__u8 le_adv_data[HCI_MAX_EXT_AD_LENGTH];
__u8 le_adv_data_len;
__u8 le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN];
@@@ -769,7 -777,7 +777,7 @@@
struct hci_dev *hdev;
spinlock_t proto_lock; /* lock guarding protocol data */
- void *l2cap_data;
+ void *l2cap_data __guarded_by(&proto_lock, &hdev->lock);
void *sco_data;
void *iso_data __guarded_by(&proto_lock);
@@@ -812,6 -820,14 +820,14 @@@ struct hci_conn_params
u16 conn_latency;
u16 supervision_timeout;
+ u16 rate_min_interval;
+ u16 rate_max_interval;
+ u16 subrate_min;
+ u16 subrate_max;
+ u16 max_latency;
+ u16 cont_num;
+ u16 rate_supv_timeout;
+
enum {
HCI_AUTO_CONN_DISABLED,
HCI_AUTO_CONN_REPORT,
@@@ -1771,7 -1787,13 +1787,13 @@@ int hci_register_suspend_notifier(struc
int hci_unregister_suspend_notifier(struct hci_dev *hdev);
int hci_suspend_dev(struct hci_dev *hdev);
int hci_resume_dev(struct hci_dev *hdev);
- int hci_reset_dev(struct hci_dev *hdev);
+ int __hci_reset_dev(struct hci_dev *hdev, u8 hw_err_code);
+
+ static inline int hci_reset_dev(struct hci_dev *hdev)
+ {
+ return __hci_reset_dev(hdev, 0);
+ }
+
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
@@@ -2079,6 -2101,11 +2101,11 @@@ void hci_conn_del_sysfs(struct hci_con
#define le_cs_host_capable(dev) \
((dev)->le_features[5] & HCI_LE_CS_HOST)
+ #define le_sci_capable(dev) \
+ ((dev)->le_features[9] & HCI_LE_SCI)
+ #define le_sci_enabled(dev) \
+ (le_enabled(dev) && le_sci_capable(dev))
+
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
@@@ -2494,6 -2521,8 +2521,8 @@@ void mgmt_advertising_removed(struct so
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
bdaddr_t *bdaddr, u8 addr_type);
+ void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn,
+ u8 status);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-07-17 14:37 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-07-17 14:37 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Kiran K, Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz
[-- Attachment #1: Type: text/plain, Size: 17803 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
drivers/bluetooth/btintel_pcie.c
between commit:
9c36951474d8e ("Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe()")
from the origin tree and commit:
603b91aeb20a2 ("Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe()")
ca75417ab1793 ("Bluetooth: btintel_pcie: split coredump worker into per-trigger works")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/bluetooth/btintel_pcie.c
index 2b7231be5973d,2e28847263ab0..0000000000000
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@@ -1446,72 -1446,134 +1446,134 @@@ exit_on_error
return err;
}
+ /* Queue a coredump dump_traces() pass.
+ *
+ * Returns true if a new coredump was queued, false if one was already
+ * in-flight (the BTINTEL_PCIE_COREDUMP_INPROGRESS bit serves as the
+ * single-writer guard for the @coredump_work item) or the workqueue is
+ * disabled (reset / remove in progress).
+ *
+ * Always queue this AFTER any companion event-reader work (hwexp /
+ * fwtrigger) so that, on the ordered @dump_workqueue, the event reader
+ * runs first and populates dmp_hdr.event_type / event_id before
+ * dump_traces consumes them.
+ */
+ static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
+ u16 trigger_reason)
+ {
+ if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
+ return false;
+
+ data->dmp_hdr.trigger_reason = trigger_reason;
+
+ if (queue_work(data->dump_workqueue, &data->coredump_work))
+ return true;
+
+ /* Workqueue is disabled (reset/remove drained it). Release the
+ * guard so a later trigger, after re-probe, can succeed.
+ */
+ clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
+ return false;
+ }
+
static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
{
bt_dev_dbg(data->hdev, "Received firmware smart trigger cause");
- if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags))
+ /* Per-work guard: deduplicate concurrent FW-trigger interrupts.
+ * Cleared at the tail of btintel_pcie_fwtrigger_worker().
+ */
+ if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
+ &data->flags))
return;
- /* Trigger device core dump when there is FW assert */
- if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
- data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
+ if (!queue_work(data->dump_workqueue, &data->fwtrigger_work)) {
+ clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
+ return;
+ }
- queue_work(data->coredump_workqueue, &data->coredump_work);
+ /* Queue coredump after the fwtrigger event reader so dmp_hdr.event_*
+ * is populated before dump_traces consumes it.
+ */
+ btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
}
static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
{
bt_dev_err(data->hdev, "Received hw exception interrupt");
+ /* CORE_HALTED is the single-writer guard for this handler. It is
+ * set once on first HW exception and cleared only by re-probe
+ * (data is reallocated), so it also serializes hwexp_work
+ * scheduling without needing a separate bit.
+ */
if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
return;
- if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags))
- return;
+ /* Queue companion coredump first so it is appended after hwexp_work
+ * on the ordered @dump_workqueue (preserves the original
+ * coredump-then-hwexp ordering).
+ */
+ btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
- /* Trigger device core dump when there is HW exception */
- if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
- data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
-
- queue_work(data->coredump_workqueue, &data->coredump_work);
+ queue_work(data->dump_workqueue, &data->hwexp_work);
}
static void btintel_pcie_coredump_worker(struct work_struct *work)
{
struct btintel_pcie_data *data = container_of(work,
struct btintel_pcie_data, coredump_work);
- int err;
/* hdev is NULL until setup_hdev() succeeds, and is cleared on
* teardown after disable_work_sync() drains us; bail in that case.
*/
+ if (!data->hdev)
+ goto out;
+
+ btintel_pcie_dump_traces(data->hdev);
+ out:
+ /* Release guard last so a new trigger can run only after this
+ * pass has fully completed (including dev_coredumpv()).
+ */
+ clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
+ }
+
+ static void btintel_pcie_hwexp_worker(struct work_struct *work)
+ {
+ struct btintel_pcie_data *data = container_of(work,
+ struct btintel_pcie_data, hwexp_work);
+
if (!data->hdev)
return;
- if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
- err = btintel_pcie_dump_fwtrigger_event(data);
- if (err)
- bt_dev_warn(data->hdev, "failed to log fwtrigger event");
- clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
- }
+ /* Unlike usb products, controller will not send hardware exception
+ * event on exception. Instead controller writes the hardware event
+ * to device memory along with optional debug events, raises MSIX
+ * and halts. Driver shall read the exception event from device
+ * memory and passes it to the stack for further processing.
+ *
+ * Re-entry is gated by BTINTEL_PCIE_CORE_HALTED in the IRQ
+ * handler, which is only cleared by re-probe; no per-work bit
+ * is needed here.
+ */
+ btintel_pcie_read_hwexp(data);
+ }
- if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
- btintel_pcie_dump_traces(data->hdev);
- clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
- }
+ static void btintel_pcie_fwtrigger_worker(struct work_struct *work)
+ {
+ struct btintel_pcie_data *data = container_of(work,
+ struct btintel_pcie_data, fwtrigger_work);
+ int err;
- if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) {
- /* Unlike usb products, controller will not send hardware
- * exception event on exception. Instead controller writes the
- * hardware event to device memory along with optional debug
- * events, raises MSIX and halts. Driver shall read the
- * exception event from device memory and passes it stack for
- * further processing.
- */
- btintel_pcie_read_hwexp(data);
- clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
- }
+ if (!data->hdev)
+ goto out;
+
+ err = btintel_pcie_dump_fwtrigger_event(data);
+ if (err)
+ bt_dev_warn(data->hdev, "failed to log fwtrigger event");
+ out:
+ /* Release guard last; matches set in fw_trigger handler. */
+ clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
}
static void btintel_pcie_rx_work(struct work_struct *work)
@@@ -2488,8 -2550,6 +2550,6 @@@ static void btintel_pcie_inc_recovery_c
}
}
- static void btintel_pcie_reset(struct hci_dev *hdev);
-
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
{
union acpi_object *obj, argv4;
@@@ -2650,20 -2710,22 +2710,22 @@@ static void btintel_pcie_reset_work(str
btintel_pcie_synchronize_irqs(data);
flush_work(&data->rx_work);
- /* Drain any in-flight coredump and block new ones across reset.
- * Safe from self-deadlock: coredump_work runs on a separate wq.
+ /* Drain any in-flight dump workers and block new ones across reset.
+ * Safe from self-deadlock: they all run on a separate wq.
*/
disable_work_sync(&data->coredump_work);
+ disable_work_sync(&data->hwexp_work);
+ disable_work_sync(&data->fwtrigger_work);
bt_dev_dbg(data->hdev, "Release bluetooth interface");
/* Both reset paths follow the same contract: on success they
* destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
- * the coredump_work with disable count 0), so enable_work() must
+ * the dump workers with disable count 0), so enable_work() must
* NOT be called on the success path. Only the FLR path can fail
* with 'data' still alive, in which case we balance the
- * disable_work_sync() above so a later successful reset is not
- * permanently blocked.
+ * disable_work_sync() calls above so a later successful reset is
+ * not permanently blocked.
*
* pci_lock_rescan_remove() (held above) serializes against PCI
* device addition/removal (hotplug), so no device can be added to
@@@ -2674,64 -2736,97 +2736,97 @@@
goto out;
}
- if (btintel_pcie_perform_flr(data))
+ if (btintel_pcie_perform_flr(data)) {
enable_work(&data->coredump_work);
+ enable_work(&data->hwexp_work);
+ enable_work(&data->fwtrigger_work);
+ }
out:
pci_dev_put(pdev);
pci_unlock_rescan_remove();
}
- static void btintel_pcie_reset(struct hci_dev *hdev)
+ /* Schedule a device reset of the requested type.
+ *
+ * BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters
+ * (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error
+ * path, etc.) so that:
+ *
+ * - dev_data->reset_type is written by exactly one caller (the
+ * thread that wins test_and_set_bit), eliminating the race where
+ * a second hw_error could clobber an already-scheduled reset's
+ * type;
+ * - the write happens AFTER the bit is set, so reset_work observes
+ * it through schedule_work()'s memory ordering;
+ * - losers return without touching reset_type or scheduling the
+ * work, so concurrent triggers are silently coalesced into the
+ * in-flight one (whose recovery will reinitialize the device
+ * regardless of the dropped trigger's variant).
+ *
+ * The bit is cleared only by .remove() / re-probe via fresh devm
+ * allocation, which is the intended one-shot semantics: a reset
+ * tears down and re-probes 'data', so there is no "in-flight"
+ * reset to follow up after device_reprobe() succeeds.
+ */
+ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
+ enum btintel_pcie_reset_type type)
{
- struct btintel_pcie_data *data;
-
- data = hci_get_drvdata(hdev);
-
if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
return;
if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
return;
+ data->reset_type = type;
+
pci_dev_get(data->pdev);
schedule_work(&data->reset_work);
}
+ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
+ {
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
+ }
+
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
- struct btintel_pcie_dev_recovery *data;
+ struct btintel_pcie_dev_recovery *rec;
struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev);
struct pci_dev *pdev = dev_data->pdev;
+ enum btintel_pcie_reset_type type;
time64_t retry_window;
+ if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags))
+ return;
+
btintel_pcie_dump_debug_registers(hdev);
- data = btintel_pcie_get_recovery(pdev, &hdev->dev);
- if (!data)
+ rec = btintel_pcie_get_recovery(pdev, &hdev->dev);
+ if (!rec)
return;
- if (code == 0x13)
- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR;
- else
- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
+ type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR
+ : BTINTEL_PCIE_IOSF_PRR_FLR;
bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code,
- dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
- retry_window = ktime_get_boottime_seconds() - data->last_error;
+ type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
+ retry_window = ktime_get_boottime_seconds() - rec->last_error;
if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
- data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
+ rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d",
- BTINTEL_PCIE_FLR_MAX_RETRY, data->count);
+ BTINTEL_PCIE_FLR_MAX_RETRY, rec->count);
bt_dev_dbg(hdev, "Boot time: %lld seconds",
ktime_get_boottime_seconds());
bt_dev_dbg(hdev, "last error at: %lld seconds",
- data->last_error);
+ rec->last_error);
return;
}
btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
- btintel_pcie_reset(hdev);
+ btintel_pcie_request_reset(dev_data, type);
}
static bool btintel_pcie_wakeup(struct hci_dev *hdev)
@@@ -2821,7 -2916,7 +2916,7 @@@ static int btintel_pcie_setup_hdev(stru
hdev->hw_error = btintel_pcie_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
- hdev->reset = btintel_pcie_reset;
+ hdev->reset = btintel_pcie_hci_reset;
hdev->wakeup = btintel_pcie_wakeup;
hdev->hci_drv = &btintel_pcie_hci_drv;
@@@ -2869,8 -2964,8 +2964,8 @@@ static int btintel_pcie_probe(struct pc
if (!data->workqueue)
return -ENOMEM;
- data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
- if (!data->coredump_workqueue) {
+ data->dump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
+ if (!data->dump_workqueue) {
destroy_workqueue(data->workqueue);
return -ENOMEM;
}
@@@ -2879,6 -2974,8 +2974,8 @@@
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
+ INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
+ INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker);
data->boot_stage_cache = 0x00;
data->img_resp_cache = 0x00;
@@@ -2921,7 -3018,7 +3018,7 @@@ exit_error
/* reset device before exit */
btintel_pcie_reset_bt(data);
- destroy_workqueue(data->coredump_workqueue);
+ destroy_workqueue(data->dump_workqueue);
pci_clear_master(pdev);
@@@ -2940,12 -3037,14 +3037,14 @@@ static void btintel_pcie_remove(struct
return;
}
- /* Permanently block coredump triggers and drain the worker before
- * tearing down. Must run before cancel_work_sync(&reset_work) so
- * the disable counter stays >= 1 even after reset_work()'s
+ /* Permanently block all dump triggers and drain the workers before
+ * tearing down. Must run before disable_work_sync(&reset_work) so
+ * the disable counters stay >= 1 even after reset_work()'s
* balanced enable_work() (counter 2 -> 1, never reaching 0).
*/
disable_work_sync(&data->coredump_work);
+ disable_work_sync(&data->hwexp_work);
+ disable_work_sync(&data->fwtrigger_work);
/* Cancel pending reset work. Skip only when remove() is called from
* within the reset work itself (PLDR device_reprobe path) to avoid
@@@ -2973,7 -3072,7 +3072,7 @@@
btintel_pcie_release_hdev(data);
- destroy_workqueue(data->coredump_workqueue);
+ destroy_workqueue(data->dump_workqueue);
destroy_workqueue(data->workqueue);
btintel_pcie_free(data);
@@@ -2992,16 -3091,8 +3091,8 @@@ static void btintel_pcie_coredump(struc
if (!data)
return;
- if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
- return;
-
- data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
- /* queue_work() returns false if the work is disabled (reset or
- * remove in progress); clear the in-progress bit so a later
- * trigger can succeed once the work is re-enabled.
- */
- if (!queue_work(data->coredump_workqueue, &data->coredump_work))
- clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
+ btintel_pcie_queue_coredump(data,
+ BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER);
}
#endif
@@@ -3113,8 -3204,7 +3204,7 @@@ static int btintel_pcie_resume(struct d
if (data->pm_sx_event == PM_EVENT_FREEZE ||
data->pm_sx_event == PM_EVENT_HIBERNATE) {
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
- data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
- btintel_pcie_reset(data->hdev);
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
return 0;
}
@@@ -3138,14 -3228,10 +3228,10 @@@
if (btintel_pcie_in_error(data) ||
btintel_pcie_in_device_halt(data)) {
bt_dev_err(data->hdev, "Controller in error state for D0 entry");
- if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
- &data->flags)) {
- data->dmp_hdr.trigger_reason =
- BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
- queue_work(data->coredump_workqueue, &data->coredump_work);
- }
+ btintel_pcie_queue_coredump(data,
+ BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
- btintel_pcie_reset(data->hdev);
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
return err;
}
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-06-01 14:48 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-06-01 14:48 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Dudu Lu, Jann Horn, Linux Kernel Mailing List,
Linux Next Mailing List, Luiz Augusto von Dentz
[-- Attachment #1: Type: text/plain, Size: 3951 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/bnep/core.c
between commits:
59e932ded949f ("Bluetooth: bnep: Fix UAF read of dev->name")
72b8deccff17a ("Bluetooth: bnep: fix incorrect length parsing in bnep_rx_frame() extension handling")
from the origin tree and commits:
feaef2aa27201 ("Bluetooth: bnep: fix incorrect length parsing in bnep_rx_frame() extension handling")
ffeee619a13bf ("Bluetooth: bnep: Fix UAF read of dev->name")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined net/bluetooth/bnep/core.c
index 0de5df690bd0b,5c5f53ff30e8e..0000000000000
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@@ -206,14 -206,11 +206,11 @@@ static int bnep_ctrl_set_mcfilter(struc
return 0;
}
- static int bnep_rx_control(struct bnep_session *s, void *data, int len)
+ static int bnep_rx_control_cmd(struct bnep_session *s, u8 cmd, void *data,
+ int len)
{
- u8 cmd = *(u8 *)data;
int err = 0;
- data++;
- len--;
-
switch (cmd) {
case BNEP_CMD_NOT_UNDERSTOOD:
case BNEP_SETUP_CONN_RSP:
@@@ -254,6 -251,14 +251,14 @@@
return err;
}
+ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
+ {
+ if (len < 1)
+ return -EILSEQ;
+
+ return bnep_rx_control_cmd(s, *(u8 *)data, data + 1, len - 1);
+ }
+
static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
{
struct bnep_ext_hdr *h;
@@@ -299,19 -304,26 +304,26 @@@ static int bnep_rx_frame(struct bnep_se
{
struct net_device *dev = s->dev;
struct sk_buff *nskb;
+ u8 *data;
u8 type, ctrl_type;
dev->stats.rx_bytes += skb->len;
- type = *(u8 *) skb->data;
- skb_pull(skb, 1);
- ctrl_type = *(u8 *)skb->data;
+ data = skb_pull_data(skb, sizeof(type));
+ if (!data)
+ goto badframe;
+ type = *data;
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
- if (bnep_rx_control(s, skb->data, skb->len) < 0) {
+ data = skb_pull_data(skb, sizeof(ctrl_type));
+ if (!data)
+ goto badframe;
+ ctrl_type = *data;
+
+ if (bnep_rx_control_cmd(s, ctrl_type, skb->data, skb->len) < 0) {
dev->stats.tx_errors++;
kfree_skb(skb);
return 0;
@@@ -324,24 -336,27 +336,31 @@@
/* Verify and pull ctrl message since it's already processed */
switch (ctrl_type) {
- case BNEP_SETUP_CONN_REQ:
- /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
- if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
+ case BNEP_SETUP_CONN_REQ: {
+ u8 uuid_size;
+
+ /* Pull uuid_size and the dst/src service UUIDs. */
+ data = skb_pull_data(skb, sizeof(uuid_size));
+ if (!data)
+ goto badframe;
+ uuid_size = *data;
+ if (!skb_pull(skb, uuid_size + uuid_size))
goto badframe;
break;
+ }
case BNEP_FILTER_MULTI_ADDR_SET:
- case BNEP_FILTER_NET_TYPE_SET:
- /* Pull: len (2 b), data (len bytes) */
- data = skb_pull_data(skb, sizeof(u16));
- if (!data)
+ case BNEP_FILTER_NET_TYPE_SET: {
+ u8 *hdr;
+
+ /* Pull ctrl type (1 b) + len (2 b) */
+ hdr = skb_pull_data(skb, 3);
+ if (!hdr)
goto badframe;
- if (!skb_pull(skb, get_unaligned_be16(data)))
+ /* Pull data (len bytes); length is big-endian */
+ if (!skb_pull(skb, get_unaligned_be16(&hdr[1])))
goto badframe;
break;
+ }
default:
kfree_skb(skb);
return 0;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-05-18 12:47 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-05-18 12:47 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pauli Virtanen, Tristan Madani
[-- Attachment #1: Type: text/plain, Size: 2134 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
drivers/bluetooth/btmtk.c
between commit:
634a4408c0615 ("Bluetooth: btmtk: validate WMT event SKB length before struct access")
from the origin tree and commits:
041e88fb0c086 ("Bluetooth: btmtk: validate WMT event SKB length before struct access")
162b1adeb057d ("Bluetooth: btmtk: accept too short WMT FUNC_CTRL events")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/bluetooth/btmtk.c
index f70c1b0f89903,68a32d11e5ec9..0000000000000
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@@ -719,8 -719,8 +719,8 @@@ static int btmtk_usb_hci_wmt_sync(struc
case BTMTK_WMT_FUNC_CTRL:
if (!skb_pull_data(data->evt_skb,
sizeof(wmt_evt_funcc->status))) {
- err = -EINVAL;
- goto err_free_skb;
+ status = BTMTK_WMT_ON_UNDONE;
+ break;
}
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
@@@ -1545,6 -1545,29 +1545,29 @@@ int btmtk_usb_shutdown(struct hci_dev *
return 0;
}
EXPORT_SYMBOL_GPL(btmtk_usb_shutdown);
+
+ int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ struct hci_ev_cmd_complete *ec;
+
+ if (hdr->evt == HCI_EV_CMD_COMPLETE &&
+ skb->len >= HCI_EVENT_HDR_SIZE + sizeof(*ec)) {
+ u16 opcode;
+
+ ec = (void *)(skb->data + HCI_EVENT_HDR_SIZE);
+ opcode = __le16_to_cpu(ec->opcode);
+
+ /* Filter vendor opcode */
+ if (opcode == 0xfc5d) {
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ return hci_recv_frame(hdev, skb);
+ }
+ EXPORT_SYMBOL_GPL(btmtk_recv_event);
#endif
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2026-03-12 13:14 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2026-03-12 13:14 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz
[-- Attachment #1: Type: text/plain, Size: 5920 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/l2cap_core.c
between commit:
c28d2bff70444 ("Bluetooth: L2CAP: Fix result of L2CAP_ECRED_CONN_RSP when MTU is too short")
from the origin tree and commit:
19ba9c64840d4 ("Bluetooth: L2CAP: Fix accepting multiple L2CAP_ECRED_CONN_REQ")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined net/bluetooth/l2cap_core.c
index ad98db9632fd2,475fdf1908cb8..0000000000000
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@@ -442,7 -442,7 +442,7 @@@ struct l2cap_chan *l2cap_chan_create(vo
{
struct l2cap_chan *chan;
- chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
+ chan = kzalloc_obj(*chan, GFP_ATOMIC);
if (!chan)
return NULL;
@@@ -1678,17 -1678,15 +1678,15 @@@ static void l2cap_info_timeout(struct w
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
int ret;
/* We need to check whether l2cap_conn is registered. If it is not, we
- * must not register the l2cap_user. l2cap_conn_del() is unregisters
- * l2cap_conn objects, but doesn't provide its own locking. Instead, it
- * relies on the parent hci_conn object to be locked. This itself relies
- * on the hci_dev object to be locked. So we must lock the hci device
- * here, too. */
+ * must not register the l2cap_user. l2cap_conn_del() unregisters
+ * l2cap_conn objects under conn->lock, and we use the same lock here
+ * to protect access to conn->users and conn->hchan.
+ */
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (!list_empty(&user->list)) {
ret = -EINVAL;
@@@ -1709,16 -1707,14 +1707,14 @@@
ret = 0;
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
return ret;
}
EXPORT_SYMBOL(l2cap_register_user);
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
-
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (list_empty(&user->list))
goto out_unlock;
@@@ -1727,7 -1723,7 +1723,7 @@@
user->remove(conn, user);
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
}
EXPORT_SYMBOL(l2cap_unregister_user);
@@@ -4616,7 -4612,8 +4612,8 @@@ static inline int l2cap_information_rsp
switch (type) {
case L2CAP_IT_FEAT_MASK:
- conn->feat_mask = get_unaligned_le32(rsp->data);
+ if (cmd_len >= sizeof(*rsp) + sizeof(u32))
+ conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
struct l2cap_info_req req;
@@@ -4635,7 -4632,8 +4632,8 @@@
break;
case L2CAP_IT_FIXED_CHAN:
- conn->remote_fixed_chan = rsp->data[0];
+ if (cmd_len >= sizeof(*rsp) + sizeof(rsp->data[0]))
+ conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@@ -5059,7 -5057,7 +5057,7 @@@ static inline int l2cap_ecred_conn_req(
u16 mtu, mps;
__le16 psm;
u8 result, rsp_len = 0;
- int i, num_scid;
+ int i, num_scid = 0;
bool defer = false;
if (!enable_ecred)
@@@ -5072,6 -5070,14 +5070,14 @@@
goto response;
}
+ /* Check if there are no pending channels with the same ident */
+ __l2cap_chan_list_id(conn, cmd->ident, l2cap_ecred_list_defer,
+ &num_scid);
+ if (num_scid) {
+ result = L2CAP_CR_LE_INVALID_PARAMS;
+ goto response;
+ }
+
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
@@@ -5424,7 -5430,7 +5430,7 @@@ static inline int l2cap_ecred_reconf_rs
u8 *data)
{
struct l2cap_chan *chan, *tmp;
- struct l2cap_ecred_conn_rsp *rsp = (void *) data;
+ struct l2cap_ecred_reconf_rsp *rsp = (void *)data;
u16 result;
if (cmd_len < sizeof(*rsp))
@@@ -5432,7 -5438,7 +5438,7 @@@
result = __le16_to_cpu(rsp->result);
- BT_DBG("result 0x%4.4x", rsp->result);
+ BT_DBG("result 0x%4.4x", result);
if (!result)
return 0;
@@@ -6662,8 -6668,17 +6668,17 @@@ static int l2cap_ecred_data_rcv(struct
return -ENOBUFS;
}
- if (chan->imtu < skb->len) {
- BT_ERR("Too big LE L2CAP PDU");
+ if (skb->len > chan->imtu) {
+ BT_ERR("Too big LE L2CAP PDU: len %u > %u", skb->len,
+ chan->imtu);
+ l2cap_send_disconn_req(chan, ECONNRESET);
+ return -ENOBUFS;
+ }
+
+ if (skb->len > chan->mps) {
+ BT_ERR("Too big LE L2CAP MPS: len %u > %u", skb->len,
+ chan->mps);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return -ENOBUFS;
}
@@@ -6689,7 -6704,9 +6704,9 @@@
sdu_len, skb->len, chan->imtu);
if (sdu_len > chan->imtu) {
- BT_ERR("Too big LE L2CAP SDU length received");
+ BT_ERR("Too big LE L2CAP SDU length: len %u > %u",
+ skb->len, sdu_len);
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EMSGSIZE;
goto failed;
}
@@@ -6725,6 -6742,7 +6742,7 @@@
if (chan->sdu->len + skb->len > chan->sdu_len) {
BT_ERR("Too much LE L2CAP data received");
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EINVAL;
goto failed;
}
@@@ -6947,7 -6965,7 +6965,7 @@@ static struct l2cap_conn *l2cap_conn_ad
if (!hchan)
return NULL;
- conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ conn = kzalloc_obj(*conn);
if (!conn) {
hci_chan_del(hchan);
return NULL;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2025-09-22 9:23 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2025-09-22 9:23 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pavel Shpakovskiy
[-- Attachment #1: Type: text/plain, Size: 840 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/mgmt.c
between commit:
6bbd0d3f0c23f ("Bluetooth: hci_sync: fix set_local_name race condition")
from the origin tree and commit:
3b3eb857d5ab6 ("Bluetooth: MGMT: Fix possible UAFs")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc net/bluetooth/mgmt.c
index 50634ef5c8b70,ee7068fb9fb59..0000000000000
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2025-09-17 11:34 Mark Brown
0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2025-09-17 11:34 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz, Pavel Shpakovskiy
[-- Attachment #1: Type: text/plain, Size: 860 bytes --]
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/mgmt.c
between commit:
6bbd0d3f0c23f ("Bluetooth: hci_sync: fix set_local_name race condition")
from the origin tree and commit:
c49a788e88e48 ("Bluetooth: hci_sync: fix set_local_name race condition")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc net/bluetooth/mgmt.c
index 50634ef5c8b70,b9c53810bf06b..0000000000000
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* linux-next: manual merge of the bluetooth tree with the origin tree
@ 2023-04-17 14:36 broonie
0 siblings, 0 replies; 10+ messages in thread
From: broonie @ 2023-04-17 14:36 UTC (permalink / raw)
To: Marcel Holtmann, Johan Hedberg
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Luiz Augusto von Dentz
Hi all,
Today's linux-next merge of the bluetooth tree got a conflict in:
net/bluetooth/hci_conn.c
between commit:
5dc7d23e167e2 ("Bluetooth: hci_conn: Fix possible UAF")
from the origin tree and commit:
0623067085473 ("Bluetooth: hci_conn: Fix possible UAF")
from the bluetooth tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc net/bluetooth/hci_conn.c
index 8455ba141ee61,640b951bf40a1..0000000000000
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-05 14:36 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-28 16:26 linux-next: manual merge of the bluetooth tree with the origin tree Mark Brown
-- strict thread matches above, loose matches on Subject: below --
2026-08-05 14:36 Mark Brown
2026-08-05 14:35 Mark Brown
2026-07-17 14:37 Mark Brown
2026-06-01 14:48 Mark Brown
2026-05-18 12:47 Mark Brown
2026-03-12 13:14 Mark Brown
2025-09-22 9:23 Mark Brown
2025-09-17 11:34 Mark Brown
2023-04-17 14:36 broonie
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox