mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
Driver core fixes for 7.4-rc1
- Make parent-lock requirements per device type instead of per bus, fixing a use-after-free on USB disconnect by limiting the requirement to USB interfaces, which retain a reference to their parent - Honor parent-lock requirements during deferred and asynchronous probing, fixing USB interface probe races, including duplicate endpoint creation attempts in sysfs -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQS2q/xV6QjXAdC7k+1FlHeO1qrKLgUCat5gLQAKCRBFlHeO1qrK LtDiAP42G5THtYuYWgtS1S42wNJGEFLAxD5FheBMDu7AfL8QuQD+KbI0FmAhQRp7 avf4YvdeDWE1QZWV5THYvBDqzNB8BQ4= =6oMk -----END PGP SIGNATURE----- Merge tag 'driver-core-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core Pull driver core fixes from Danilo Krummrich: - Make parent-lock requirements per device type instead of per bus, fixing a use-after-free on USB disconnect by limiting the requirement to USB interfaces, which retain a reference to their parent - Honor parent-lock requirements during deferred and asynchronous probing, fixing USB interface probe races, including duplicate endpoint creation attempts in sysfs * tag 'driver-core-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: driver core: take parent lock in async device attach driver core: take parent lock on deferred probe driver core: fix parent lock use-after-free on USB disconnect
This commit is contained in:
commit
0cc3ad8f4d
|
|
@ -156,6 +156,7 @@ struct kobject *virtual_device_parent(void);
|
|||
|
||||
int bus_add_device(struct device *dev);
|
||||
void bus_probe_device(struct device *dev);
|
||||
void bus_probe_device_deferred(struct device *dev);
|
||||
void bus_remove_device(struct device *dev);
|
||||
void bus_notify(struct device *dev, enum bus_notifier_event value);
|
||||
bool bus_is_registered(const struct bus_type *bus);
|
||||
|
|
|
|||
|
|
@ -606,13 +606,7 @@ int bus_add_device(struct device *dev)
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_probe_device - probe drivers for a new device
|
||||
* @dev: device to probe
|
||||
*
|
||||
* - Automatically probe for a driver if the bus allows it.
|
||||
*/
|
||||
void bus_probe_device(struct device *dev)
|
||||
static void __bus_probe_device(struct device *dev)
|
||||
{
|
||||
struct subsys_private *sp = bus_to_subsys(dev->bus);
|
||||
struct subsys_interface *sif;
|
||||
|
|
@ -630,6 +624,26 @@ void bus_probe_device(struct device *dev)
|
|||
subsys_put(sp);
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_probe_device - probe drivers for a new device
|
||||
* @dev: device to probe
|
||||
*
|
||||
* - Automatically probe for a driver if the bus allows it.
|
||||
*/
|
||||
void bus_probe_device(struct device *dev)
|
||||
{
|
||||
__bus_probe_device(dev);
|
||||
}
|
||||
|
||||
void bus_probe_device_deferred(struct device *dev)
|
||||
{
|
||||
if (dev->type && dev->type->need_parent_lock)
|
||||
device_lock(dev->parent);
|
||||
__bus_probe_device(dev);
|
||||
if (dev->type && dev->type->need_parent_lock)
|
||||
device_unlock(dev->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_remove_device - remove device from bus
|
||||
* @dev: device to be removed
|
||||
|
|
@ -857,10 +871,10 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
|
|||
int ret = 0;
|
||||
|
||||
if (!dev->driver) {
|
||||
if (dev->parent && dev->bus->need_parent_lock)
|
||||
if (dev->type && dev->type->need_parent_lock)
|
||||
device_lock(dev->parent);
|
||||
ret = device_attach(dev);
|
||||
if (dev->parent && dev->bus->need_parent_lock)
|
||||
if (dev->type && dev->type->need_parent_lock)
|
||||
device_unlock(dev->parent);
|
||||
}
|
||||
return ret < 0 ? ret : 0;
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ static void deferred_probe_work_func(struct work_struct *work)
|
|||
/*
|
||||
* This block processes every device in the deferred 'active' list.
|
||||
* Each device is removed from the active list and passed to
|
||||
* bus_probe_device() to re-attempt the probe. The loop continues
|
||||
* until every device in the active list is removed and retried.
|
||||
* bus_probe_device_deferred() to re-attempt the probe. The loop
|
||||
* continues until every device in the active list is removed and
|
||||
* retried.
|
||||
*
|
||||
* Note: Once the device is removed from the list and the mutex is
|
||||
* released, it is possible for the device get freed by another thread
|
||||
|
|
@ -121,7 +122,7 @@ static void deferred_probe_work_func(struct work_struct *work)
|
|||
device_pm_move_to_tail(dev);
|
||||
|
||||
dev_dbg(dev, "Retrying from deferred list\n");
|
||||
bus_probe_device(dev);
|
||||
bus_probe_device_deferred(dev);
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
|
||||
put_device(dev);
|
||||
|
|
@ -1029,6 +1030,38 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
|
|||
return ret == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* __device_driver_lock - acquire locks needed to manipulate dev->drv
|
||||
* @dev: Device we will update driver info for
|
||||
* @parent: Parent device. Needed if the device type requires parent lock
|
||||
*
|
||||
* This function will take the required locks for manipulating dev->drv.
|
||||
* Normally this will just be the @dev lock, but when called for a USB
|
||||
* interface, @parent lock will be held as well.
|
||||
*/
|
||||
static void __device_driver_lock(struct device *dev, struct device *parent)
|
||||
{
|
||||
if (dev->type && dev->type->need_parent_lock && parent)
|
||||
device_lock(parent);
|
||||
device_lock(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* __device_driver_unlock - release locks needed to manipulate dev->drv
|
||||
* @dev: Device we will update driver info for
|
||||
* @parent: Parent device. Needed if the device type requires parent lock
|
||||
*
|
||||
* This function will release the required locks for manipulating dev->drv.
|
||||
* Normally this will just be the @dev lock, but when called for a
|
||||
* USB interface, @parent lock will be released as well.
|
||||
*/
|
||||
static void __device_driver_unlock(struct device *dev, struct device *parent)
|
||||
{
|
||||
device_unlock(dev);
|
||||
if (dev->type && dev->type->need_parent_lock && parent)
|
||||
device_unlock(parent);
|
||||
}
|
||||
|
||||
static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
|
||||
{
|
||||
struct device *dev = _dev;
|
||||
|
|
@ -1038,7 +1071,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
|
|||
.want_async = true,
|
||||
};
|
||||
|
||||
device_lock(dev);
|
||||
__device_driver_lock(dev, dev->parent);
|
||||
|
||||
/*
|
||||
* Check if device has already been removed or claimed. This may
|
||||
|
|
@ -1060,7 +1093,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
|
|||
if (dev->parent)
|
||||
pm_runtime_put(dev->parent);
|
||||
out_unlock:
|
||||
device_unlock(dev);
|
||||
__device_driver_unlock(dev, dev->parent);
|
||||
|
||||
put_device(dev);
|
||||
}
|
||||
|
|
@ -1117,8 +1150,11 @@ static int __device_attach(struct device *dev, bool allow_async)
|
|||
}
|
||||
out_unlock:
|
||||
device_unlock(dev);
|
||||
if (async)
|
||||
async_schedule_dev(__device_attach_async_helper, dev);
|
||||
if (async && !async_schedule_dev_nocall(__device_attach_async_helper, dev)) {
|
||||
put_device(dev);
|
||||
ret = 2; /* retry synchronously */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1145,48 +1181,20 @@ EXPORT_SYMBOL_GPL(device_attach);
|
|||
void device_initial_probe(struct device *dev)
|
||||
{
|
||||
struct subsys_private *sp = bus_to_subsys(dev->bus);
|
||||
int ret;
|
||||
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
if (sp->drivers_autoprobe)
|
||||
__device_attach(dev, true);
|
||||
if (sp->drivers_autoprobe) {
|
||||
ret = __device_attach(dev, true);
|
||||
if (ret == 2)
|
||||
__device_attach(dev, false);
|
||||
}
|
||||
|
||||
subsys_put(sp);
|
||||
}
|
||||
|
||||
/*
|
||||
* __device_driver_lock - acquire locks needed to manipulate dev->drv
|
||||
* @dev: Device we will update driver info for
|
||||
* @parent: Parent device. Needed if the bus requires parent lock
|
||||
*
|
||||
* This function will take the required locks for manipulating dev->drv.
|
||||
* Normally this will just be the @dev lock, but when called for a USB
|
||||
* interface, @parent lock will be held as well.
|
||||
*/
|
||||
static void __device_driver_lock(struct device *dev, struct device *parent)
|
||||
{
|
||||
if (parent && dev->bus->need_parent_lock)
|
||||
device_lock(parent);
|
||||
device_lock(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* __device_driver_unlock - release locks needed to manipulate dev->drv
|
||||
* @dev: Device we will update driver info for
|
||||
* @parent: Parent device. Needed if the bus requires parent lock
|
||||
*
|
||||
* This function will release the required locks for manipulating dev->drv.
|
||||
* Normally this will just be the @dev lock, but when called for a
|
||||
* USB interface, @parent lock will be released as well.
|
||||
*/
|
||||
static void __device_driver_unlock(struct device *dev, struct device *parent)
|
||||
{
|
||||
device_unlock(dev);
|
||||
if (parent && dev->bus->need_parent_lock)
|
||||
device_unlock(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_driver_attach - attach a specific driver to a specific device
|
||||
* @drv: Driver to attach
|
||||
|
|
|
|||
|
|
@ -2082,5 +2082,4 @@ const struct bus_type usb_bus_type = {
|
|||
.name = "usb",
|
||||
.match = usb_device_match,
|
||||
.uevent = usb_uevent,
|
||||
.need_parent_lock = true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1922,6 +1922,7 @@ const struct device_type usb_if_device_type = {
|
|||
.name = "usb_interface",
|
||||
.release = usb_release_interface,
|
||||
.uevent = usb_if_uevent,
|
||||
.need_parent_lock = true,
|
||||
};
|
||||
|
||||
static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ int subsys_virtual_register(const struct bus_type *subsys,
|
|||
* information, equivalent to the kobj_type of a kobject.
|
||||
* If "name" is specified, the uevent will contain it in
|
||||
* the DEVTYPE variable.
|
||||
*
|
||||
* @need_parent_lock: When probing or removing a device of this type,
|
||||
* driver core should lock the device's parent. Note that
|
||||
* the device must hold a reference to its parent.
|
||||
*/
|
||||
struct device_type {
|
||||
const char *name;
|
||||
|
|
@ -93,6 +97,7 @@ struct device_type {
|
|||
void (*release)(struct device *dev);
|
||||
|
||||
const struct dev_pm_ops *pm;
|
||||
bool need_parent_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -68,8 +68,6 @@ struct fwnode_handle;
|
|||
* @driver_override: Set to true if this bus supports the driver_override
|
||||
* mechanism, which allows userspace to force a specific
|
||||
* driver to bind to a device via a sysfs attribute.
|
||||
* @need_parent_lock: When probing or removing a device on this bus, the
|
||||
* device core should lock the device's parent.
|
||||
*
|
||||
* A bus is a channel between the processor and one or more devices. For the
|
||||
* purposes of the device model, all devices are connected via a bus, even if
|
||||
|
|
@ -110,7 +108,6 @@ struct bus_type {
|
|||
const struct dev_pm_ops *pm;
|
||||
|
||||
bool driver_override;
|
||||
bool need_parent_lock;
|
||||
};
|
||||
|
||||
int __must_check companion_bus_register(const struct bus_type *bus);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user