vfio/mdev: Allow the mdev_parent_ops to specify the device driver to bind

This allows a mdev driver to opt out of using vfio_mdev.c, instead the
driver will provide a 'struct mdev_driver' and register directly with the
driver core.

Much of mdev_parent_ops becomes unused in this mode:
- create()/remove() are done via the mdev_driver probe()/remove()
- mdev_attr_groups becomes mdev_driver driver.dev_groups
- Wrapper function callbacks are replaced with the same ones from
  struct vfio_device_ops

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Kirti Wankhede <kwankhede@nvidia.com>
Link: https://lore.kernel.org/r/20210617142218.1877096-8-hch@lst.de
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
Jason Gunthorpe 2021-06-17 16:22:15 +02:00 committed by Alex Williamson
parent af3ab3f9b9
commit 88a21f265c
4 changed files with 46 additions and 31 deletions

View file

@ -93,7 +93,7 @@ interfaces:
Registration Interface for a Mediated Bus Driver
------------------------------------------------
The registration interface for a mediated bus driver provides the following
The registration interface for a mediated device driver provides the following
structure to represent a mediated device's driver::
/*
@ -136,37 +136,26 @@ The structures in the mdev_parent_ops structure are as follows:
* dev_attr_groups: attributes of the parent device
* mdev_attr_groups: attributes of the mediated device
* supported_config: attributes to define supported configurations
* device_driver: device driver to bind for mediated device instances
The functions in the mdev_parent_ops structure are as follows:
The mdev_parent_ops also still has various functions pointers. Theses exist
for historical reasons only and shall not be used for new drivers.
* create: allocate basic resources in a driver for a mediated device
* remove: free resources in a driver when a mediated device is destroyed
(Note that mdev-core provides no implicit serialization of create/remove
callbacks per mdev parent device, per mdev type, or any other categorization.
Vendor drivers are expected to be fully asynchronous in this respect or
provide their own internal resource protection.)
The callbacks in the mdev_parent_ops structure are as follows:
* open: open callback of mediated device
* close: close callback of mediated device
* ioctl: ioctl callback of mediated device
* read : read emulation callback
* write: write emulation callback
* mmap: mmap emulation callback
A driver should use the mdev_parent_ops structure in the function call to
register itself with the mdev core driver::
When a driver wants to add the GUID creation sysfs to an existing device it has
probe'd to then it should call::
extern int mdev_register_device(struct device *dev,
const struct mdev_parent_ops *ops);
However, the mdev_parent_ops structure is not required in the function call
that a driver should use to unregister itself with the mdev core driver::
This will provide the 'mdev_supported_types/XX/create' files which can then be
used to trigger the creation of a mdev_device. The created mdev_device will be
attached to the specified driver.
When the driver needs to remove itself it calls::
extern void mdev_unregister_device(struct device *dev);
Which will unbind and destroy all the created mdevs and remove the sysfs files.
Mediated Device Management Interface Through sysfs
==================================================

View file

@ -94,9 +94,11 @@ static void mdev_device_remove_common(struct mdev_device *mdev)
mdev_remove_sysfs_files(mdev);
device_del(&mdev->dev);
lockdep_assert_held(&parent->unreg_sem);
ret = parent->ops->remove(mdev);
if (ret)
dev_err(&mdev->dev, "Remove failed: err=%d\n", ret);
if (parent->ops->remove) {
ret = parent->ops->remove(mdev);
if (ret)
dev_err(&mdev->dev, "Remove failed: err=%d\n", ret);
}
/* Balances with device_initialize() */
put_device(&mdev->dev);
@ -127,7 +129,9 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
char *envp[] = { env_string, NULL };
/* check for mandatory ops */
if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups)
if (!ops || !ops->supported_type_groups)
return -EINVAL;
if (!ops->device_driver && (!ops->create || !ops->remove))
return -EINVAL;
dev = get_device(dev);
@ -256,6 +260,7 @@ int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
int ret;
struct mdev_device *mdev, *tmp;
struct mdev_parent *parent = type->parent;
struct mdev_driver *drv = parent->ops->device_driver;
mutex_lock(&mdev_list_lock);
@ -296,14 +301,22 @@ int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
goto out_put_device;
}
ret = parent->ops->create(mdev);
if (ret)
goto out_unlock;
if (parent->ops->create) {
ret = parent->ops->create(mdev);
if (ret)
goto out_unlock;
}
ret = device_add(&mdev->dev);
if (ret)
goto out_remove;
if (!drv)
drv = &vfio_mdev_driver;
ret = device_driver_attach(&drv->driver, &mdev->dev);
if (ret)
goto out_del;
ret = mdev_create_sysfs_files(mdev);
if (ret)
goto out_del;
@ -317,7 +330,8 @@ int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
out_del:
device_del(&mdev->dev);
out_remove:
parent->ops->remove(mdev);
if (parent->ops->remove)
parent->ops->remove(mdev);
out_unlock:
up_read(&parent->unreg_sem);
out_put_device:

View file

@ -71,10 +71,20 @@ static int mdev_remove(struct device *dev)
return 0;
}
static int mdev_match(struct device *dev, struct device_driver *drv)
{
/*
* No drivers automatically match. Drivers are only bound by explicit
* device_driver_attach()
*/
return 0;
}
struct bus_type mdev_bus_type = {
.name = "mdev",
.probe = mdev_probe,
.remove = mdev_remove,
.match = mdev_match,
};
EXPORT_SYMBOL_GPL(mdev_bus_type);

View file

@ -55,6 +55,7 @@ struct device *mtype_get_parent_dev(struct mdev_type *mtype);
* register the device to mdev module.
*
* @owner: The module owner.
* @device_driver: Which device driver to probe() on newly created devices
* @dev_attr_groups: Attributes of the parent device.
* @mdev_attr_groups: Attributes of the mediated device.
* @supported_type_groups: Attributes to define supported types. It is mandatory
@ -103,6 +104,7 @@ struct device *mtype_get_parent_dev(struct mdev_type *mtype);
**/
struct mdev_parent_ops {
struct module *owner;
struct mdev_driver *device_driver;
const struct attribute_group **dev_attr_groups;
const struct attribute_group **mdev_attr_groups;
struct attribute_group **supported_type_groups;