On Sun, 02 Jan 2022 14:23:18 -0800
Given the default -EINVAL there, this can not hurt in practice.
And to avoid queuing the rx work for every skb received, open code
skb_dequeue() on worker side and skb_queue_tail() on work scheduler side,
and serialize activities on both sides using the lock embedded in rx_q.
Only for thoughts now.
Hillf
+++ y/net/bluetooth/hci_core.c
@@ -4069,6 +4069,23 @@ int hci_reset_dev(struct hci_dev *hdev)
}
EXPORT_SYMBOL(hci_reset_dev);
+static void hci_queue_skb_and_work(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct sk_buff_head *list = &hdev->rx_q;
+ bool running = false;
+ unsigned long flags;
+
+ /* open code skb_queue_tail() to avoid queuing work for every skb */
+ spin_lock_irqsave(&list->lock, flags);
+ __skb_queue_tail(list, skb);
+ if (hdev->rx_worker_running)
+ running = true;
+ spin_unlock_irqrestore(&list->lock, flags);
+
+ if (running == false)
+ queue_work(hdev->workqueue, &hdev->rx_work);
+}
+
/* Receive frame from HCI drivers */
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -4092,8 +4109,7 @@ int hci_recv_frame(struct hci_dev *hdev,
/* Time stamp */
__net_timestamp(skb);
- skb_queue_tail(&hdev->rx_q, skb);
- queue_work(hdev->workqueue, &hdev->rx_work);
+ hci_queue_skb_and_work(hdev, skb);
return 0;
}
@@ -4108,8 +4124,7 @@ int hci_recv_diag(struct hci_dev *hdev,
/* Time stamp */
__net_timestamp(skb);
- skb_queue_tail(&hdev->rx_q, skb);
- queue_work(hdev->workqueue, &hdev->rx_work);
+ hci_queue_skb_and_work(hdev, skb);
return 0;
}
@@ -5041,11 +5056,18 @@ void hci_req_cmd_complete(struct hci_dev
static void hci_rx_work(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work);
+ struct sk_buff_head *list = &hdev->rx_q;
+ unsigned long flags;
struct sk_buff *skb;
BT_DBG("%s", hdev->name);
- while ((skb = skb_dequeue(&hdev->rx_q))) {
+ spin_lock_irqsave(&list->lock, flags);
+ hdev->rx_worker_running = 1;
+ /* open code skb_dequeue() to avoid queuing work for every skb */
+ while ((skb = __skb_dequeue(list))) {
+ spin_unlock_irqrestore(&list->lock, flags);
+
/* Send copy to monitor */
hci_send_to_monitor(hdev, skb);
@@ -5063,6 +5085,7 @@ static void hci_rx_work(struct work_stru
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
!test_bit(HCI_INIT, &hdev->flags)) {
kfree_skb(skb);
+ spin_lock_irqsave(&list->lock, flags);
continue;
}
@@ -5073,6 +5096,7 @@ static void hci_rx_work(struct work_stru
case HCI_SCODATA_PKT:
case HCI_ISODATA_PKT:
kfree_skb(skb);
+ spin_lock_irqsave(&list->lock, flags);
continue;
}
}
@@ -5098,7 +5122,11 @@ static void hci_rx_work(struct work_stru
kfree_skb(skb);
break;
}
+ spin_lock_irqsave(&list->lock, flags);
}
+
+ hdev->rx_worker_running = 0;
+ spin_unlock_irqrestore(&list->lock, flags);
}
static void hci_cmd_work(struct work_struct *work)
+++ y/include/net/bluetooth/hci_core.h
@@ -456,6 +456,7 @@ struct hci_dev {
__u8 le_tx_def_phys;
__u8 le_rx_def_phys;
+ __u8 rx_worker_running;
struct workqueue_struct *workqueue;
struct workqueue_struct *req_workqueue;