[PATCH 4/5] driver core: inhibit automatic driver binding on reserved devices

Zev Weiss
2021-10-22T02:00:56Z

Devices whose fwnodes are marked as reserved are instantiated, but
will not have a driver bound to them unless userspace explicitly
requests it by writing to a 'bind' sysfs file. This is to enable
devices that may require special (userspace-mediated) preparation
before a driver can safely probe them.

Signed-off-by: Zev Weiss <zev@bewilderbeest.net>
---
drivers/base/bus.c | 2 +-
drivers/base/dd.c | 13 ++++++++-----
drivers/dma/idxd/compat.c | 3 +--
drivers/vfio/mdev/mdev_core.c | 2 +-
include/linux/device.h | 14 +++++++++++++-
5 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index bdc98c5713d5..8a30f9a02de0 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -211,7 +211,7 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf,

dev = bus_find_device_by_name(bus, NULL, buf);
if (dev && driver_match_device(drv, dev)) {
- err = device_driver_attach(drv, dev);
+ err = device_driver_attach(drv, dev, DRV_BIND_EXPLICIT);
if (!err) {
/* success */
err = count;
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 68ea1f949daa..ee64740a63d9 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -22,6 +22,7 @@
#include <linux/dma-map-ops.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/async.h>
@@ -727,13 +728,14 @@ void wait_for_device_probe(void)
}
EXPORT_SYMBOL_GPL(wait_for_device_probe);

-static int __driver_probe_device(struct device_driver *drv, struct device *dev)
+static int __driver_probe_device(struct device_driver *drv, struct device *dev, u32 flags)
{
int ret = 0;

if (dev->p->dead || !device_is_registered(dev))
return -ENODEV;
- if (dev->driver)
+ if (dev->driver ||
+ (fwnode_device_is_reserved(dev->fwnode) && !(flags & DRV_BIND_EXPLICIT)))
return -EBUSY;

dev->can_match = true;
@@ -778,7 +780,7 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev)
int ret;

atomic_inc(&probe_count);
- ret = __driver_probe_device(drv, dev);
+ ret = __driver_probe_device(drv, dev, DRV_BIND_DEFAULT);
if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
driver_deferred_probe_add(dev);

@@ -1052,16 +1054,17 @@ static void __device_driver_unlock(struct device *dev, struct device *parent)
* device_driver_attach - attach a specific driver to a specific device
* @drv: Driver to attach
* @dev: Device to attach it to
+ * @flags: Bitmask of DRV_BIND_* flags
*
* Manually attach driver to a device. Will acquire both @dev lock and
* @dev->parent lock if needed. Returns 0 on success, -ERR on failure.
*/
-int device_driver_attach(struct device_driver *drv, struct device *dev)
+int device_driver_attach(struct device_driver *drv, struct device *dev, u32 flags)
{
int ret;

__device_driver_lock(dev, dev->parent);
- ret = __driver_probe_device(drv, dev);
+ ret = __driver_probe_device(drv, dev, flags);
__device_driver_unlock(dev, dev->parent);

/* also return probe errors as normal negative errnos */
diff --git a/drivers/dma/idxd/compat.c b/drivers/dma/idxd/compat.c
index 3df21615f888..51df38dea15a 100644
--- a/drivers/dma/idxd/compat.c
+++ b/drivers/dma/idxd/compat.c
@@ -7,7 +7,6 @@
#include <linux/device/bus.h>
#include "idxd.h"

-extern int device_driver_attach(struct device_driver *drv, struct device *dev);
extern void device_driver_detach(struct device *dev);

#define DRIVER_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
@@ -56,7 +55,7 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf, size_t cou
if (!alt_drv)
return -ENODEV;

- rc = device_driver_attach(alt_drv, dev);
+ rc = device_driver_attach(alt_drv, dev, DRV_BIND_EXPLICIT);
if (rc < 0)
return rc;

diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c
index b314101237fe..f42c6ec543c8 100644
--- a/drivers/vfio/mdev/mdev_core.c
+++ b/drivers/vfio/mdev/mdev_core.c
@@ -309,7 +309,7 @@ int mdev_device_create(struct mdev_type *type, const guid_t *uuid)

if (!drv)
drv = &vfio_mdev_driver;
- ret = device_driver_attach(&drv->driver, &mdev->dev);
+ ret = device_driver_attach(&drv->driver, &mdev->dev, DRV_BIND_DEFAULT);
if (ret)
goto out_del;

diff --git a/include/linux/device.h b/include/linux/device.h
index e270cb740b9e..1ada1093799b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -876,12 +876,24 @@ static inline void *dev_get_platdata(const struct device *dev)
return dev->platform_data;
}

+/*
+ * Driver-binding flags (for passing to device_driver_attach())
+ *
+ * DRV_BIND_DEFAULT: a default, automatic bind, e.g. as a result of a device
+ * being added for which we already have a driver, or vice
+ * versa.
+ * DRV_BIND_EXPLICIT: an explicit, userspace-requested driver bind, e.g. as a
+ * result of a write to /sys/bus/.../drivers/.../bind
+ */
+#define DRV_BIND_DEFAULT 0
+#define DRV_BIND_EXPLICIT BIT(0)
+
/*
* Manual binding of a device to driver. See drivers/base/bus.c
* for information on use.
*/
int __must_check device_driver_attach(struct device_driver *drv,
- struct device *dev);
+ struct device *dev, u32 flags);
int __must_check device_bind_driver(struct device *dev);
void device_release_driver(struct device *dev);
int __must_check device_attach(struct device *dev);
--
2.33.1