OSDN Git Service

vdpa: add driver_override support
authorStefano Garzarella <sgarzare@redhat.com>
Fri, 26 Nov 2021 16:47:53 +0000 (17:47 +0100)
committerMichael S. Tsirkin <mst@redhat.com>
Fri, 14 Jan 2022 23:50:52 +0000 (18:50 -0500)
`driver_override` allows to control which of the vDPA bus drivers
binds to a vDPA device.

If `driver_override` is not set, the previous behaviour is followed:
devices use the first vDPA bus driver loaded (unless auto binding
is disabled).

Tested on Fedora 34 with driverctl(8):
  $ modprobe virtio-vdpa
  $ modprobe vhost-vdpa
  $ modprobe vdpa-sim-net

  $ vdpa dev add mgmtdev vdpasim_net name dev1

  # dev1 is attached to the first vDPA bus driver loaded
  $ driverctl -b vdpa list-devices
    dev1 virtio_vdpa

  $ driverctl -b vdpa set-override dev1 vhost_vdpa

  $ driverctl -b vdpa list-devices
    dev1 vhost_vdpa [*]

  Note: driverctl(8) integrates with udev so the binding is
  preserved.

Suggested-by: Jason Wang <jasowang@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Link: https://lore.kernel.org/r/20211126164753.181829-3-sgarzare@redhat.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Documentation/ABI/testing/sysfs-bus-vdpa
drivers/vdpa/vdpa.c
include/linux/vdpa.h

index 4e55761..28a6111 100644 (file)
@@ -35,3 +35,23 @@ Description:
                Writing a device name to this file will cause the driver to
                attempt to unbind from the device. This may be useful when
                overriding default bindings.
+
+What:          /sys/bus/vdpa/devices/.../driver_override
+Date:          November 2021
+Contact:       virtualization@lists.linux-foundation.org
+Description:
+               This file allows the driver for a device to be specified.
+               When specified, only a driver with a name matching the value
+               written to driver_override will have an opportunity to bind to
+               the device. The override is specified by writing a string to the
+               driver_override file (echo vhost-vdpa > driver_override) and may
+               be cleared with an empty string (echo > driver_override).
+               This returns the device to standard matching rules binding.
+               Writing to driver_override does not automatically unbind the
+               device from its current driver or make any attempt to
+               automatically load the specified driver. If no driver with a
+               matching name is currently loaded in the kernel, the device will
+               not bind to any driver. This also allows devices to opt-out of
+               driver binding using a driver_override name such as "none".
+               Only a single driver may be specified in the override, there is
+               no support for parsing delimiters.
index 09bbe53..59d0b8b 100644 (file)
@@ -52,8 +52,81 @@ static void vdpa_dev_remove(struct device *d)
                drv->remove(vdev);
 }
 
+static int vdpa_dev_match(struct device *dev, struct device_driver *drv)
+{
+       struct vdpa_device *vdev = dev_to_vdpa(dev);
+
+       /* Check override first, and if set, only use the named driver */
+       if (vdev->driver_override)
+               return strcmp(vdev->driver_override, drv->name) == 0;
+
+       /* Currently devices must be supported by all vDPA bus drivers */
+       return 1;
+}
+
+static ssize_t driver_override_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct vdpa_device *vdev = dev_to_vdpa(dev);
+       const char *driver_override, *old;
+       char *cp;
+
+       /* We need to keep extra room for a newline */
+       if (count >= (PAGE_SIZE - 1))
+               return -EINVAL;
+
+       driver_override = kstrndup(buf, count, GFP_KERNEL);
+       if (!driver_override)
+               return -ENOMEM;
+
+       cp = strchr(driver_override, '\n');
+       if (cp)
+               *cp = '\0';
+
+       device_lock(dev);
+       old = vdev->driver_override;
+       if (strlen(driver_override)) {
+               vdev->driver_override = driver_override;
+       } else {
+               kfree(driver_override);
+               vdev->driver_override = NULL;
+       }
+       device_unlock(dev);
+
+       kfree(old);
+
+       return count;
+}
+
+static ssize_t driver_override_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct vdpa_device *vdev = dev_to_vdpa(dev);
+       ssize_t len;
+
+       device_lock(dev);
+       len = snprintf(buf, PAGE_SIZE, "%s\n", vdev->driver_override);
+       device_unlock(dev);
+
+       return len;
+}
+static DEVICE_ATTR_RW(driver_override);
+
+static struct attribute *vdpa_dev_attrs[] = {
+       &dev_attr_driver_override.attr,
+       NULL,
+};
+
+static const struct attribute_group vdpa_dev_group = {
+       .attrs  = vdpa_dev_attrs,
+};
+__ATTRIBUTE_GROUPS(vdpa_dev);
+
 static struct bus_type vdpa_bus = {
        .name  = "vdpa",
+       .dev_groups = vdpa_dev_groups,
+       .match = vdpa_dev_match,
        .probe = vdpa_dev_probe,
        .remove = vdpa_dev_remove,
 };
@@ -68,6 +141,7 @@ static void vdpa_release_dev(struct device *d)
 
        ida_simple_remove(&vdpa_index_ida, vdev->index);
        mutex_destroy(&vdev->cf_mutex);
+       kfree(vdev->driver_override);
        kfree(vdev);
 }
 
index c3011cc..ae34015 100644 (file)
@@ -64,6 +64,7 @@ struct vdpa_mgmt_dev;
  * struct vdpa_device - representation of a vDPA device
  * @dev: underlying device
  * @dma_dev: the actual device that is performing DMA
+ * @driver_override: driver name to force a match
  * @config: the configuration ops for this device.
  * @cf_mutex: Protects get and set access to configuration layout.
  * @index: device index
@@ -76,6 +77,7 @@ struct vdpa_mgmt_dev;
 struct vdpa_device {
        struct device dev;
        struct device *dma_dev;
+       const char *driver_override;
        const struct vdpa_config_ops *config;
        struct mutex cf_mutex; /* Protects get/set config */
        unsigned int index;