linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval
@ 2021-01-26 18:50 Luiz Augusto von Dentz
  2021-01-27  0:14 ` [v3] " bluez.test.bot
  2021-02-04 13:06 ` [PATCH v3] " Marcel Holtmann
  0 siblings, 2 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2021-01-26 18:50 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This makes btusb to queue ACL and events during a polling interval
by using of a delayed work, with the interval working as a time window
where frames received from different endpoints are considered to be
arrived at same time and then attempt to resolve potential conflics by
processing the events ahead of ACL packets.

It worth noting though that priorizing events over ACL data may result
in inverting the order compared to how they appeared over the air, for
instance there may be packets received before a disconnect event that
will be discarded and unencrypted packets received before encryption
change which would considered encrypted, because of these potential
changes on the order the support for queuing during the polling
interval is not enabled by default so platforms have the following
means to enable it:

At build-time:

    CONFIG_BT_HCIBTUSB_INTERVAL=y

At runtime with use of module option:

    enable_interval

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---

v2: Calculate the delayed_work interval based on the intr urb->interval which
is derived from endpoint bInterval.
v3: Drop use of a queue for events.

 drivers/bluetooth/Kconfig |  7 +++
 drivers/bluetooth/btusb.c | 98 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 94 insertions(+), 11 deletions(-)

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e73a531b377..2f20a853d946 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -41,6 +41,13 @@ config BT_HCIBTUSB_AUTOSUSPEND
 	  This can be overridden by passing btusb.enable_autosuspend=[y|n]
 	  on the kernel commandline.
 
+config BT_HCIBTUSB_INTERVAL
+	bool "Enable notification of USB polling interval"
+	depends on BT_HCIBTUSB
+	help
+	  Say Y here to enable notification of USB polling interval for
+	  Bluetooth USB devices by default.
+
 config BT_HCIBTUSB_BCM
 	bool "Broadcom protocol support"
 	depends on BT_HCIBTUSB
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index b14102fba601..4266c057746e 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -30,7 +30,7 @@
 static bool disable_scofix;
 static bool force_scofix;
 static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
-
+static bool enable_interval = IS_ENABLED(CONFIG_BT_HCIBTUSB_INTERVAL);
 static bool reset = true;
 
 static struct usb_driver btusb_driver;
@@ -519,8 +519,12 @@ struct btusb_data {
 
 	unsigned long flags;
 
-	struct work_struct work;
-	struct work_struct waker;
+	int intr_interval;
+	struct work_struct  work;
+	struct work_struct  waker;
+	struct delayed_work rx_work;
+
+	struct sk_buff_head acl_q;
 
 	struct usb_anchor deferred;
 	struct usb_anchor tx_anchor;
@@ -557,7 +561,7 @@ struct btusb_data {
 	int isoc_altsetting;
 	int suspend_count;
 
-	int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
+	int (*recv_event)(struct btusb_data *data, struct sk_buff *skb);
 	int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
 
 	int (*setup_on_usb)(struct hci_dev *hdev);
@@ -707,7 +711,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
 
 		if (!hci_skb_expect(skb)) {
 			/* Complete frame */
-			data->recv_event(data->hdev, skb);
+			data->recv_event(data, skb);
 			skb = NULL;
 		}
 	}
@@ -718,6 +722,24 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
 	return err;
 }
 
+static int btusb_rx_queue(struct btusb_data *data, struct sk_buff *skb,
+			  struct sk_buff_head *queue, unsigned int interval)
+{
+	skb_queue_tail(queue, skb);
+
+	schedule_delayed_work(&data->rx_work, interval);
+
+	return 0;
+}
+
+static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
+{
+	if (!enable_interval)
+		return hci_recv_frame(data->hdev, skb);
+
+	return btusb_rx_queue(data, skb, &data->acl_q, data->intr_interval);
+}
+
 static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
 {
 	struct sk_buff *skb;
@@ -765,7 +787,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
 
 		if (!hci_skb_expect(skb)) {
 			/* Complete frame */
-			hci_recv_frame(data->hdev, skb);
+			btusb_recv_acl(data, skb);
 			skb = NULL;
 		}
 	}
@@ -917,6 +939,19 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 		usb_unanchor_urb(urb);
 	}
 
+	/* The units are frames (milliseconds) for full and low speed devices,
+	 * and microframes (1/8 millisecond) for highspeed and SuperSpeed
+	 * devices.
+	 */
+	switch (urb->dev->speed) {
+	case USB_SPEED_SUPER_PLUS:
+	case USB_SPEED_SUPER:	/* units are 125us */
+		data->intr_interval = usecs_to_jiffies(urb->interval * 125);
+		break;
+	default:
+		data->intr_interval = msecs_to_jiffies(urb->interval);
+	}
+
 	usb_free_urb(urb);
 
 	return err;
@@ -1383,9 +1418,12 @@ static int btusb_close(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
+	cancel_delayed_work(&data->rx_work);
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->waker);
 
+	skb_queue_purge(&data->acl_q);
+
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
@@ -1417,6 +1455,10 @@ static int btusb_flush(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
+	cancel_delayed_work(&data->rx_work);
+
+	skb_queue_purge(&data->acl_q);
+
 	usb_kill_anchored_urbs(&data->tx_anchor);
 	btusb_free_frags(data);
 
@@ -1769,6 +1811,24 @@ static void btusb_waker(struct work_struct *work)
 	usb_autopm_put_interface(data->intf);
 }
 
+static void btusb_rx_dequeue(struct btusb_data *data,
+			     struct sk_buff_head *queue)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(queue)))
+		hci_recv_frame(data->hdev, skb);
+}
+
+static void btusb_rx_work(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data,
+					       rx_work.work);
+
+	/* Dequeue ACL data received during the interval */
+	btusb_rx_dequeue(data, &data->acl_q);
+}
+
 static int btusb_setup_bcm92035(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -2304,10 +2364,8 @@ static void btusb_intel_secure_send_result(struct btusb_data *data,
 		wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
 }
 
-static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
+static int btusb_recv_event_intel(struct btusb_data *data, struct sk_buff *skb)
 {
-	struct btusb_data *data = hci_get_drvdata(hdev);
-
 	if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
 		struct hci_event_hdr *hdr = (void *)skb->data;
 
@@ -2336,7 +2394,7 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
 		}
 	}
 
