From 0bd606b31d40dceb718bf22e3ce7b4cff7e34bf6 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:41 -0700 Subject: [PATCH] Bluetooth: hci_event: Introduce handle_ev_vendor() for HCI_EV_VENDOR Introduce the hook to solve issues below: msft_vendor_evt(), the current handler for all VSEs, is unsuitable since: - many VSEs are not MSFT ones; - it always corrupts the non-MSFT VSEs by calling skb_pull_data() once the MSFT extension is enabled. Several issues are caused by many transport drivers pre-processing VSEs in their RX path, often an IRQ-disabled atomic context. Take the two typical cases below as examples: Case 1: // no btmon log, no way to reach userspace Step 1: handle and free @original_skb directly Case 2: // hurts performance and consumes GFP_ATOMIC memory Step 1: cloned_skb = skb_clone(original_skb, GFP_ATOMIC); // the VSE is handled here Step 2: handle and free @cloned_skb Step 3: hci_recv_frame(hdev, original_skb); // already handled, but re-enters the stack's event-handling path Step 4: hci_event_packet(hdev, original_skb); Fix by introducing the hook with usage: 1) the transport driver registers the hook for VSEs of interest; 2) the stack calls it in process context, handling the VSE like any other event: - if interested, handle the VSE - no need to free it - and return true; - otherwise return false. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_event.c | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6ff47f9bf758..e07418a5adce 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -646,6 +646,8 @@ struct hci_dev { 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); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6890d60ade93..d8e9125ae74a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7604,6 +7604,14 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data, subev->func(hdev, data, skb); } +static void hci_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) +{ + if (hdev->handle_ev_vendor && hdev->handle_ev_vendor(hdev, skb)) + return; + + msft_vendor_evt(hdev, data, skb); +} + static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 event, struct sk_buff *skb) { @@ -7831,7 +7839,7 @@ static const struct hci_ev { HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt, sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE), /* [0xff = HCI_EV_VENDOR] */ - HCI_EV_VL(HCI_EV_VENDOR, msft_vendor_evt, 0, HCI_MAX_EVENT_SIZE), + HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_SIZE), }; static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb,