Add support for the 'driver_override' attribute to Virtio devices. This allows users to control which Virtio bus driver binds to a given Virtio device. If 'driver_override' is not set, the existing behavior is preserved and devices will continue to auto-bind to the first matching Virtio bus driver. Tested with virtio blk device (virtio core and pci drivers are loaded): $ modprobe my_virtio_blk # automatically unbind from virtio_blk driver and override + bind to # my_virtio_blk driver. $ driverctl -v -b virtio set-override virtio0 my_virtio_blk In addition, driverctl saves the configuration persistently under /etc/driverctl.d/. Signed-off-by: Avraham Evdaev Signed-off-by: Max Gurtovoy --- changes from v1: - use !strcmp() to compare strings (MST) - extend commit msg with example (MST) --- drivers/virtio/virtio.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/virtio.h | 4 ++++ 2 files changed, 38 insertions(+) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index a09eb4d62f82..993dc928be49 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -61,12 +61,41 @@ static ssize_t features_show(struct device *_d, } static DEVICE_ATTR_RO(features); +static ssize_t driver_override_store(struct device *_d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtio_device *dev = dev_to_virtio(_d); + int ret; + + ret = driver_set_override(_d, &dev->driver_override, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t driver_override_show(struct device *_d, + struct device_attribute *attr, char *buf) +{ + struct virtio_device *dev = dev_to_virtio(_d); + ssize_t len; + + device_lock(_d); + len = sysfs_emit(buf, "%s\n", dev->driver_override); + device_unlock(_d); + + return len; +} +static DEVICE_ATTR_RW(driver_override); + static struct attribute *virtio_dev_attrs[] = { &dev_attr_device.attr, &dev_attr_vendor.attr, &dev_attr_status.attr, &dev_attr_modalias.attr, &dev_attr_features.attr, + &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(virtio_dev); @@ -88,6 +117,10 @@ static int virtio_dev_match(struct device *_dv, const struct device_driver *_dr) struct virtio_device *dev = dev_to_virtio(_dv); const struct virtio_device_id *ids; + /* Check override first, and if set, only use the named driver */ + if (dev->driver_override) + return !strcmp(dev->driver_override, _dr->name); + ids = drv_to_virtio(_dr)->id_table; for (i = 0; ids[i].device; i++) if (virtio_id_match(dev, &ids[i])) @@ -582,6 +615,7 @@ void unregister_virtio_device(struct virtio_device *dev) { int index = dev->index; /* save for after device release */ + kfree(dev->driver_override); device_unregister(&dev->dev); virtio_debug_device_exit(dev); ida_free(&virtio_index_ida, index); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index db31fc6f4f1f..418bb490bdc6 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -138,6 +138,9 @@ struct virtio_admin_cmd { * @config_lock: protects configuration change reporting * @vqs_list_lock: protects @vqs. * @dev: underlying device. + * @driver_override: driver name to force a match; do not set directly, + * because core frees it; use driver_set_override() to + * set or clear it. * @id: the device type identification (used to match it with a driver). * @config: the configuration ops for this device. * @vringh_config: configuration ops for host vrings. @@ -158,6 +161,7 @@ struct virtio_device { spinlock_t config_lock; spinlock_t vqs_list_lock; struct device dev; + const char *driver_override; struct virtio_device_id id; const struct virtio_config_ops *config; const struct vringh_config_ops *vringh_config; -- 2.18.1