From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jike Song Subject: [RFC v2 1/4] Mediated device Core driver Date: Fri, 2 Sep 2016 16:16:09 +0800 Message-ID: <1472804172-25542-2-git-send-email-jike.song@intel.com> References: <1472804172-25542-1-git-send-email-jike.song@intel.com> Cc: kevin.tian@intel.com, guangrong.xiao@linux.intel.com, kvm@vger.kernel.org, qemu-devel@nongnu.org, zhenyuw@linux.intel.com, jike.song@intel.com, zhiyuan.lv@intel.com, pbonzini@redhat.com, bjsdjshi@linux.vnet.ibm.com, kraxel@redhat.com To: alex.williamson@redhat.com, kwankhede@nvidia.com, cjia@nvidia.com Return-path: In-Reply-To: <1472804172-25542-1-git-send-email-jike.song@intel.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+gceq-qemu-devel=gmane.org@nongnu.org Sender: "Qemu-devel" List-Id: kvm.vger.kernel.org Design for Mediated Device Driver: Main purpose of this driver is to provide a common interface for mediated device management that can be used by different drivers of different devices. This module provides a generic interface to create the mdev device, add it to the mdev bus, add it to IOMMU group and then to vfio group. Below is the high level block diagram, with Nvidia, Intel and IBM devices as example, since these are the devices which are going to actively use this module as of now. +---------------+ | | | +-----------+ | | | | | | | | | | | MDEV | | mdev_register_driver() +---------------+ | | Bus | |<------------------------------+ | | | Driver | | | | | | | +------------------------------>| vfio_mdev.ko |<-> VFIO user | | | | probe()/remove() | | APIs | | | | +---------------+ | +-----------+ | | | | MDEV CORE | | MODULE | | mdev.ko | | | | +-----------+ | mdev_register_host_device() +---------------+ | | | +<------------------------------+ | | | | | | nvidia.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | | Physical/ | | | | Host | | mdev_register_host_device() +---------------+ | | Device | |<------------------------------+ | | | Interface | | | i915.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | | | | | | | | mdev_register_host_device() +---------------+ | | | +<------------------------------+ | | | | | | ccw_device.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | +-----------+ | +---------------+ Signed-off-by: Kirti Wankhede Signed-off-by: Neo Jia Signed-off-by: Xiao Guangrong Signed-off-by: Jike Song --- drivers/vfio/Kconfig | 1 + drivers/vfio/Makefile | 1 + drivers/vfio/mdev/Kconfig | 10 ++ drivers/vfio/mdev/Makefile | 4 + drivers/vfio/mdev/mdev_core.c | 250 +++++++++++++++++++++++++++++++++++++++ drivers/vfio/mdev/mdev_driver.c | 155 ++++++++++++++++++++++++ drivers/vfio/mdev/mdev_private.h | 29 +++++ drivers/vfio/mdev/mdev_sysfs.c | 155 ++++++++++++++++++++++++ include/linux/mdev.h | 159 +++++++++++++++++++++++++ 9 files changed, 764 insertions(+) create mode 100644 drivers/vfio/mdev/Kconfig create mode 100644 drivers/vfio/mdev/Makefile create mode 100644 drivers/vfio/mdev/mdev_core.c create mode 100644 drivers/vfio/mdev/mdev_driver.c create mode 100644 drivers/vfio/mdev/mdev_private.h create mode 100644 drivers/vfio/mdev/mdev_sysfs.c create mode 100644 include/linux/mdev.h diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index da6e2ce..23eced0 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -48,4 +48,5 @@ menuconfig VFIO_NOIOMMU source "drivers/vfio/pci/Kconfig" source "drivers/vfio/platform/Kconfig" +source "drivers/vfio/mdev/Kconfig" source "virt/lib/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 7b8a31f..7c70753 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o obj-$(CONFIG_VFIO_PCI) += pci/ obj-$(CONFIG_VFIO_PLATFORM) += platform/ +obj-$(CONFIG_MDEV) += mdev/ diff --git a/drivers/vfio/mdev/Kconfig b/drivers/vfio/mdev/Kconfig new file mode 100644 index 0000000..d25439f --- /dev/null +++ b/drivers/vfio/mdev/Kconfig @@ -0,0 +1,10 @@ + +config MDEV + tristate "Mediated device driver framework" + depends on VFIO + default n + help + Provides a framework to virtualize device. + See Documentation/vfio-mediated-device.txt for more details. + + If you don't know what do here, say N. diff --git a/drivers/vfio/mdev/Makefile b/drivers/vfio/mdev/Makefile new file mode 100644 index 0000000..8bd78b5 --- /dev/null +++ b/drivers/vfio/mdev/Makefile @@ -0,0 +1,4 @@ + +mdev-y := mdev_core.o mdev_sysfs.o mdev_driver.o + +obj-$(CONFIG_MDEV) += mdev.o diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c new file mode 100644 index 0000000..cb27ccf --- /dev/null +++ b/drivers/vfio/mdev/mdev_core.c @@ -0,0 +1,250 @@ +/* + * Mediated device Core Driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * Author: + * Xiao Guangrong + * Jike Song + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "NVIDIA Corporation" +#define DRIVER_DESC "Mediated Device Core Driver" + + +static int __find_mdev_device(struct device *dev, void *data) +{ + struct mdev_device *mdev = dev_to_mdev(dev); + + return (uuid_le_cmp(mdev->uuid, *(uuid_le *)data) == 0); +} + +static struct mdev_device *find_mdev_device(struct mdev_host *host, + uuid_le uuid) +{ + struct device *dev; + + dev = device_find_child(&host->dev, &uuid, __find_mdev_device); + if (!dev) + return NULL; + + return dev_to_mdev(dev); +} + +static int mdev_device_create_ops(struct mdev_device *mdev, char *mdev_params) +{ + struct mdev_host *host = dev_to_host(mdev->dev.parent); + + return host->ops->create(mdev, mdev_params); +} + +static void mdev_device_destroy_ops(struct mdev_device *mdev) +{ + struct mdev_host *host = dev_to_host(mdev->dev.parent); + + host->ops->destroy(mdev); +} + +/* + * mdev_register_host_device : register a mdev host device + * @dev: device structure of the physical device under which the created + * host device will be. + * @ops: Parent device operation structure to be registered. + * + * Register a mdev host device as the mediator of mdev devices. + * Returns the pointer of mdev host device structure for success, NULL + * for errors. + */ +struct mdev_host *mdev_register_host_device(struct device *pdev, + const struct mdev_host_ops *ops) +{ + int rc = 0; + struct mdev_host *host; + + if (!pdev || !ops) { + dev_warn(pdev, "dev or ops is NULL\n"); + return NULL; + } + + /* check for mandatory ops */ + if (!ops->create || !ops->destroy) { + dev_warn(pdev, "create and destroy methods are necessary\n"); + return NULL; + } + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return NULL; + + host->dev.parent = pdev; + host->ops = ops; + dev_set_name(&host->dev, "mdev-host"); + + rc = device_register(&host->dev); + if (rc) + goto register_error; + + rc = mdev_create_sysfs_files(&host->dev); + if (rc) + goto add_sysfs_error; + + rc = sysfs_create_groups(&host->dev.kobj, ops->hdev_attr_groups); + if (rc) + goto add_group_error; + + dev_info(&host->dev, "mdev host device registered\n"); + return host; + +add_group_error: + mdev_remove_sysfs_files(&host->dev); + +add_sysfs_error: + device_unregister(&host->dev); + +register_error: + kfree(host); + return NULL; +} +EXPORT_SYMBOL(mdev_register_host_device); + +static int __mdev_device_destroy(struct device *dev, void *data) +{ + struct mdev_device *mdev = dev_to_mdev(dev); + + mdev_device_destroy_ops(mdev); + device_unregister(&mdev->dev); + + return 0; +} + +/* + * mdev_unregister_host_device : unregister a mdev host device + * @host: the mdev host device structure + * + * Unregister a mdev host device as the mediator + */ +void mdev_unregister_host_device(struct mdev_host *host) +{ + if (!host) + return; + + dev_info(&host->dev, "mdev host device unregistered\n"); + + mdev_remove_sysfs_files(&host->dev); + sysfs_remove_groups(&host->dev.kobj, host->ops->hdev_attr_groups); + device_for_each_child(&host->dev, NULL, __mdev_device_destroy); + device_unregister(&host->dev); +} +EXPORT_SYMBOL(mdev_unregister_host_device); + +int mdev_device_create(struct device *dev, uuid_le uuid, char *mdev_params) +{ + int ret; + struct mdev_device *mdev; + struct mdev_host *host = dev_to_host(dev); + + /* Check for duplicate */ + mdev = find_mdev_device(host, uuid); + if (mdev) { + ret = -EEXIST; + goto create_err; + } + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) { + ret = -ENOMEM; + goto create_err; + } + + memcpy(&mdev->uuid, &uuid, sizeof(uuid_le)); + + mdev->dev.parent = dev; + mdev->dev.bus = &mdev_bus_type; + mdev->dev.groups = host->ops->mdev_attr_groups; + dev_set_name(&mdev->dev, "%pUl", uuid.b); + + ret = device_register(&mdev->dev); + if (ret) { + put_device(&mdev->dev); + goto create_err; + } + + ret = mdev_device_create_ops(mdev, mdev_params); + if (ret) + goto create_failed; + + dev_dbg(&mdev->dev, "MDEV: created\n"); + + return ret; + +create_failed: + device_unregister(&mdev->dev); + +create_err: + return ret; +} + +int mdev_device_destroy(struct device *dev, uuid_le uuid) +{ + struct mdev_device *mdev; + struct mdev_host *host = dev_to_host(dev); + + mdev = find_mdev_device(host, uuid); + if (!mdev) + return -ENODEV; + + return __mdev_device_destroy(&mdev->dev, NULL); +} + +void mdev_device_supported_config(struct device *dev, char *str) +{ + struct mdev_host *host = dev_to_host(dev); + + if (host->ops->supported_config) + host->ops->supported_config(&host->dev, str); +} + +static int __init mdev_init(void) +{ + int ret; + + ret = mdev_bus_register(); + if (ret) + pr_err("failed to register mdev bus: %d\n", ret); + + return ret; +} + +static void __exit mdev_exit(void) +{ + mdev_bus_unregister(); +} + +module_init(mdev_init) +module_exit(mdev_exit) + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/mdev/mdev_driver.c b/drivers/vfio/mdev/mdev_driver.c new file mode 100644 index 0000000..d298aaf --- /dev/null +++ b/drivers/vfio/mdev/mdev_driver.c @@ -0,0 +1,155 @@ +/* + * MDEV driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "mdev_private.h" + +static int mdev_attach_iommu(struct mdev_device *mdev) +{ + int ret; + struct iommu_group *group; + + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(&mdev->dev, "MDEV: failed to allocate group!\n"); + return PTR_ERR(group); + } + + ret = iommu_group_add_device(group, &mdev->dev); + if (ret) { + dev_err(&mdev->dev, "MDEV: failed to add dev to group!\n"); + goto attach_fail; + } + + mdev->group = group; + + dev_info(&mdev->dev, "MDEV: group_id = %d\n", + iommu_group_id(group)); +attach_fail: + iommu_group_put(group); + return ret; +} + +static void mdev_detach_iommu(struct mdev_device *mdev) +{ + iommu_group_remove_device(&mdev->dev); + mdev->group = NULL; + dev_info(&mdev->dev, "MDEV: detaching iommu\n"); +} + +static int mdev_probe(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = dev_to_mdev(dev); + int ret; + + ret = mdev_attach_iommu(mdev); + if (ret) { + dev_err(dev, "Failed to attach IOMMU\n"); + return ret; + } + + if (drv && drv->probe) + ret = drv->probe(dev); + + if (ret) + mdev_detach_iommu(mdev); + + return ret; +} + +static int mdev_remove(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = dev_to_mdev(dev); + + if (drv && drv->remove) + drv->remove(dev); + + mdev_detach_iommu(mdev); + + return 0; +} + +static int mdev_online(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + + if (drv && drv->online) + return drv->online(dev); + + return 0; +} + +static int mdev_offline(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + + if (drv && drv->offline) + return drv->offline(dev); + + return 0; +} + +struct bus_type mdev_bus_type = { + .name = "mdev", + .probe = mdev_probe, + .remove = mdev_remove, + .online = mdev_online, + .offline = mdev_offline, +}; +EXPORT_SYMBOL_GPL(mdev_bus_type); + +/* + * mdev_register_driver - register a new MDEV driver + * @drv: the driver to register + * @owner: module owner of driver to be registered + * + * Returns a negative value on error, otherwise 0. + */ +int mdev_register_driver(struct mdev_driver *drv, struct module *owner) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &mdev_bus_type; + drv->driver.owner = owner; + + /* register with core */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(mdev_register_driver); + +/* + * mdev_unregister_driver - unregister MDEV driver + * @drv: the driver to unregister + * + */ +void mdev_unregister_driver(struct mdev_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(mdev_unregister_driver); + +int mdev_bus_register(void) +{ + return bus_register(&mdev_bus_type); +} + +void mdev_bus_unregister(void) +{ + bus_unregister(&mdev_bus_type); +} diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h new file mode 100644 index 0000000..f153292 --- /dev/null +++ b/drivers/vfio/mdev/mdev_private.h @@ -0,0 +1,29 @@ +/* + * Mediated device interal definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_PRIVATE_H +#define MDEV_PRIVATE_H + +int mdev_bus_register(void); +void mdev_bus_unregister(void); + +/* Function prototypes for mdev_sysfs */ +int mdev_create_sysfs_files(struct device *dev); +void mdev_remove_sysfs_files(struct device *dev); + +int mdev_device_create(struct device *dev, uuid_le uuid, char *mdev_params); +int mdev_device_destroy(struct device *dev, uuid_le uuid); +void mdev_device_supported_config(struct device *dev, char *str); + +#endif /* MDEV_PRIVATE_H */ diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c new file mode 100644 index 0000000..7d55188 --- /dev/null +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -0,0 +1,155 @@ +/* + * File attributes for Mediated devices + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * Author: + * Jike Song + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +/* Prototypes */ +static ssize_t mdev_supported_types_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static DEVICE_ATTR_RO(mdev_supported_types); + +static ssize_t mdev_create_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_WO(mdev_create); + +static ssize_t mdev_destroy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_WO(mdev_destroy); + +static const struct attribute *mdev_host_attrs[] = { + &dev_attr_mdev_supported_types.attr, + &dev_attr_mdev_create.attr, + &dev_attr_mdev_destroy.attr, + NULL, +}; + + +#define SUPPORTED_TYPE_BUFFER_LENGTH 4096 + +/* mdev host sysfs functions */ +static ssize_t mdev_supported_types_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char *str, *ptr; + ssize_t n; + + str = kzalloc(sizeof(*str) * SUPPORTED_TYPE_BUFFER_LENGTH, GFP_KERNEL); + if (!str) + return -ENOMEM; + + ptr = str; + mdev_device_supported_config(dev, str); + + n = sprintf(buf, "%s\n", str); + kfree(ptr); + + return n; +} + +static ssize_t mdev_create_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *str; + char *uuid_str, *params = NULL; + uuid_le uuid; + int ret; + + str = kstrndup(buf, count, GFP_KERNEL); + if (!str) + return -ENOMEM; + + uuid_str = strsep(&str, ":"); + if (!uuid_str) { + pr_err("mdev_create: empty UUID string %s\n", buf); + ret = -EINVAL; + goto create_error; + } + + if (str) + params = kstrdup(str, GFP_KERNEL); + + ret = uuid_le_to_bin(uuid_str, &uuid); + if (ret) { + pr_err("mdev_create: UUID parse error %s\n", buf); + goto create_error; + } + + ret = mdev_device_create(dev, uuid, params); + if (ret) + pr_err("mdev_create: Failed to create mdev device\n"); + else + ret = count; + +create_error: + kfree(params); + kfree(str); + return ret; +} + +static ssize_t mdev_destroy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *str; + uuid_le uuid; + int ret; + + str = kstrndup(buf, count, GFP_KERNEL); + if (!str) + return -ENOMEM; + + ret = uuid_le_to_bin(str, &uuid); + if (ret) { + pr_err("mdev_destroy: UUID parse error %s\n", buf); + goto destroy_error; + } + + ret = mdev_device_destroy(dev, uuid); + if (ret == 0) + ret = count; + +destroy_error: + kfree(str); + return ret; +} + +int mdev_create_sysfs_files(struct device *dev) +{ + int ret; + + ret = sysfs_create_files(&dev->kobj, mdev_host_attrs); + if (ret) + pr_err("sysfs_create_files failed: %d\n", ret); + + return ret; +} + +void mdev_remove_sysfs_files(struct device *dev) +{ + sysfs_remove_files(&dev->kobj, mdev_host_attrs); +} diff --git a/include/linux/mdev.h b/include/linux/mdev.h new file mode 100644 index 0000000..1236200 --- /dev/null +++ b/include/linux/mdev.h @@ -0,0 +1,159 @@ +/* + * Mediated device definition + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_H +#define MDEV_H + +#include + + +/* mediated device */ +struct mdev_device { + struct device dev; + struct iommu_group *group; + uuid_le uuid; + void *driver_data; +}; + +/** + * struct mdev_host_ops - Structure to be registered for each host device to + * to mdev. + * + * @owner: The module owner. + * @hdev_attr_groups: Default attributes of the host device. + * @mdev_attr_groups: Default attributes of the mdev device. + * @supported_config: Called to get information about supported types. + * @dev : device structure of host device. + * @config: should return string listing supported config + * Returns integer: success (0) or error (< 0) + * @create: Called to allocate basic resources in host device's + * driver for a particular mediated device. It is + * mandatory to provide create ops. + * @mdev: mdev_device structure on of mediated device + * that is being created + * @mdev_params: extra parameters required by host + * device's driver. + * Returns integer: success (0) or error (< 0) + * @destroy: Called to free resources in host device's driver for a + * a mediated device instance. It is mandatory to provide + * destroy ops. + * @mdev: mdev_device device structure which is being + * destroyed + * Returns integer: success (0) or error (< 0) + * If VMM is running and destroy() is called that means the + * mdev is being hotunpluged. Return error if VMM is + * running and driver doesn't support mediated device + * hotplug. + * @start: Called to initiate mediated device initialization + * process in host device's driver before VMM starts. + * @mdev: mediated device structure + * Returns integer: success (0) or error (< 0) + * @stop: Called to teardown mediated device related resources + * @mdev: mediated device structure + * Returns integer: success (0) or error (< 0) + * @read: Read emulation callback + * @mdev: mediated device structure + * @buf: read buffer + * @count: number of bytes to read + * @pos: address. + * Retuns number on bytes read on success or error. + * @write: Write emulation callback + * @mdev: mediated device structure + * @buf: write buffer + * @count: number of bytes to be written + * @pos: address. + * Retuns number on bytes written on success or error. + * @mmap: Memory Map + * @mdev: mediated device structure + * @pos: address + * @virtaddr: target user address to start at. Vendor + * driver can change if required. + * @pfn: host address of kernel memory, vendor driver + * can change if required. + * @size: size of map area, vendor driver can change the + * size of map area if desired. + * @prot: page protection flags for this mapping, vendor + * driver can change, if required. + * Returns integer: success (0) or error (< 0) + * + * Host device that support mediated device should be registered with mdev + * module with mdev_host_ops structure. + */ +struct mdev_host_ops { + struct module *owner; + const struct attribute_group **hdev_attr_groups; + const struct attribute_group **mdev_attr_groups; + + int (*supported_config)(struct device *dev, char *config); + int (*create)(struct mdev_device *mdev, char *mdev_params); + void (*destroy)(struct mdev_device *mdev); + + int (*start)(struct mdev_device *mdev); + int (*stop)(struct mdev_device *mdev); + + ssize_t (*read)(struct mdev_device *mdev, char __user *buf, + size_t count, loff_t *pos); + ssize_t (*write)(struct mdev_device *mdev, const char __user *buf, + size_t count, loff_t *pos); + int (*mmap)(struct mdev_device *mdev, struct vm_area_struct *vma); + long (*ioctl)(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg); +}; + +/* mdev host device */ +struct mdev_host { + struct device dev; + const struct mdev_host_ops *ops; +}; + +/** + * struct mdev_driver - Mediated device driver + * @name: driver name + * @probe: called when new device created + * @remove: called when device removed + * @driver: device driver structure + **/ +struct mdev_driver { + const char *name; + int (*probe)(struct device *dev); + void (*remove)(struct device *dev); + int (*online)(struct device *dev); + int (*offline)(struct device *dev); + struct device_driver driver; +}; + +static inline void *mdev_get_drvdata(struct mdev_device *mdev) +{ + return mdev->driver_data; +} + +static inline void mdev_set_drvdata(struct mdev_device *mdev, void *data) +{ + mdev->driver_data = data; +} + +extern struct bus_type mdev_bus_type; + +#define to_mdev_driver(drv) container_of(drv, struct mdev_driver, driver) +#define dev_to_host(_dev) container_of((_dev), struct mdev_host, dev) +#define dev_to_mdev(_dev) container_of((_dev), struct mdev_device, dev) + +struct mdev_host *mdev_register_host_device(struct device *dev, + const struct mdev_host_ops *ops); +void mdev_unregister_host_device(struct mdev_host *host); + +int mdev_register_driver(struct mdev_driver *drv, struct module *owner); +void mdev_unregister_driver(struct mdev_driver *drv); + +#endif /* MDEV_H */ -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52195) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bfjh9-0004Ok-2l for qemu-devel@nongnu.org; Fri, 02 Sep 2016 04:19:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bfjh3-0007Bo-MS for qemu-devel@nongnu.org; Fri, 02 Sep 2016 04:19:25 -0400 Received: from mga05.intel.com ([192.55.52.43]:48821) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bfjh3-0007Aw-62 for qemu-devel@nongnu.org; Fri, 02 Sep 2016 04:19:21 -0400 From: Jike Song Date: Fri, 2 Sep 2016 16:16:09 +0800 Message-Id: <1472804172-25542-2-git-send-email-jike.song@intel.com> In-Reply-To: <1472804172-25542-1-git-send-email-jike.song@intel.com> References: <1472804172-25542-1-git-send-email-jike.song@intel.com> Subject: [Qemu-devel] [RFC v2 1/4] Mediated device Core driver List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: alex.williamson@redhat.com, kwankhede@nvidia.com, cjia@nvidia.com Cc: qemu-devel@nongnu.org, kvm@vger.kernel.org, bjsdjshi@linux.vnet.ibm.com, kevin.tian@intel.com, guangrong.xiao@linux.intel.com, zhenyuw@linux.intel.com, zhiyuan.lv@intel.com, jike.song@intel.com, pbonzini@redhat.com, kraxel@redhat.com Design for Mediated Device Driver: Main purpose of this driver is to provide a common interface for mediated device management that can be used by different drivers of different devices. This module provides a generic interface to create the mdev device, add it to the mdev bus, add it to IOMMU group and then to vfio group. Below is the high level block diagram, with Nvidia, Intel and IBM devices as example, since these are the devices which are going to actively use this module as of now. +---------------+ | | | +-----------+ | | | | | | | | | | | MDEV | | mdev_register_driver() +---------------+ | | Bus | |<------------------------------+ | | | Driver | | | | | | | +------------------------------>| vfio_mdev.ko |<-> VFIO user | | | | probe()/remove() | | APIs | | | | +---------------+ | +-----------+ | | | | MDEV CORE | | MODULE | | mdev.ko | | | | +-----------+ | mdev_register_host_device() +---------------+ | | | +<------------------------------+ | | | | | | nvidia.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | | Physical/ | | | | Host | | mdev_register_host_device() +---------------+ | | Device | |<------------------------------+ | | | Interface | | | i915.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | | | | | | | | mdev_register_host_device() +---------------+ | | | +<------------------------------+ | | | | | | ccw_device.ko |<-> physical | | | +------------------------------>+ | device | | | | callbacks +---------------+ | +-----------+ | +---------------+ Signed-off-by: Kirti Wankhede Signed-off-by: Neo Jia Signed-off-by: Xiao Guangrong Signed-off-by: Jike Song --- drivers/vfio/Kconfig | 1 + drivers/vfio/Makefile | 1 + drivers/vfio/mdev/Kconfig | 10 ++ drivers/vfio/mdev/Makefile | 4 + drivers/vfio/mdev/mdev_core.c | 250 +++++++++++++++++++++++++++++++++++++++ drivers/vfio/mdev/mdev_driver.c | 155 ++++++++++++++++++++++++ drivers/vfio/mdev/mdev_private.h | 29 +++++ drivers/vfio/mdev/mdev_sysfs.c | 155 ++++++++++++++++++++++++ include/linux/mdev.h | 159 +++++++++++++++++++++++++ 9 files changed, 764 insertions(+) create mode 100644 drivers/vfio/mdev/Kconfig create mode 100644 drivers/vfio/mdev/Makefile create mode 100644 drivers/vfio/mdev/mdev_core.c create mode 100644 drivers/vfio/mdev/mdev_driver.c create mode 100644 drivers/vfio/mdev/mdev_private.h create mode 100644 drivers/vfio/mdev/mdev_sysfs.c create mode 100644 include/linux/mdev.h diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index da6e2ce..23eced0 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -48,4 +48,5 @@ menuconfig VFIO_NOIOMMU source "drivers/vfio/pci/Kconfig" source "drivers/vfio/platform/Kconfig" +source "drivers/vfio/mdev/Kconfig" source "virt/lib/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 7b8a31f..7c70753 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o obj-$(CONFIG_VFIO_PCI) += pci/ obj-$(CONFIG_VFIO_PLATFORM) += platform/ +obj-$(CONFIG_MDEV) += mdev/ diff --git a/drivers/vfio/mdev/Kconfig b/drivers/vfio/mdev/Kconfig new file mode 100644 index 0000000..d25439f --- /dev/null +++ b/drivers/vfio/mdev/Kconfig @@ -0,0 +1,10 @@ + +config MDEV + tristate "Mediated device driver framework" + depends on VFIO + default n + help + Provides a framework to virtualize device. + See Documentation/vfio-mediated-device.txt for more details. + + If you don't know what do here, say N. diff --git a/drivers/vfio/mdev/Makefile b/drivers/vfio/mdev/Makefile new file mode 100644 index 0000000..8bd78b5 --- /dev/null +++ b/drivers/vfio/mdev/Makefile @@ -0,0 +1,4 @@ + +mdev-y := mdev_core.o mdev_sysfs.o mdev_driver.o + +obj-$(CONFIG_MDEV) += mdev.o diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c new file mode 100644 index 0000000..cb27ccf --- /dev/null +++ b/drivers/vfio/mdev/mdev_core.c @@ -0,0 +1,250 @@ +/* + * Mediated device Core Driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * Author: + * Xiao Guangrong + * Jike Song + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "NVIDIA Corporation" +#define DRIVER_DESC "Mediated Device Core Driver" + + +static int __find_mdev_device(struct device *dev, void *data) +{ + struct mdev_device *mdev = dev_to_mdev(dev); + + return (uuid_le_cmp(mdev->uuid, *(uuid_le *)data) == 0); +} + +static struct mdev_device *find_mdev_device(struct mdev_host *host, + uuid_le uuid) +{ + struct device *dev; + + dev = device_find_child(&host->dev, &uuid, __find_mdev_device); + if (!dev) + return NULL; + + return dev_to_mdev(dev); +} + +static int mdev_device_create_ops(struct mdev_device *mdev, char *mdev_params) +{ + struct mdev_host *host = dev_to_host(mdev->dev.parent); + + return host->ops->create(mdev, mdev_params); +} + +static void mdev_device_destroy_ops(struct mdev_device *mdev) +{ + struct mdev_host *host = dev_to_host(mdev->dev.parent); + + host->ops->destroy(mdev); +} + +/* + * mdev_register_host_device : register a mdev host device + * @dev: device structure of the physical device under which the created + * host device will be. + * @ops: Parent device operation structure to be registered. + * + * Register a mdev host device as the mediator of mdev devices. + * Returns the pointer of mdev host device structure for success, NULL + * for errors. + */ +struct mdev_host *mdev_register_host_device(struct device *pdev, + const struct mdev_host_ops *ops) +{ + int rc = 0; + struct mdev_host *host; + + if (!pdev || !ops) { + dev_warn(pdev, "dev or ops is NULL\n"); + return NULL; + } + + /* check for mandatory ops */ + if (!ops->create || !ops->destroy) { + dev_warn(pdev, "create and destroy methods are necessary\n"); + return NULL; + } + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return NULL; + + host->dev.parent = pdev; + host->ops = ops; + dev_set_name(&host->dev, "mdev-host"); + + rc = device_register(&host->dev); + if (rc) + goto register_error; + + rc = mdev_create_sysfs_files(&host->dev); + if (rc) + goto add_sysfs_error; + + rc = sysfs_create_groups(&host->dev.kobj, ops->hdev_attr_groups); + if (rc) + goto add_group_error; + + dev_info(&host->dev, "mdev host device registered\n"); + return host; + +add_group_error: + mdev_remove_sysfs_files(&host->dev); + +add_sysfs_error: + device_unregister(&host->dev); + +register_error: + kfree(host); + return NULL; +} +EXPORT_SYMBOL(mdev_register_host_device); + +static int __mdev_device_destroy(struct device *dev, void *data) +{ + struct mdev_device *mdev = dev_to_mdev(dev); + + mdev_device_destroy_ops(mdev); + device_unregister(&mdev->dev); + + return 0; +} + +/* + * mdev_unregister_host_device : unregister a mdev host device + * @host: the mdev host device structure + * + * Unregister a mdev host device as the mediator + */ +void mdev_unregister_host_device(struct mdev_host *host) +{ + if (!host) + return; + + dev_info(&host->dev, "mdev host device unregistered\n"); + + mdev_remove_sysfs_files(&host->dev); + sysfs_remove_groups(&host->dev.kobj, host->ops->hdev_attr_groups); + device_for_each_child(&host->dev, NULL, __mdev_device_destroy); + device_unregister(&host->dev); +} +EXPORT_SYMBOL(mdev_unregister_host_device); + +int mdev_device_create(struct device *dev, uuid_le uuid, char *mdev_params) +{ + int ret; + struct mdev_device *mdev; + struct mdev_host *host = dev_to_host(dev); + + /* Check for duplicate */ + mdev = find_mdev_device(host, uuid); + if (mdev) { + ret = -EEXIST; + goto create_err; + } + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) { + ret = -ENOMEM; + goto create_err; + } + + memcpy(&mdev->uuid, &uuid, sizeof(uuid_le)); + + mdev->dev.parent = dev; + mdev->dev.bus = &mdev_bus_type; + mdev->dev.groups = host->ops->mdev_attr_groups; + dev_set_name(&mdev->dev, "%pUl", uuid.b); + + ret = device_register(&mdev->dev); + if (ret) { + put_device(&mdev->dev); + goto create_err; + } + + ret = mdev_device_create_ops(mdev, mdev_params); + if (ret) + goto create_failed; + + dev_dbg(&mdev->dev, "MDEV: created\n"); + + return ret; + +create_failed: + device_unregister(&mdev->dev); + +create_err: + return ret; +} + +int mdev_device_destroy(struct device *dev, uuid_le uuid) +{ + struct mdev_device *mdev; + struct mdev_host *host = dev_to_host(dev); + + mdev = find_mdev_device(host, uuid); + if (!mdev) + return -ENODEV; + + return __mdev_device_destroy(&mdev->dev, NULL); +} + +void mdev_device_supported_config(struct device *dev, char *str) +{ + struct mdev_host *host = dev_to_host(dev); + + if (host->ops->supported_config) + host->ops->supported_config(&host->dev, str); +} + +static int __init mdev_init(void) +{ + int ret; + + ret = mdev_bus_register(); + if (ret) + pr_err("failed to register mdev bus: %d\n", ret); + + return ret; +} + +static void __exit mdev_exit(void) +{ + mdev_bus_unregister(); +} + +module_init(mdev_init) +module_exit(mdev_exit) + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/mdev/mdev_driver.c b/drivers/vfio/mdev/mdev_driver.c new file mode 100644 index 0000000..d298aaf --- /dev/null +++ b/drivers/vfio/mdev/mdev_driver.c @@ -0,0 +1,155 @@ +/* + * MDEV driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "mdev_private.h" + +static int mdev_attach_iommu(struct mdev_device *mdev) +{ + int ret; + struct iommu_group *group; + + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(&mdev->dev, "MDEV: failed to allocate group!\n"); + return PTR_ERR(group); + } + + ret = iommu_group_add_device(group, &mdev->dev); + if (ret) { + dev_err(&mdev->dev, "MDEV: failed to add dev to group!\n"); + goto attach_fail; + } + + mdev->group = group; + + dev_info(&mdev->dev, "MDEV: group_id = %d\n", + iommu_group_id(group)); +attach_fail: + iommu_group_put(group); + return ret; +} + +static void mdev_detach_iommu(struct mdev_device *mdev) +{ + iommu_group_remove_device(&mdev->dev); + mdev->group = NULL; + dev_info(&mdev->dev, "MDEV: detaching iommu\n"); +} + +static int mdev_probe(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = dev_to_mdev(dev); + int ret; + + ret = mdev_attach_iommu(mdev); + if (ret) { + dev_err(dev, "Failed to attach IOMMU\n"); + return ret; + } + + if (drv && drv->probe) + ret = drv->probe(dev); + + if (ret) + mdev_detach_iommu(mdev); + + return ret; +} + +static int mdev_remove(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = dev_to_mdev(dev); + + if (drv && drv->remove) + drv->remove(dev); + + mdev_detach_iommu(mdev); + + return 0; +} + +static int mdev_online(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + + if (drv && drv->online) + return drv->online(dev); + + return 0; +} + +static int mdev_offline(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + + if (drv && drv->offline) + return drv->offline(dev); + + return 0; +} + +struct bus_type mdev_bus_type = { + .name = "mdev", + .probe = mdev_probe, + .remove = mdev_remove, + .online = mdev_online, + .offline = mdev_offline, +}; +EXPORT_SYMBOL_GPL(mdev_bus_type); + +/* + * mdev_register_driver - register a new MDEV driver + * @drv: the driver to register + * @owner: module owner of driver to be registered + * + * Returns a negative value on error, otherwise 0. + */ +int mdev_register_driver(struct mdev_driver *drv, struct module *owner) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &mdev_bus_type; + drv->driver.owner = owner; + + /* register with core */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(mdev_register_driver); + +/* + * mdev_unregister_driver - unregister MDEV driver + * @drv: the driver to unregister + * + */ +void mdev_unregister_driver(struct mdev_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(mdev_unregister_driver); + +int mdev_bus_register(void) +{ + return bus_register(&mdev_bus_type); +} + +void mdev_bus_unregister(void) +{ + bus_unregister(&mdev_bus_type); +} diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h new file mode 100644 index 0000000..f153292 --- /dev/null +++ b/drivers/vfio/mdev/mdev_private.h @@ -0,0 +1,29 @@ +/* + * Mediated device interal definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_PRIVATE_H +#define MDEV_PRIVATE_H + +int mdev_bus_register(void); +void mdev_bus_unregister(void); + +/* Function prototypes for mdev_sysfs */ +int mdev_create_sysfs_files(struct device *dev); +void mdev_remove_sysfs_files(struct device *dev); + +int mdev_device_create(struct device *dev, uuid_le uuid, char *mdev_params); +int mdev_device_destroy(struct device *dev, uuid_le uuid); +void mdev_device_supported_config(struct device *dev, char *str); + +#endif /* MDEV_PRIVATE_H */ diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c new file mode 100644 index 0000000..7d55188 --- /dev/null +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -0,0 +1,155 @@ +/* + * File attributes for Mediated devices + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * Author: + * Jike Song + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +/* Prototypes */ +static ssize_t mdev_supported_types_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static DEVICE_ATTR_RO(mdev_supported_types); + +static ssize_t mdev_create_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_WO(mdev_create); + +static ssize_t mdev_destroy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_WO(mdev_destroy); + +static const struct attribute *mdev_host_attrs[] = { + &dev_attr_mdev_supported_types.attr, + &dev_attr_mdev_create.attr, + &dev_attr_mdev_destroy.attr, + NULL, +}; + + +#define SUPPORTED_TYPE_BUFFER_LENGTH 4096 + +/* mdev host sysfs functions */ +static ssize_t mdev_supported_types_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char *str, *ptr; + ssize_t n; + + str = kzalloc(sizeof(*str) * SUPPORTED_TYPE_BUFFER_LENGTH, GFP_KERNEL); + if (!str) + return -ENOMEM; + + ptr = str; + mdev_device_supported_config(dev, str); + + n = sprintf(buf, "%s\n", str); + kfree(ptr); + + return n; +} + +static ssize_t mdev_create_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *str; + char *uuid_str, *params = NULL; + uuid_le uuid; + int ret; + + str = kstrndup(buf, count, GFP_KERNEL); + if (!str) + return -ENOMEM; + + uuid_str = strsep(&str, ":"); + if (!uuid_str) { + pr_err("mdev_create: empty UUID string %s\n", buf); + ret = -EINVAL; + goto create_error; + } + + if (str) + params = kstrdup(str, GFP_KERNEL); + + ret = uuid_le_to_bin(uuid_str, &uuid); + if (ret) { + pr_err("mdev_create: UUID parse error %s\n", buf); + goto create_error; + } + + ret = mdev_device_create(dev, uuid, params); + if (ret) + pr_err("mdev_create: Failed to create mdev device\n"); + else + ret = count; + +create_error: + kfree(params); + kfree(str); + return ret; +} + +static ssize_t mdev_destroy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *str; + uuid_le uuid; + int ret; + + str = kstrndup(buf, count, GFP_KERNEL); + if (!str) + return -ENOMEM; + + ret = uuid_le_to_bin(str, &uuid); + if (ret) { + pr_err("mdev_destroy: UUID parse error %s\n", buf); + goto destroy_error; + } + + ret = mdev_device_destroy(dev, uuid); + if (ret == 0) + ret = count; + +destroy_error: + kfree(str); + return ret; +} + +int mdev_create_sysfs_files(struct device *dev) +{ + int ret; + + ret = sysfs_create_files(&dev->kobj, mdev_host_attrs); + if (ret) + pr_err("sysfs_create_files failed: %d\n", ret); + + return ret; +} + +void mdev_remove_sysfs_files(struct device *dev) +{ + sysfs_remove_files(&dev->kobj, mdev_host_attrs); +} diff --git a/include/linux/mdev.h b/include/linux/mdev.h new file mode 100644 index 0000000..1236200 --- /dev/null +++ b/include/linux/mdev.h @@ -0,0 +1,159 @@ +/* + * Mediated device definition + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * Copyright (c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_H +#define MDEV_H + +#include + + +/* mediated device */ +struct mdev_device { + struct device dev; + struct iommu_group *group; + uuid_le uuid; + void *driver_data; +}; + +/** + * struct mdev_host_ops - Structure to be registered for each host device to + * to mdev. + * + * @owner: The module owner. + * @hdev_attr_groups: Default attributes of the host device. + * @mdev_attr_groups: Default attributes of the mdev device. + * @supported_config: Called to get information about supported types. + * @dev : device structure of host device. + * @config: should return string listing supported config + * Returns integer: success (0) or error (< 0) + * @create: Called to allocate basic resources in host device's + * driver for a particular mediated device. It is + * mandatory to provide create ops. + * @mdev: mdev_device structure on of mediated device + * that is being created + * @mdev_params: extra parameters required by host + * device's driver. + * Returns integer: success (0) or error (< 0) + * @destroy: Called to free resources in host device's driver for a + * a mediated device instance. It is mandatory to provide + * destroy ops. + * @mdev: mdev_device device structure which is being + * destroyed + * Returns integer: success (0) or error (< 0) + * If VMM is running and destroy() is called that means the + * mdev is being hotunpluged. Return error if VMM is + * running and driver doesn't support mediated device + * hotplug. + * @start: Called to initiate mediated device initialization + * process in host device's driver before VMM starts. + * @mdev: mediated device structure + * Returns integer: success (0) or error (< 0) + * @stop: Called to teardown mediated device related resources + * @mdev: mediated device structure + * Returns integer: success (0) or error (< 0) + * @read: Read emulation callback + * @mdev: mediated device structure + * @buf: read buffer + * @count: number of bytes to read + * @pos: address. + * Retuns number on bytes read on success or error. + * @write: Write emulation callback + * @mdev: mediated device structure + * @buf: write buffer + * @count: number of bytes to be written + * @pos: address. + * Retuns number on bytes written on success or error. + * @mmap: Memory Map + * @mdev: mediated device structure + * @pos: address + * @virtaddr: target user address to start at. Vendor + * driver can change if required. + * @pfn: host address of kernel memory, vendor driver + * can change if required. + * @size: size of map area, vendor driver can change the + * size of map area if desired. + * @prot: page protection flags for this mapping, vendor + * driver can change, if required. + * Returns integer: success (0) or error (< 0) + * + * Host device that support mediated device should be registered with mdev + * module with mdev_host_ops structure. + */ +struct mdev_host_ops { + struct module *owner; + const struct attribute_group **hdev_attr_groups; + const struct attribute_group **mdev_attr_groups; + + int (*supported_config)(struct device *dev, char *config); + int (*create)(struct mdev_device *mdev, char *mdev_params); + void (*destroy)(struct mdev_device *mdev); + + int (*start)(struct mdev_device *mdev); + int (*stop)(struct mdev_device *mdev); + + ssize_t (*read)(struct mdev_device *mdev, char __user *buf, + size_t count, loff_t *pos); + ssize_t (*write)(struct mdev_device *mdev, const char __user *buf, + size_t count, loff_t *pos); + int (*mmap)(struct mdev_device *mdev, struct vm_area_struct *vma); + long (*ioctl)(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg); +}; + +/* mdev host device */ +struct mdev_host { + struct device dev; + const struct mdev_host_ops *ops; +}; + +/** + * struct mdev_driver - Mediated device driver + * @name: driver name + * @probe: called when new device created + * @remove: called when device removed + * @driver: device driver structure + **/ +struct mdev_driver { + const char *name; + int (*probe)(struct device *dev); + void (*remove)(struct device *dev); + int (*online)(struct device *dev); + int (*offline)(struct device *dev); + struct device_driver driver; +}; + +static inline void *mdev_get_drvdata(struct mdev_device *mdev) +{ + return mdev->driver_data; +} + +static inline void mdev_set_drvdata(struct mdev_device *mdev, void *data) +{ + mdev->driver_data = data; +} + +extern struct bus_type mdev_bus_type; + +#define to_mdev_driver(drv) container_of(drv, struct mdev_driver, driver) +#define dev_to_host(_dev) container_of((_dev), struct mdev_host, dev) +#define dev_to_mdev(_dev) container_of((_dev), struct mdev_device, dev) + +struct mdev_host *mdev_register_host_device(struct device *dev, + const struct mdev_host_ops *ops); +void mdev_unregister_host_device(struct mdev_host *host); + +int mdev_register_driver(struct mdev_driver *drv, struct module *owner); +void mdev_unregister_driver(struct mdev_driver *drv); + +#endif /* MDEV_H */ -- 1.9.1