From 5c285465bb76d20289eabff6b93d116dd056c56d Mon Sep 17 00:00:00 2001 From: Sylvain Fasel Date: Tue, 30 Jan 2024 14:15:50 +0100 Subject: [PATCH] hotplug_exit removes from list all parents with a single child in parent tree This ensure that no parents of the direct parent of the device being considered are left in the list, when appearing before their child in the processing order of the context device list. --- libusb/hotplug.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/libusb/hotplug.c b/libusb/hotplug.c index 3c64f6919..8cb926e3b 100644 --- a/libusb/hotplug.c +++ b/libusb/hotplug.c @@ -161,6 +161,25 @@ void usbi_hotplug_init(struct libusb_context *ctx) usbi_atomic_store(&ctx->hotplug_ready, 1); } +static void usbi_recursively_remove_parents(struct libusb_device *dev, struct libusb_device *next_dev) +{ + if (dev && dev->parent_dev) { + if (usbi_atomic_load(&dev->parent_dev->refcnt) == 1) { + /* the parent was processed before this device in the list + * and therefore has its own ref count already decrement. + * The only remaining ref counting comes from its remaining single child. + * It will thus be released when its child will be released. + We remove it from the list. This is safe as parent_dev can not be + equal to next_dev given we know at this point that it was + previously seen in the list. */ + assert (dev->parent_dev != next_dev); + list_del(&dev->parent_dev->list); + } + + usbi_recursively_remove_parents(dev->parent_dev, next_dev); + } +} + void usbi_hotplug_exit(struct libusb_context *ctx) { struct usbi_hotplug_callback *hotplug_cb, *next_cb; @@ -201,13 +220,9 @@ void usbi_hotplug_exit(struct libusb_context *ctx) if (usbi_atomic_load(&dev->refcnt) == 1) { list_del(&dev->list); } - if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) { - /* the parent was before this device in the list and will be released. - remove it from the list. this is safe as parent_dev can not be - equal to next_dev. */ - assert (dev->parent_dev != next_dev); - list_del(&dev->parent_dev->list); - } + + usbi_recursively_remove_parents(dev, next_dev); + libusb_unref_device(dev); }