[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.