[PATCH v4] Bluetooth: virtio: Fix virtbt_probe() init and cleanup
From: Haoxiang Li
Date: Thu Jul 09 2026 - 07:48:31 EST
virtbt_probe() allocates vbt before setting up the virtqueues, but some
failure paths return without freeing it.
The probe path also registers the HCI device before the virtio transport
is opened. Since hci_register_dev() makes the HCI device visible and queues
power_on work, move it after virtio_device_ready() and virtbt_open_vdev()
so the transport is ready before the HCI core can use it.
On failures after DRIVER_OK, reset and close the virtio device before
deleting the virtqueues and freeing vbt. This also cancels pending rx work
before vbt is freed.
Fixes: afd2daa26c7a ("Bluetooth: Add support for virtio transport driver")
Fixes: dc65b4b0f90a ("Bluetooth: virtio_bt: fix device removal")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Haoxiang Li <haoxiang_li2024@xxxxxxx>
---
Changes in v2:
- Rework virtbt_probe() error paths into an unwind ladder.
- Free vbt on probe failures.
- Reset the virtio device and unregister the HCI device before freeing it
when virtbt_open_vdev() fails.
- Close the virtio device before unregistering the HCI device in remove().
Thanks Dan for the suggestions. The blog is very helpful.
Changes in v3:
- Remove virtio_reset_device() from the virtbt_open_vdev() failure path.
Changes in v4:
- Move hci_register_dev() after virtio_device_ready() and virtbt_open_vdev().
- Reset and close the virtio device on probe failures after DRIVER_OK. Thanks, Luiz!
---
drivers/bluetooth/virtio_bt.c | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 140ab55c9fc5..e7e79ba3c1f7 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -311,12 +311,12 @@ static int virtbt_probe(struct virtio_device *vdev)
err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, vqs_info, NULL);
if (err)
- return err;
+ goto err_free_vbt;
hdev = hci_alloc_dev();
if (!hdev) {
err = -ENOMEM;
- goto failed;
+ goto err_del_vqs;
}
vbt->hdev = hdev;
@@ -383,23 +383,28 @@ static int virtbt_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
hci_set_aosp_capable(hdev);
- if (hci_register_dev(hdev) < 0) {
- hci_free_dev(hdev);
- err = -EBUSY;
- goto failed;
- }
-
virtio_device_ready(vdev);
err = virtbt_open_vdev(vbt);
if (err)
- goto open_failed;
+ goto err_close_vdev;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ err = -EBUSY;
+ goto err_close_vdev;
+ }
return 0;
-open_failed:
+err_close_vdev:
+ virtio_reset_device(vdev);
+ virtbt_close_vdev(vbt);
hci_free_dev(hdev);
-failed:
+err_del_vqs:
vdev->config->del_vqs(vdev);
+err_free_vbt:
+ vdev->priv = NULL;
+ kfree(vbt);
return err;
}
--
2.25.1