[PATCH RFC v2 3/3] Bluetooth: Add hdev->recv_bt_vendor() to handle BT vendor frames
Zijun Hu <[email protected]>
| Newsgroups | org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
For unsolicited VSE and ACL frames with vendor reserved handles: Many transport drivers pre-process them in the RX-path because the BT core cannot recognize them, and cause: - Impact performance since skb_clone() in IRQ-disabled context - Difficult to debug as frames consumed without btmon logging Fix by adding the hook to process them within stack's process context, when they have been logged by btmon. Signed-off-by: Zijun Hu <[email protected]> --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/hci_core.c | 33 +++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 428261591288..12f3720f6cc8 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); + /* Return true if @skb was consumed, false otherwise */ + bool (*recv_bt_vendor)(struct hci_dev *hdev, struct sk_buff *skb); void (*recv_vendor_pkt)(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); @@ -2329,6 +2331,7 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); +bool hci_recv_bt_vendor(struct hci_dev *hdev, struct sk_buff *skb); int hci_send_vendor_frame(struct hci_dev *hdev, struct iov_iter *iter); int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 90807562e8ed..21ef748e3528 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3830,6 +3830,12 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) __u16 handle, flags; int err; + /* Header is valid when ACL frame arrives here */ + if (hci_recv_bt_vendor(hdev, skb)) { + hdev->stat.acl_rx++; + return; + } + hdr = skb_pull_data(skb, sizeof(*hdr)); if (!hdr) { bt_dev_err(hdev, "ACL packet too small"); @@ -3918,6 +3924,33 @@ static void hci_isodata_packet(struct hci_dev *hdev, struct sk_buff *skb) handle, err); } +bool hci_recv_bt_vendor(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_event_hdr *evt_hdr; + u8 evt, pkt_type; + + if (!hdev->recv_bt_vendor) + return false; + + pkt_type = hci_skb_pkt_type(skb); + + /* Save the event code before calling into the driver, since @skb + * may be freed once consumed. + */ + if (pkt_type == HCI_EVENT_PKT) { + evt_hdr = hci_event_hdr(skb); + evt = evt_hdr->evt; + } + + if (!hdev->recv_bt_vendor(hdev, skb)) + return false; + + if (pkt_type == HCI_EVENT_PKT && evt != HCI_EV_VENDOR) + bt_dev_warn(hdev, "consumed unexpected BT event 0x%2.2x", evt); + + return true; +} + static bool hci_req_is_complete(struct hci_dev *hdev) { struct sk_buff *skb; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea858391c789..94c02b66bdfa 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7801,6 +7801,11 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) goto done; } + if (hci_recv_bt_vendor(hdev, skb)) { + hdev->stat.evt_rx++; + return; + } + hci_dev_lock(hdev); kfree_skb(hdev->recv_event); hdev->recv_event = skb_clone(skb, GFP_KERNEL); -- 2.34.1