Commit 539fec78 authored by Stefano Garzarella's avatar Stefano Garzarella Committed by Michael S. Tsirkin
Browse files

vdpa: add driver_override support



`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: default avatarJason Wang <jasowang@redhat.com>
Acked-by: default avatarJason Wang <jasowang@redhat.com>
Signed-off-by: default avatarStefano Garzarella <sgarzare@redhat.com>
Link: https://lore.kernel.org/r/20211126164753.181829-3-sgarzare@redhat.com


Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 9c25cdeb
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -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.
+74 −0
Original line number Diff line number Diff line
@@ -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);
}

+2 −0
Original line number Diff line number Diff line
@@ -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;