-	return hci_recv_frame(hdev, skb);
+	return hci_recv_frame(data->hdev, skb);
 }
 
 static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
@@ -4279,6 +4337,17 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
 	return 0;
 }
 
+static int btusb_recv_evt(struct btusb_data *data, struct sk_buff *skb)
+{
+	if (!enable_interval)
+		return hci_recv_frame(data->hdev, skb);
+
+	/* Trigger dequeue immediatelly if an event is received */
+	schedule_delayed_work(&data->rx_work, 0);
+
+	return hci_recv_frame(data->hdev, skb);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
 {
@@ -4362,6 +4431,10 @@ static int btusb_probe(struct usb_interface *intf,
 
 	INIT_WORK(&data->work, btusb_work);
 	INIT_WORK(&data->waker, btusb_waker);
+	INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
+
+	skb_queue_head_init(&data->acl_q);
+
 	init_usb_anchor(&data->deferred);
 	init_usb_anchor(&data->tx_anchor);
 	spin_lock_init(&data->txlock);
@@ -4378,7 +4451,7 @@ static int btusb_probe(struct usb_interface *intf,
 		data->recv_bulk = btusb_recv_bulk_intel;
 		set_bit(BTUSB_BOOTLOADER, &data->flags);
 	} else {
-		data->recv_event = hci_recv_frame;
+		data->recv_event = btusb_recv_evt;
 		data->recv_bulk = btusb_recv_bulk;
 	}
 
@@ -4867,6 +4940,9 @@ MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
 module_param(enable_autosuspend, bool, 0644);
 MODULE_PARM_DESC(enable_autosuspend, "Enable USB autosuspend by default");
 
+module_param(enable_interval, bool, 0644);
+MODULE_PARM_DESC(enable_interval, "Enable USB polling interval by default");
+
 module_param(reset, bool, 0644);
 MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
 
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: [v3] Bluetooth: btusb: Add support for queuing during polling interval
  2021-01-26 18:50 [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval Luiz Augusto von Dentz
@ 2021-01-27  0:14 ` bluez.test.bot
  2021-02-04 13:06 ` [PATCH v3] " Marcel Holtmann
  1 sibling, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2021-01-27  0:14 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz

[-- Attachment #1: Type: text/plain, Size: 2032 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=422381

---Test result---

##############################
    Test: CheckPatch - PASS
    

    ##############################
    Test: CheckGitLint - FAIL
    workflow: Add workflow files for ci
1: T1 Title exceeds max length (92>72): "Merge dffab7c7a45b66316b22875315944235dc9e4d4b into 2508d6a5f98404e4ee3c8d74aa7fb0edff630969"
3: B6 Body message is missing

Bluetooth: btusb: Add support for queuing during polling interval
1: T1 Title exceeds max length (92>72): "Merge dffab7c7a45b66316b22875315944235dc9e4d4b into 2508d6a5f98404e4ee3c8d74aa7fb0edff630969"
3: B6 Body message is missing


    ##############################
    Test: CheckBuildK - PASS
    

    ##############################
    Test: CheckTestRunner: Setup - PASS
    

    ##############################
    Test: CheckTestRunner: l2cap-tester - PASS
    Total: 40, Passed: 34 (85.0%), Failed: 0, Not Run: 6

    ##############################
    Test: CheckTestRunner: bnep-tester - PASS
    Total: 1, Passed: 1 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: mgmt-tester - PASS
    Total: 416, Passed: 402 (96.6%), Failed: 0, Not Run: 14

    ##############################
    Test: CheckTestRunner: rfcomm-tester - PASS
    Total: 9, Passed: 9 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: sco-tester - PASS
    Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: smp-tester - PASS
    Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: userchan-tester - PASS
    Total: 3, Passed: 3 (100.0%), Failed: 0, Not Run: 0

    

---
Regards,
Linux Bluetooth


[-- Attachment #2: l2cap-tester.log --]
[-- Type: application/octet-stream, Size: 43340 bytes --]

[-- Attachment #3: bnep-tester.log --]
[-- Type: application/octet-stream, Size: 3531 bytes --]

[-- Attachment #4: mgmt-tester.log --]
[-- Type: application/octet-stream, Size: 546678 bytes --]

[-- Attachment #5: rfcomm-tester.log --]
[-- Type: application/octet-stream, Size: 11651 bytes --]

[-- Attachment #6: sco-tester.log --]
[-- Type: application/octet-stream, Size: 9886 bytes --]

[-- Attachment #7: smp-tester.log --]
[-- Type: application/octet-stream, Size: 11797 bytes --]

[-- Attachment #8: userchan-tester.log --]
[-- Type: application/octet-stream, Size: 5426 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval
  2021-01-26 18:50 [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval Luiz Augusto von Dentz
  2021-01-27  0:14 ` [v3] " bluez.test.bot
@ 2021-02-04 13:06 ` Marcel Holtmann
  2021-02-08 21:13   ` Luiz Augusto von Dentz
  1 sibling, 1 reply; 6+ messages in thread
From: Marcel Holtmann @ 2021-02-04 13:06 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

> This makes btusb to queue ACL and events during a polling interval
> by using of a delayed work, with the interval working as a time window
> where frames received from different endpoints are considered to be
> arrived at same time and then attempt to resolve potential conflics by
> processing the events ahead of ACL packets.
> 
> It worth noting though that priorizing events over ACL data may result
> in inverting the order compared to how they appeared over the air, for
> instance there may be packets received before a disconnect event that
> will be discarded and unencrypted packets received before encryption
> change which would considered encrypted, because of these potential
> changes on the order the support for queuing during the polling
> interval is not enabled by default so platforms have the following
> means to enable it:
> 
> At build-time:
> 
>    CONFIG_BT_HCIBTUSB_INTERVAL=y
> 
> At runtime with use of module option:
> 
>    enable_interval
> 
> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> ---
> 
> v2: Calculate the delayed_work interval based on the intr urb->interval which
> is derived from endpoint bInterval.
> v3: Drop use of a queue for events.

we should get this tested and if this actually helps reducing the issue.

If this does work in general, we can adopt a version of this upstream.

> drivers/bluetooth/Kconfig |  7 +++
> drivers/bluetooth/btusb.c | 98 ++++++++++++++++++++++++++++++++++-----
> 2 files changed, 94 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index 4e73a531b377..2f20a853d946 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -41,6 +41,13 @@ config BT_HCIBTUSB_AUTOSUSPEND
> 	  This can be overridden by passing btusb.enable_autosuspend=[y|n]
> 	  on the kernel commandline.
> 
> +config BT_HCIBTUSB_INTERVAL
> +	bool "Enable notification of USB polling interval"
> +	depends on BT_HCIBTUSB
> +	help
> +	  Say Y here to enable notification of USB polling interval for
> +	  Bluetooth USB devices by default.
> +

So, I am not really convinced it is a good idea to have this as a Kconfig option. I think this always needs to be some runtime thing you enable with a config file. Module option are really just a lame way of hacking things and we should most likely start to get rid of them.

Anyhow, my main concern here is that no matter how good your comment is here, people will false enable it and wonder why things might not work as expected anymore when they try to qualify things and packets get wrongly labeled as encrypted but in reality they are not.

So I wonder if we can do mgmt experimental features on a per driver basis. If so, we might better provide this feature via an obscure UUID that is documented. And I wanted to expose more details about the attached hardware anyway. That way the platform or bluetoothd can then decide on when to enable this or not. There might be hardware that does a way better job anyhow than other and doesn’t need this.

> config BT_HCIBTUSB_BCM
> 	bool "Broadcom protocol support"
> 	depends on BT_HCIBTUSB
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index b14102fba601..4266c057746e 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -30,7 +30,7 @@
> static bool disable_scofix;
> static bool force_scofix;
> static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
> -
> +static bool enable_interval = IS_ENABLED(CONFIG_BT_HCIBTUSB_INTERVAL);
> static bool reset = true;
> 
> static struct usb_driver btusb_driver;
> @@ -519,8 +519,12 @@ struct btusb_data {
> 
> 	unsigned long flags;
> 
> -	struct work_struct work;
> -	struct work_struct waker;
> +	int intr_interval;
> +	struct work_struct  work;
> +	struct work_struct  waker;
> +	struct delayed_work rx_work;
> +
> +	struct sk_buff_head acl_q;
> 
> 	struct usb_anchor deferred;
> 	struct usb_anchor tx_anchor;
> @@ -557,7 +561,7 @@ struct btusb_data {
> 	int isoc_altsetting;
> 	int suspend_count;
> 
> -	int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
> +	int (*recv_event)(struct btusb_data *data, struct sk_buff *skb);
> 	int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
> 
> 	int (*setup_on_usb)(struct hci_dev *hdev);
> @@ -707,7 +711,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> 
> 		if (!hci_skb_expect(skb)) {
> 			/* Complete frame */
> -			data->recv_event(data->hdev, skb);
> +			data->recv_event(data, skb);
> 			skb = NULL;
> 		}
> 	}
> @@ -718,6 +722,24 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> 	return err;
> }
> 
> +static int btusb_rx_queue(struct btusb_data *data, struct sk_buff *skb,
> +			  struct sk_buff_head *queue, unsigned int interval)
> +{
> +	skb_queue_tail(queue, skb);
> +
> +	schedule_delayed_work(&data->rx_work, interval);
> +
> +	return 0;
> +}
> +
> +static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
> +{
> +	if (!enable_interval)
> +		return hci_recv_frame(data->hdev, skb);
> +
> +	return btusb_rx_queue(data, skb, &data->acl_q, data->intr_interval);
> +}
> +

Combine these two into one function.

> static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> {
> 	struct sk_buff *skb;
> @@ -765,7 +787,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> 
> 		if (!hci_skb_expect(skb)) {
> 			/* Complete frame */
> -			hci_recv_frame(data->hdev, skb);
> +			btusb_recv_acl(data, skb);
> 			skb = NULL;
> 		}
> 	}
> @@ -917,6 +939,19 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
> 		usb_unanchor_urb(urb);
> 	}
> 
> +	/* The units are frames (milliseconds) for full and low speed devices,
> +	 * and microframes (1/8 millisecond) for highspeed and SuperSpeed
> +	 * devices.
> +	 */
> +	switch (urb->dev->speed) {
> +	case USB_SPEED_SUPER_PLUS:
> +	case USB_SPEED_SUPER:	/* units are 125us */
> +		data->intr_interval = usecs_to_jiffies(urb->interval * 125);
> +		break;
> +	default:
> +		data->intr_interval = msecs_to_jiffies(urb->interval);
> +	}
> +

Do we have to do this on every interrupt URB submission? Or can we just get this once at probe()?

> 	usb_free_urb(urb);
> 
> 	return err;
> @@ -1383,9 +1418,12 @@ static int btusb_close(struct hci_dev *hdev)
> 
> 	BT_DBG("%s", hdev->name);
> 
> +	cancel_delayed_work(&data->rx_work);
> 	cancel_work_sync(&data->work);
> 	cancel_work_sync(&data->waker);
> 
> +	skb_queue_purge(&data->acl_q);
> +
> 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> @@ -1417,6 +1455,10 @@ static int btusb_flush(struct hci_dev *hdev)
> 
> 	BT_DBG("%s", hdev->name);
> 
> +	cancel_delayed_work(&data->rx_work);
> +
> +	skb_queue_purge(&data->acl_q);
> +
> 	usb_kill_anchored_urbs(&data->tx_anchor);
> 	btusb_free_frags(data);
> 
> @@ -1769,6 +1811,24 @@ static void btusb_waker(struct work_struct *work)
> 	usb_autopm_put_interface(data->intf);
> }
> 
> +static void btusb_rx_dequeue(struct btusb_data *data,
> +			     struct sk_buff_head *queue)
> +{
> +	struct sk_buff *skb;
> +
> +	while ((skb = skb_dequeue(queue)))
> +		hci_recv_frame(data->hdev, skb);
> +}
> +
> +static void btusb_rx_work(struct work_struct *work)
> +{
> +	struct btusb_data *data = container_of(work, struct btusb_data,
> +					       rx_work.work);
> +
> +	/* Dequeue ACL data received during the interval */
> +	btusb_rx_dequeue(data, &data->acl_q);
> +}
> +

Combine these two into one function.

> static int btusb_setup_bcm92035(struct hci_dev *hdev)
> {
> 	struct sk_buff *skb;
> @@ -2304,10 +2364,8 @@ static void btusb_intel_secure_send_result(struct btusb_data *data,
> 		wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
> }
> 
> -static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
> +static int btusb_recv_event_intel(struct btusb_data *data, struct sk_buff *skb)
> {
> -	struct btusb_data *data = hci_get_drvdata(hdev);
> -
> 	if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
> 		struct hci_event_hdr *hdr = (void *)skb->data;
> 
> @@ -2336,7 +2394,7 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
> 		}
> 	}
> 
> -	return hci_recv_frame(hdev, skb);
> +	return hci_recv_frame(data->hdev, skb);
> }
> 
> static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
> @@ -4279,6 +4337,17 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
> 	return 0;
> }
> 
> +static int btusb_recv_evt(struct btusb_data *data, struct sk_buff *skb)
> +{
> +	if (!enable_interval)
> +		return hci_recv_frame(data->hdev, skb);
> +
> +	/* Trigger dequeue immediatelly if an event is received */
> +	schedule_delayed_work(&data->rx_work, 0);
> +
> +	return hci_recv_frame(data->hdev, skb);
> +}
> +
> static int btusb_probe(struct usb_interface *intf,
> 		       const struct usb_device_id *id)
> {
> @@ -4362,6 +4431,10 @@ static int btusb_probe(struct usb_interface *intf,
> 
> 	INIT_WORK(&data->work, btusb_work);
> 	INIT_WORK(&data->waker, btusb_waker);
> +	INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
> +
> +	skb_queue_head_init(&data->acl_q);
> +
> 	init_usb_anchor(&data->deferred);
> 	init_usb_anchor(&data->tx_anchor);
> 	spin_lock_init(&data->txlock);
> @@ -4378,7 +4451,7 @@ static int btusb_probe(struct usb_interface *intf,
> 		data->recv_bulk = btusb_recv_bulk_intel;
> 		set_bit(BTUSB_BOOTLOADER, &data->flags);
> 	} else {
> -		data->recv_event = hci_recv_frame;
> +		data->recv_event = btusb_recv_evt;
> 		data->recv_bulk = btusb_recv_bulk;
> 	}
> 
> @@ -4867,6 +4940,9 @@ MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
> module_param(enable_autosuspend, bool, 0644);
> MODULE_PARM_DESC(enable_autosuspend, "Enable USB autosuspend by default");
> 
> +module_param(enable_interval, bool, 0644);
> +MODULE_PARM_DESC(enable_interval, "Enable USB polling interval by default");
> +

For a testing patch, leave this option in (just scrap the Kconfig part) and name “Enable URB polling interval synchronization”. I would also name the option “enable_poll_sync”.

> module_param(reset, bool, 0644);
> MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");

Regards

Marcel


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval
  2021-02-04 13:06 ` [PATCH v3] " Marcel Holtmann
@ 2021-02-08 21:13   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2021-02-08 21:13 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth

Hi Marcel,

On Thu, Feb 4, 2021 at 5:06 AM Marcel Holtmann <marcel@holtmann.org> wrote:
>
> Hi Luiz,
>
> > This makes btusb to queue ACL and events during a polling interval
> > by using of a delayed work, with the interval working as a time window
> > where frames received from different endpoints are considered to be
> > arrived at same time and then attempt to resolve potential conflics by
> > processing the events ahead of ACL packets.
> >
> > It worth noting though that priorizing events over ACL data may result
> > in inverting the order compared to how they appeared over the air, for
> > instance there may be packets received before a disconnect event that
> > will be discarded and unencrypted packets received before encryption
> > change which would considered encrypted, because of these potential
> > changes on the order the support for queuing during the polling
> > interval is not enabled by default so platforms have the following
> > means to enable it:
> >
> > At build-time:
> >
> >    CONFIG_BT_HCIBTUSB_INTERVAL=y
> >
> > At runtime with use of module option:
> >
> >    enable_interval
> >
> > Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > ---
> >
> > v2: Calculate the delayed_work interval based on the intr urb->interval which
> > is derived from endpoint bInterval.
> > v3: Drop use of a queue for events.
>
> we should get this tested and if this actually helps reducing the issue.
>
> If this does work in general, we can adopt a version of this upstream.
>
> > drivers/bluetooth/Kconfig |  7 +++
> > drivers/bluetooth/btusb.c | 98 ++++++++++++++++++++++++++++++++++-----
> > 2 files changed, 94 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> > index 4e73a531b377..2f20a853d946 100644
> > --- a/drivers/bluetooth/Kconfig
> > +++ b/drivers/bluetooth/Kconfig
> > @@ -41,6 +41,13 @@ config BT_HCIBTUSB_AUTOSUSPEND
> >         This can be overridden by passing btusb.enable_autosuspend=[y|n]
> >         on the kernel commandline.
> >
> > +config BT_HCIBTUSB_INTERVAL
> > +     bool "Enable notification of USB polling interval"
> > +     depends on BT_HCIBTUSB
> > +     help
> > +       Say Y here to enable notification of USB polling interval for
> > +       Bluetooth USB devices by default.
> > +
>
> So, I am not really convinced it is a good idea to have this as a Kconfig option. I think this always needs to be some runtime thing you enable with a config file. Module option are really just a lame way of hacking things and we should most likely start to get rid of them.
>
> Anyhow, my main concern here is that no matter how good your comment is here, people will false enable it and wonder why things might not work as expected anymore when they try to qualify things and packets get wrongly labeled as encrypted but in reality they are not.
>
> So I wonder if we can do mgmt experimental features on a per driver basis. If so, we might better provide this feature via an obscure UUID that is documented. And I wanted to expose more details about the attached hardware anyway. That way the platform or bluetoothd can then decide on when to enable this or not. There might be hardware that does a way better job anyhow than other and doesn’t need this.

Ok, how about we first introduce as a module parameter and then once
we have the hardware information shared over the MGMT interface we can
use the experimental features to enable/disable it.

> > config BT_HCIBTUSB_BCM
> >       bool "Broadcom protocol support"
> >       depends on BT_HCIBTUSB
> > diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> > index b14102fba601..4266c057746e 100644
> > --- a/drivers/bluetooth/btusb.c
> > +++ b/drivers/bluetooth/btusb.c
> > @@ -30,7 +30,7 @@
> > static bool disable_scofix;
> > static bool force_scofix;
> > static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
> > -
> > +static bool enable_interval = IS_ENABLED(CONFIG_BT_HCIBTUSB_INTERVAL);
> > static bool reset = true;
> >
> > static struct usb_driver btusb_driver;
> > @@ -519,8 +519,12 @@ struct btusb_data {
> >
> >       unsigned long flags;
> >
> > -     struct work_struct work;
> > -     struct work_struct waker;
> > +     int intr_interval;
> > +     struct work_struct  work;
> > +     struct work_struct  waker;
> > +     struct delayed_work rx_work;
> > +
> > +     struct sk_buff_head acl_q;
> >
> >       struct usb_anchor deferred;
> >       struct usb_anchor tx_anchor;
> > @@ -557,7 +561,7 @@ struct btusb_data {
> >       int isoc_altsetting;
> >       int suspend_count;
> >
> > -     int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
> > +     int (*recv_event)(struct btusb_data *data, struct sk_buff *skb);
> >       int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
> >
> >       int (*setup_on_usb)(struct hci_dev *hdev);
> > @@ -707,7 +711,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> >
> >               if (!hci_skb_expect(skb)) {
> >                       /* Complete frame */
> > -                     data->recv_event(data->hdev, skb);
> > +                     data->recv_event(data, skb);
> >                       skb = NULL;
> >               }
> >       }
> > @@ -718,6 +722,24 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> >       return err;
> > }
> >
> > +static int btusb_rx_queue(struct btusb_data *data, struct sk_buff *skb,
> > +                       struct sk_buff_head *queue, unsigned int interval)
> > +{
> > +     skb_queue_tail(queue, skb);
> > +
> > +     schedule_delayed_work(&data->rx_work, interval);
> > +
> > +     return 0;
> > +}
> > +
> > +static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
> > +{
> > +     if (!enable_interval)
> > +             return hci_recv_frame(data->hdev, skb);
> > +
> > +     return btusb_rx_queue(data, skb, &data->acl_q, data->intr_interval);
> > +}
> > +
>
> Combine these two into one function.
>
> > static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> > {
> >       struct sk_buff *skb;
> > @@ -765,7 +787,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> >
> >               if (!hci_skb_expect(skb)) {
> >                       /* Complete frame */
> > -                     hci_recv_frame(data->hdev, skb);
> > +                     btusb_recv_acl(data, skb);
> >                       skb = NULL;
> >               }
> >       }
> > @@ -917,6 +939,19 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
> >               usb_unanchor_urb(urb);
> >       }
> >
> > +     /* The units are frames (milliseconds) for full and low speed devices,
> > +      * and microframes (1/8 millisecond) for highspeed and SuperSpeed
> > +      * devices.
> > +      */
> > +     switch (urb->dev->speed) {
> > +     case USB_SPEED_SUPER_PLUS:
> > +     case USB_SPEED_SUPER:   /* units are 125us */
> > +             data->intr_interval = usecs_to_jiffies(urb->interval * 125);
> > +             break;
> > +     default:
> > +             data->intr_interval = msecs_to_jiffies(urb->interval);
> > +     }
> > +
>
> Do we have to do this on every interrupt URB submission? Or can we just get this once at probe()?

Afaik btusb_submit_intr_urb is only called in btusb_open and
btusb_resume, note there are some changes to urb->dev->speed when
usb_submit_urb is called urb->interval maybe adjusted thus why I had
captured the interval after submission.

> >       usb_free_urb(urb);
> >
> >       return err;
> > @@ -1383,9 +1418,12 @@ static int btusb_close(struct hci_dev *hdev)
> >
> >       BT_DBG("%s", hdev->name);
> >
> > +     cancel_delayed_work(&data->rx_work);
> >       cancel_work_sync(&data->work);
> >       cancel_work_sync(&data->waker);
> >
> > +     skb_queue_purge(&data->acl_q);
> > +
> >       clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> >       clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> >       clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> > @@ -1417,6 +1455,10 @@ static int btusb_flush(struct hci_dev *hdev)
> >
> >       BT_DBG("%s", hdev->name);
> >
> > +     cancel_delayed_work(&data->rx_work);
> > +
> > +     skb_queue_purge(&data->acl_q);
> > +
> >       usb_kill_anchored_urbs(&data->tx_anchor);
> >       btusb_free_frags(data);
> >
> > @@ -1769,6 +1811,24 @@ static void btusb_waker(struct work_struct *work)
> >       usb_autopm_put_interface(data->intf);
> > }
> >
> > +static void btusb_rx_dequeue(struct btusb_data *data,
> > +                          struct sk_buff_head *queue)
> > +{
> > +     struct sk_buff *skb;
> > +
> > +     while ((skb = skb_dequeue(queue)))
> > +             hci_recv_frame(data->hdev, skb);
> > +}
> > +
> > +static void btusb_rx_work(struct work_struct *work)
> > +{
> > +     struct btusb_data *data = container_of(work, struct btusb_data,
> > +                                            rx_work.work);
> > +
> > +     /* Dequeue ACL data received during the interval */
> > +     btusb_rx_dequeue(data, &data->acl_q);
> > +}
> > +
>
> Combine these two into one function.
>
> > static int btusb_setup_bcm92035(struct hci_dev *hdev)
> > {
> >       struct sk_buff *skb;
> > @@ -2304,10 +2364,8 @@ static void btusb_intel_secure_send_result(struct btusb_data *data,
> >               wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
> > }
> >
> > -static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
> > +static int btusb_recv_event_intel(struct btusb_data *data, struct sk_buff *skb)
> > {
> > -     struct btusb_data *data = hci_get_drvdata(hdev);
> > -
> >       if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
> >               struct hci_event_hdr *hdr = (void *)skb->data;
> >
> > @@ -2336,7 +2394,7 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
> >               }
> >       }
> >
> > -     return hci_recv_frame(hdev, skb);
> > +     return hci_recv_frame(data->hdev, skb);
> > }
> >
> > static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
> > @@ -4279,6 +4337,17 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
> >       return 0;
> > }
> >
> > +static int btusb_recv_evt(struct btusb_data *data, struct sk_buff *skb)
> > +{
> > +     if (!enable_interval)
> > +             return hci_recv_frame(data->hdev, skb);
> > +
> > +     /* Trigger dequeue immediatelly if an event is received */
> > +     schedule_delayed_work(&data->rx_work, 0);
> > +
> > +     return hci_recv_frame(data->hdev, skb);
> > +}
> > +
> > static int btusb_probe(struct usb_interface *intf,
> >                      const struct usb_device_id *id)
> > {
> > @@ -4362,6 +4431,10 @@ static int btusb_probe(struct usb_interface *intf,
> >
> >       INIT_WORK(&data->work, btusb_work);
> >       INIT_WORK(&data->waker, btusb_waker);
> > +     INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
> > +
> > +     skb_queue_head_init(&data->acl_q);
> > +
> >       init_usb_anchor(&data->deferred);
> >       init_usb_anchor(&data->tx_anchor);
> >       spin_lock_init(&data->txlock);
> > @@ -4378,7 +4451,7 @@ static int btusb_probe(struct usb_interface *intf,
> >               data->recv_bulk = btusb_recv_bulk_intel;
> >               set_bit(BTUSB_BOOTLOADER, &data->flags);
> >       } else {
> > -             data->recv_event = hci_recv_frame;
> > +             data->recv_event = btusb_recv_evt;
> >               data->recv_bulk = btusb_recv_bulk;
> >       }
> >
> > @@ -4867,6 +4940,9 @@ MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
> > module_param(enable_autosuspend, bool, 0644);
> > MODULE_PARM_DESC(enable_autosuspend, "Enable USB autosuspend by default");
> >
> > +module_param(enable_interval, bool, 0644);
> > +MODULE_PARM_DESC(enable_interval, "Enable USB polling interval by default");
> > +
>
> For a testing patch, leave this option in (just scrap the Kconfig part) and name “Enable URB polling interval synchronization”. I would also name the option “enable_poll_sync”.

Will update according to your comments.

> > module_param(reset, bool, 0644);
> > MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
>
> Regards
>
> Marcel
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval
  2021-12-02 21:09 Luiz Augusto von Dentz
@ 2021-12-03 21:46 ` Marcel Holtmann
  0 siblings, 0 replies; 6+ messages in thread
From: Marcel Holtmann @ 2021-12-03 21:46 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

> This makes btusb to queue ACL and events during a polling interval
> by using of a delayed work, with the interval working as a time window
> where frames received from different endpoints are considered to be
> arrived at same time and then attempt to resolve potential conflics by
> processing the events ahead of ACL packets.
> 
> It worth noting though that priorizing events over ACL data may result
> in inverting the order compared to how they appeared over the air, for
> instance there may be packets received before a disconnect event that
> will be discarded and unencrypted packets received before encryption
> change which would considered encrypted, because of these potential
> changes on the order the support for queuing during the polling
> interval is not enabled by default so it requires setting
> force_poll_sync debugfs while the adapter is down.
> 
> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> ---
> v2: Check for intr_interval instead of enable_poll_sync when dispatching
> since the former can only be changed on open/resume.
> v3: Change the way the feature is enabled from module parameter to debugfs
> entry and rename it from enable_poll_sync to force_poll_sync.
> 
> drivers/bluetooth/btusb.c | 125 ++++++++++++++++++++++++++++++++++++--
> 1 file changed, 120 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index ab169fc673ea..9b3cadb33fcb 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -16,6 +16,7 @@
> #include <linux/of_irq.h>
> #include <linux/suspend.h>
> #include <linux/gpio/consumer.h>
> +#include <linux/debugfs.h>
> #include <asm/unaligned.h>
> 
> #include <net/bluetooth/bluetooth.h>
> @@ -31,7 +32,6 @@
> static bool disable_scofix;
> static bool force_scofix;
> static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
> -
> static bool reset = true;
> 
> static struct usb_driver btusb_driver;
> @@ -558,8 +558,13 @@ struct btusb_data {
> 
> 	unsigned long flags;
> 
> -	struct work_struct work;
> -	struct work_struct waker;
> +	bool poll_sync;
> +	int intr_interval;
> +	struct work_struct  work;
> +	struct work_struct  waker;
> +	struct delayed_work rx_work;
> +
> +	struct sk_buff_head acl_q;
> 
> 	struct usb_anchor deferred;
> 	struct usb_anchor tx_anchor;
> @@ -724,6 +729,15 @@ static inline void btusb_free_frags(struct btusb_data *data)
> 	spin_unlock_irqrestore(&data->rxlock, flags);
> }
> 
> +static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
> +{
> +	if (data->intr_interval)

enclose in {

> +		/* Trigger dequeue immediatelly if an event is received */
> +		schedule_delayed_work(&data->rx_work, 0);

} this please.

> +
> +	return data->recv_event(data->hdev, skb);
> +}
> +
> static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> {
> 	struct sk_buff *skb;
> @@ -769,7 +783,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> 
> 		if (!hci_skb_expect(skb)) {
> 			/* Complete frame */
> -			data->recv_event(data->hdev, skb);
> +			btusb_recv_event(data, skb);
> 			skb = NULL;
> 		}
> 	}
> @@ -780,6 +794,18 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
> 	return err;
> }
> 
> +static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
> +{

Add a comment above here.

> +	if (!data->intr_interval)
> +		return data->recv_acl(data->hdev, skb);
> +
> +	skb_queue_tail(&data->acl_q, skb);
> +

Skip this empty line.

> +	schedule_delayed_work(&data->rx_work, data->intr_interval);
> +
> +	return 0;
> +}
> +
> static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> {
> 	struct sk_buff *skb;
> @@ -827,7 +853,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
> 
> 		if (!hci_skb_expect(skb)) {
> 			/* Complete frame */
> -			data->recv_acl(data->hdev, skb);
> +			btusb_recv_acl(data, skb);
> 			skb = NULL;
> 		}
> 	}
> @@ -979,6 +1005,27 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
> 		usb_unanchor_urb(urb);
> 	}
> 
> +	/* Only initialize intr_interval if URB poll sync is enabled */
> +	if (!data->poll_sync)
> +		goto done;
> +
> +	/* The units are frames (milliseconds) for full and low speed devices,
> +	 * and microframes (1/8 millisecond) for highspeed and SuperSpeed
> +	 * devices.
> +	 *
> +	 * This is done once on open/resume so it shouldn't change even if
> +	 * enable_poll_sync changes.
> +	 */
> +	switch (urb->dev->speed) {
> +	case USB_SPEED_SUPER_PLUS:
> +	case USB_SPEED_SUPER:	/* units are 125us */
> +		data->intr_interval = usecs_to_jiffies(urb->interval * 125);
> +		break;
> +	default:
> +		data->intr_interval = msecs_to_jiffies(urb->interval);

Add break here.

> +	}
> +
> +done:
> 	usb_free_urb(urb);
> 
> 	return err;
> @@ -1438,9 +1485,12 @@ static int btusb_close(struct hci_dev *hdev)
> 
> 	BT_DBG("%s", hdev->name);
> 
> +	cancel_delayed_work(&data->rx_work);
> 	cancel_work_sync(&data->work);
> 	cancel_work_sync(&data->waker);
> 
> +	skb_queue_purge(&data->acl_q);
> +
> 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> @@ -1472,6 +1522,10 @@ static int btusb_flush(struct hci_dev *hdev)
> 
> 	BT_DBG("%s", hdev->name);
> 
> +	cancel_delayed_work(&data->rx_work);
> +
> +	skb_queue_purge(&data->acl_q);
> +
> 	usb_kill_anchored_urbs(&data->tx_anchor);
> 	btusb_free_frags(data);
> 
> @@ -1835,6 +1889,17 @@ static void btusb_waker(struct work_struct *work)
> 	usb_autopm_put_interface(data->intf);
> }
> 
> +static void btusb_rx_work(struct work_struct *work)
> +{
> +	struct btusb_data *data = container_of(work, struct btusb_data,
> +					       rx_work.work);
> +	struct sk_buff *skb;
> +
> +	/* Dequeue ACL data received during the interval */
> +	while ((skb = skb_dequeue(&data->acl_q)))
> +		data->recv_acl(data->hdev, skb);
> +}
> +
> static int btusb_setup_bcm92035(struct hci_dev *hdev)
> {
> 	struct sk_buff *skb;
> @@ -3392,6 +3457,49 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
> 	return 0;
> }
> 
> +static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf,
> +				    size_t count, loff_t *ppos)
> +{
> +	struct btusb_data *data = file->private_data;
> +	char buf[3];
> +
> +	buf[0] = data->poll_sync ? 'Y' : 'N';
> +	buf[1] = '\n';
> +	buf[2] = '\0';
> +	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
> +}
> +
> +static ssize_t force_poll_sync_write(struct file *file,
> +				     const char __user *user_buf,
> +				     size_t count, loff_t *ppos)
> +{
> +	struct btusb_data *data = file->private_data;
> +	bool enable;
> +	int err;
> +
> +	err = kstrtobool_from_user(user_buf, count, &enable);
> +	if (err)
> +		return err;
> +
> +	/* Only allow changes while the adapter is down */
> +	if (test_bit(HCI_UP, &data->hdev->flags))
> +		return -EPERM;
> +
> +	if (data->poll_sync == enable)
> +		return -EALREADY;
> +
> +	data->poll_sync = enable;
> +
> +	return count;
> +}
> +
> +static const struct file_operations force_poll_sync_fops = {
> +	.open		= simple_open,
> +	.read		= force_poll_sync_read,
> +	.write		= force_poll_sync_write,
> +	.llseek		= default_llseek,
> +};
> +
> static int btusb_probe(struct usb_interface *intf,
> 		       const struct usb_device_id *id)
> {
> @@ -3475,6 +3583,10 @@ static int btusb_probe(struct usb_interface *intf,
> 
> 	INIT_WORK(&data->work, btusb_work);
> 	INIT_WORK(&data->waker, btusb_waker);
> +	INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
> +
> +	skb_queue_head_init(&data->acl_q);
> +
> 	init_usb_anchor(&data->deferred);
> 	init_usb_anchor(&data->tx_anchor);
> 	spin_lock_init(&data->txlock);
> @@ -3740,6 +3852,9 @@ static int btusb_probe(struct usb_interface *intf,
> 
> 	usb_set_intfdata(intf, data);
> 
> +	debugfs_create_file("force_poll_sync", 0644, hdev->debugfs, data,
> +			    &force_poll_sync_fops);
> +
> 	return 0;

So besides cosmetic changes, looks good.

Regards

Marcel


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval
@ 2021-12-02 21:09 Luiz Augusto von Dentz
  2021-12-03 21:46 ` Marcel Holtmann
  0 siblings, 1 reply; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2021-12-02 21:09 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This makes btusb to queue ACL and events during a polling interval
by using of a delayed work, with the interval working as a time window
where frames received from different endpoints are considered to be
arrived at same time and then attempt to resolve potential conflics by
processing the events ahead of ACL packets.

It worth noting though that priorizing events over ACL data may result
in inverting the order compared to how they appeared over the air, for
instance there may be packets received before a disconnect event that
will be discarded and unencrypted packets received before encryption
change which would considered encrypted, because of these potential
changes on the order the support for queuing during the polling
interval is not enabled by default so it requires setting
force_poll_sync debugfs while the adapter is down.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
v2: Check for intr_interval instead of enable_poll_sync when dispatching
since the former can only be changed on open/resume.
v3: Change the way the feature is enabled from module parameter to debugfs
entry and rename it from enable_poll_sync to force_poll_sync.

 drivers/bluetooth/btusb.c | 125 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 120 insertions(+), 5 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index ab169fc673ea..9b3cadb33fcb 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -16,6 +16,7 @@
 #include <linux/of_irq.h>
 #include <linux/suspend.h>
 #include <linux/gpio/consumer.h>
+#include <linux/debugfs.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -31,7 +32,6 @@
 static bool disable_scofix;
 static bool force_scofix;
 static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
-
 static bool reset = true;
 
 static struct usb_driver btusb_driver;
@@ -558,8 +558,13 @@ struct btusb_data {
 
 	unsigned long flags;
 
-	struct work_struct work;
-	struct work_struct waker;
+	bool poll_sync;
+	int intr_interval;
+	struct work_struct  work;
+	struct work_struct  waker;
+	struct delayed_work rx_work;
+
+	struct sk_buff_head acl_q;
 
 	struct usb_anchor deferred;
 	struct usb_anchor tx_anchor;
@@ -724,6 +729,15 @@ static inline void btusb_free_frags(struct btusb_data *data)
 	spin_unlock_irqrestore(&data->rxlock, flags);
 }
 
+static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
+{
+	if (data->intr_interval)
+		/* Trigger dequeue immediatelly if an event is received */
+		schedule_delayed_work(&data->rx_work, 0);
+
+	return data->recv_event(data->hdev, skb);
+}
+
 static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
 {
 	struct sk_buff *skb;
@@ -769,7 +783,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
 
 		if (!hci_skb_expect(skb)) {
 			/* Complete frame */
-			data->recv_event(data->hdev, skb);
+			btusb_recv_event(data, skb);
 			skb = NULL;
 		}
 	}
@@ -780,6 +794,18 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
 	return err;
 }
 
+static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
+{
+	if (!data->intr_interval)
+		return data->recv_acl(data->hdev, skb);
+
+	skb_queue_tail(&data->acl_q, skb);
+
+	schedule_delayed_work(&data->rx_work, data->intr_interval);
+
+	return 0;
+}
+
 static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
 {
 	struct sk_buff *skb;
@@ -827,7 +853,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
 
 		if (!hci_skb_expect(skb)) {
 			/* Complete frame */
-			data->recv_acl(data->hdev, skb);
+			btusb_recv_acl(data, skb);
 			skb = NULL;
 		}
 	}
@@ -979,6 +1005,27 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 		usb_unanchor_urb(urb);
 	}
 
+	/* Only initialize intr_interval if URB poll sync is enabled */
+	if (!data->poll_sync)
+		goto done;
+
+	/* The units are frames (milliseconds) for full and low speed devices,
+	 * and microframes (1/8 millisecond) for highspeed and SuperSpeed
+	 * devices.
+	 *
+	 * This is done once on open/resume so it shouldn't change even if
+	 * enable_poll_sync changes.
+	 */
+	switch (urb->dev->speed) {
+	case USB_SPEED_SUPER_PLUS:
+	case USB_SPEED_SUPER:	/* units are 125us */
+		data->intr_interval = usecs_to_jiffies(urb->interval * 125);
+		break;
+	default:
+		data->intr_interval = msecs_to_jiffies(urb->interval);
+	}
+
+done:
 	usb_free_urb(urb);
 
 	return err;
@@ -1438,9 +1485,12 @@ static int btusb_close(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
+	cancel_delayed_work(&data->rx_work);
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->waker);
 
+	skb_queue_purge(&data->acl_q);
+
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
@@ -1472,6 +1522,10 @@ static int btusb_flush(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
+	cancel_delayed_work(&data->rx_work);
+
+	skb_queue_purge(&data->acl_q);
+
 	usb_kill_anchored_urbs(&data->tx_anchor);
 	btusb_free_frags(data);
 
@@ -1835,6 +1889,17 @@ static void btusb_waker(struct work_struct *work)
 	usb_autopm_put_interface(data->intf);
 }
 
+static void btusb_rx_work(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data,
+					       rx_work.work);
+	struct sk_buff *skb;
+
+	/* Dequeue ACL data received during the interval */
+	while ((skb = skb_dequeue(&data->acl_q)))
+		data->recv_acl(data->hdev, skb);
+}
+
 static int btusb_setup_bcm92035(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -3392,6 +3457,49 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
 	return 0;
 }
 
+static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct btusb_data *data = file->private_data;
+	char buf[3];
+
+	buf[0] = data->poll_sync ? 'Y' : 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_poll_sync_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct btusb_data *data = file->private_data;
+	bool enable;
+	int err;
+
+	err = kstrtobool_from_user(user_buf, count, &enable);
+	if (err)
+		return err;
+
+	/* Only allow changes while the adapter is down */
+	if (test_bit(HCI_UP, &data->hdev->flags))
+		return -EPERM;
+
+	if (data->poll_sync == enable)
+		return -EALREADY;
+
+	data->poll_sync = enable;
+
+	return count;
+}
+
+static const struct file_operations force_poll_sync_fops = {
+	.open		= simple_open,
+	.read		= force_poll_sync_read,
+	.write		= force_poll_sync_write,
+	.llseek		= default_llseek,
+};
+
 static int btusb_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
 {
@@ -3475,6 +3583,10 @@ static int btusb_probe(struct usb_interface *intf,
 
 	INIT_WORK(&data->work, btusb_work);
 	INIT_WORK(&data->waker, btusb_waker);
+	INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
+
+	skb_queue_head_init(&data->acl_q);
+
 	init_usb_anchor(&data->deferred);
 	init_usb_anchor(&data->tx_anchor);
 	spin_lock_init(&data->txlock);
@@ -3740,6 +3852,9 @@ static int btusb_probe(struct usb_interface *intf,
 
 	usb_set_intfdata(intf, data);
 
+	debugfs_create_file("force_poll_sync", 0644, hdev->debugfs, data,
+			    &force_poll_sync_fops);
+
 	return 0;
 
 out_free_dev:
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-12-03 21:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-26 18:50 [PATCH v3] Bluetooth: btusb: Add support for queuing during polling interval Luiz Augusto von Dentz
2021-01-27  0:14 ` [v3] " bluez.test.bot
2021-02-04 13:06 ` [PATCH v3] " Marcel Holtmann
2021-02-08 21:13   ` Luiz Augusto von Dentz
2021-12-02 21:09 Luiz Augusto von Dentz
2021-12-03 21:46 ` Marcel Holtmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).