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: 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>
This commit is contained in:
Родитель
9c25cdeb5f
Коммит
539fec78ed
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче