summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-03 21:49:33 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-03 21:49:33 +0300
commit8e8d9442d1139d05d0c3b83efa34c4b7693d2969 (patch)
tree33af777d1e177b0f950a406ae7c8a2dac2147dbc /drivers/base
parent58ec9059b396a570b208239b4edc45eeb68b14c4 (diff)
parent6a45ece4c9af473555f01f0f8b97eba56e3c7d0d (diff)
downloadlinux-8e8d9442d1139d05d0c3b83efa34c4b7693d2969.tar.xz
Merge tag 'vfio-v5.14-rc1' of git://github.com/awilliam/linux-vfio
Pull VFIO updates from Alex Williamson: - Module reference fixes, structure renaming (Max Gurtovoy) - Export and use common pci_dev_trylock() (Luis Chamberlain) - Enable direct mdev device creation and probing by parent (Christoph Hellwig & Jason Gunthorpe) - Fix mdpy error path leak (Colin Ian King) - Fix mtty list entry leak (Jason Gunthorpe) - Enforce mtty device limit (Alex Williamson) - Resolve concurrent vfio-pci mmap faults (Alex Williamson) * tag 'vfio-v5.14-rc1' of git://github.com/awilliam/linux-vfio: vfio/pci: Handle concurrent vma faults vfio/mtty: Enforce available_instances vfio/mtty: Delete mdev_devices_list vfio: use the new pci_dev_trylock() helper to simplify try lock PCI: Export pci_dev_trylock() and pci_dev_unlock() vfio/mdpy: Fix memory leak of object mdev_state->vconfig vfio/iommu_type1: rename vfio_group struck to vfio_iommu_group vfio/mbochs: Convert to use vfio_register_group_dev() vfio/mdpy: Convert to use vfio_register_group_dev() vfio/mtty: Convert to use vfio_register_group_dev() vfio/mdev: Allow the mdev_parent_ops to specify the device driver to bind vfio/mdev: Remove CONFIG_VFIO_MDEV_DEVICE driver core: Export device_driver_attach() driver core: Don't return EPROBE_DEFER to userspace during sysfs bind driver core: Flow the return code from ->probe() through to sysfs bind driver core: Better distinguish probe errors in really_probe driver core: Pull required checks into driver_probe_device() vfio/platform: remove unneeded parent_module attribute vfio: centralize module refcount in subsystem layer
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/base.h1
-rw-r--r--drivers/base/bus.c8
-rw-r--r--drivers/base/dd.c192
3 files changed, 108 insertions, 93 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h
index e5f9b7e656c3..404db83ee5ec 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -152,7 +152,6 @@ extern int driver_add_groups(struct device_driver *drv,
const struct attribute_group **groups);
extern void driver_remove_groups(struct device_driver *drv,
const struct attribute_group **groups);
-int device_driver_attach(struct device_driver *drv, struct device *dev);
void device_driver_detach(struct device *dev);
extern char *make_class_name(const char *name, struct kobject *kobj);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 36d0c654ea61..1f6b4bd61056 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -210,15 +210,11 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf,
int err = -ENODEV;
dev = bus_find_device_by_name(bus, NULL, buf);
- if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {
+ if (dev && driver_match_device(drv, dev)) {
err = device_driver_attach(drv, dev);
-
- if (err > 0) {
+ if (!err) {
/* success */
err = count;
- } else if (err == 0) {
- /* driver didn't accept device */
- err = -ENODEV;
}
}
put_device(dev);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index ecd7cf848daf..daeb9b5763ae 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -471,6 +471,8 @@ static void driver_sysfs_remove(struct device *dev)
* (It is ok to call with no other effort from a driver's probe() method.)
*
* This function must be called with the device lock held.
+ *
+ * Callers should prefer to use device_driver_attach() instead.
*/
int device_bind_driver(struct device *dev)
{
@@ -491,15 +493,6 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
-static void driver_deferred_probe_add_trigger(struct device *dev,
- int local_trigger_count)
-{
- driver_deferred_probe_add(dev);
- /* Did a trigger occur while probing? Need to re-trigger if yes */
- if (local_trigger_count != atomic_read(&deferred_trigger_count))
- driver_deferred_probe_trigger();
-}
-
static ssize_t state_synced_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -513,12 +506,43 @@ static ssize_t state_synced_show(struct device *dev,
}
static DEVICE_ATTR_RO(state_synced);
+
+static int call_driver_probe(struct device *dev, struct device_driver *drv)
+{
+ int ret = 0;
+
+ if (dev->bus->probe)
+ ret = dev->bus->probe(dev);
+ else if (drv->probe)
+ ret = drv->probe(dev);
+
+ switch (ret) {
+ case 0:
+ break;
+ case -EPROBE_DEFER:
+ /* Driver requested deferred probing */
+ dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
+ break;
+ case -ENODEV:
+ case -ENXIO:
+ pr_debug("%s: probe of %s rejects match %d\n",
+ drv->name, dev_name(dev), ret);
+ break;
+ default:
+ /* driver matched but the probe failed */
+ pr_warn("%s: probe of %s failed with error %d\n",
+ drv->name, dev_name(dev), ret);
+ break;
+ }
+
+ return ret;
+}
+
static int really_probe(struct device *dev, struct device_driver *drv)
{
- int ret = -EPROBE_DEFER;
- int local_trigger_count = atomic_read(&deferred_trigger_count);
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
!drv->suppress_bind_attrs;
+ int ret;
if (defer_all_probes) {
/*
@@ -527,17 +551,13 @@ static int really_probe(struct device *dev, struct device_driver *drv)
* wait_for_device_probe() right after that to avoid any races.
*/
dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
- driver_deferred_probe_add(dev);
- return ret;
+ return -EPROBE_DEFER;
}
ret = device_links_check_suppliers(dev);
- if (ret == -EPROBE_DEFER)
- driver_deferred_probe_add_trigger(dev, local_trigger_count);
if (ret)
return ret;
- atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
if (!list_empty(&dev->devres_head)) {
@@ -572,14 +592,14 @@ re_probe:
goto probe_failed;
}
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
+ ret = call_driver_probe(dev, drv);
+ if (ret) {
+ /*
+ * Return probe errors as positive values so that the callers
+ * can distinguish them from other errors.
+ */
+ ret = -ret;
+ goto probe_failed;
}
if (device_add_groups(dev, drv->dev_groups)) {
@@ -621,7 +641,6 @@ re_probe:
dev->pm_domain->sync(dev);
driver_bound(dev);
- ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
@@ -650,31 +669,7 @@ pinctrl_bind_failed:
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);
-
- switch (ret) {
- case -EPROBE_DEFER:
- /* Driver requested deferred probing */
- dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
- driver_deferred_probe_add_trigger(dev, local_trigger_count);
- break;
- case -ENODEV:
- case -ENXIO:
- pr_debug("%s: probe of %s rejects match %d\n",
- drv->name, dev_name(dev), ret);
- break;
- default:
- /* driver matched but the probe failed */
- pr_warn("%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
done:
- atomic_dec(&probe_count);
- wake_up_all(&probe_waitqueue);
return ret;
}
@@ -728,25 +723,14 @@ void wait_for_device_probe(void)
}
EXPORT_SYMBOL_GPL(wait_for_device_probe);
-/**
- * driver_probe_device - attempt to bind device & driver together
- * @drv: driver to bind a device to
- * @dev: device to try to bind to the driver
- *
- * This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound successfully and 0 otherwise.
- *
- * This function must be called with @dev lock held. When called for a
- * USB interface, @dev->parent lock must be held as well.
- *
- * If the device has a parent, runtime-resume the parent before driver probing.
- */
-static int driver_probe_device(struct device_driver *drv, struct device *dev)
+static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
- if (!device_is_registered(dev))
+ if (dev->p->dead || !device_is_registered(dev))
return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
dev->can_match = true;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
@@ -770,6 +754,42 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev)
return ret;
}
+/**
+ * driver_probe_device - attempt to bind device & driver together
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+ * This function returns -ENODEV if the device is not registered, -EBUSY if it
+ * already has a driver, 0 if the device is bound successfully and a positive
+ * (inverted) error code for failures from the ->probe method.
+ *
+ * This function must be called with @dev lock held. When called for a
+ * USB interface, @dev->parent lock must be held as well.
+ *
+ * If the device has a parent, runtime-resume the parent before driver probing.
+ */
+static int driver_probe_device(struct device_driver *drv, struct device *dev)
+{
+ int trigger_count = atomic_read(&deferred_trigger_count);
+ int ret;
+
+ atomic_inc(&probe_count);
+ ret = __driver_probe_device(drv, dev);
+ if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
+ driver_deferred_probe_add(dev);
+
+ /*
+ * Did a trigger occur while probing? Need to re-trigger if yes
+ */
+ if (trigger_count != atomic_read(&deferred_trigger_count) &&
+ !defer_all_probes)
+ driver_deferred_probe_trigger();
+ }
+ atomic_dec(&probe_count);
+ wake_up_all(&probe_waitqueue);
+ return ret;
+}
+
static inline bool cmdline_requested_async_probing(const char *drv_name)
{
return parse_option_str(async_probe_drv_names, drv_name);
@@ -867,7 +887,14 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
if (data->check_async && async_allowed != data->want_async)
return 0;
- return driver_probe_device(drv, dev);
+ /*
+ * Ignore errors returned by ->probe so that the next driver can try
+ * its luck.
+ */
+ ret = driver_probe_device(drv, dev);
+ if (ret < 0)
+ return ret;
+ return ret == 0;
}
static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
@@ -1023,43 +1050,34 @@ static void __device_driver_unlock(struct device *dev, struct device *parent)
* @dev: Device to attach it to
*
* Manually attach driver to a device. Will acquire both @dev lock and
- * @dev->parent lock if needed.
+ * @dev->parent lock if needed. Returns 0 on success, -ERR on failure.
*/
int device_driver_attach(struct device_driver *drv, struct device *dev)
{
- int ret = 0;
+ int ret;
__device_driver_lock(dev, dev->parent);
-
- /*
- * If device has been removed or someone has already successfully
- * bound a driver before us just skip the driver probe call.
- */
- if (!dev->p->dead && !dev->driver)
- ret = driver_probe_device(drv, dev);
-
+ ret = __driver_probe_device(drv, dev);
__device_driver_unlock(dev, dev->parent);
+ /* also return probe errors as normal negative errnos */
+ if (ret > 0)
+ ret = -ret;
+ if (ret == -EPROBE_DEFER)
+ return -EAGAIN;
return ret;
}
+EXPORT_SYMBOL_GPL(device_driver_attach);
static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
{
struct device *dev = _dev;
struct device_driver *drv;
- int ret = 0;
+ int ret;
__device_driver_lock(dev, dev->parent);
-
drv = dev->p->async_driver;
-
- /*
- * If device has been removed or someone has already successfully
- * bound a driver before us just skip the driver probe call.
- */
- if (!dev->p->dead && !dev->driver)
- ret = driver_probe_device(drv, dev);
-
+ ret = driver_probe_device(drv, dev);
__device_driver_unlock(dev, dev->parent);
dev_dbg(dev, "driver %s async attach completed: %d\n", drv->name, ret);
@@ -1114,7 +1132,9 @@ static int __driver_attach(struct device *dev, void *data)
return 0;
}
- device_driver_attach(drv, dev);
+ __device_driver_lock(dev, dev->parent);
+ driver_probe_device(drv, dev);
+ __device_driver_unlock(dev, dev->parent);
return 0;
}