@@ -122,6 +122,8 @@ source "drivers/uio/Kconfig"
source "drivers/vfio/Kconfig"
+source "drivers/vgpu/Kconfig"
+
source "drivers/vlynq/Kconfig"
source "drivers/virt/Kconfig"
@@ -84,6 +84,7 @@ obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
obj-$(CONFIG_UIO) += uio/
obj-$(CONFIG_VFIO) += vfio/
+obj-$(CONFIG_VGPU) += vgpu/
obj-y += cdrom/
obj-y += auxdisplay/
obj-$(CONFIG_PCCARD) += pcmcia/
@@ -947,19 +947,18 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
if (IS_ERR(data)) {
ret = PTR_ERR(data);
module_put(driver->ops->owner);
- goto skip_drivers_unlock;
+ continue;
}
ret = __vfio_container_attach_groups(container, driver, data);
if (!ret) {
container->iommu_driver = driver;
container->iommu_data = data;
+ goto skip_drivers_unlock;
} else {
driver->ops->release(data);
module_put(driver->ops->owner);
}
-
- goto skip_drivers_unlock;
}
mutex_unlock(&vfio.iommu_drivers_lock);
new file mode 100644
@@ -0,0 +1,26 @@
+
+menuconfig VGPU
+ tristate "VGPU driver framework"
+ depends on VFIO
+ select VGPU_VFIO
+ select VFIO_IOMMU_TYPE1_VGPU
+ help
+ VGPU provides a framework to virtualize GPU without SR-IOV cap
+ See Documentation/vgpu.txt for more details.
+
+ If you don't know what do here, say N.
+
+config VGPU
+ tristate
+ depends on VFIO
+ default n
+
+config VGPU_VFIO
+ tristate
+ depends on VGPU
+ default n
+
+config VFIO_IOMMU_TYPE1_VGPU
+ tristate
+ depends on VGPU_VFIO
+ default n
new file mode 100644
@@ -0,0 +1,5 @@
+
+vgpu-y := vgpu_sysfs.o vgpu_dev.o vgpu_vfio.o
+
+obj-$(CONFIG_VGPU) += vgpu.o
+obj-$(CONFIG_VFIO_IOMMU_TYPE1_VGPU) += vfio_iommu_type1_vgpu.o
new file mode 100644
@@ -0,0 +1,511 @@
+/*
+ * VGPU : IOMMU DMA mapping support for VGPU
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author: Neo Jia <cjia@nvidia.com>
+ * Kirti Wankhede <kwankhede@nvidia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+#include <linux/vfio.h>
+#include <linux/iommu.h>
+#include <linux/vgpu.h>
+
+#include "vgpu_private.h"
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "NVIDIA Corporation"
+#define DRIVER_DESC "VGPU Type1 IOMMU driver for VFIO"
+
+// VFIO structures
+
+struct vfio_iommu_vgpu {
+ struct mutex lock;
+ struct iommu_group *group;
+ struct vgpu_device *vgpu_dev;
+ struct rb_root dma_list;
+ struct mm_struct * vm_mm;
+};
+
+struct vgpu_vfio_dma {
+ struct rb_node node;
+ dma_addr_t iova;
+ unsigned long vaddr;
+ size_t size;
+ int prot;
+};
+
+/*
+ * VGPU VFIO FOPs definition
+ *
+ */
+
+/*
+ * Duplicated from vfio_link_dma, just quick hack ... should
+ * reuse code later
+ */
+
+static void vgpu_link_dma(struct vfio_iommu_vgpu *iommu,
+ struct vgpu_vfio_dma *new)
+{
+ struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL;
+ struct vgpu_vfio_dma *dma;
+
+ while (*link) {
+ parent = *link;
+ dma = rb_entry(parent, struct vgpu_vfio_dma, node);
+
+ if (new->iova + new->size <= dma->iova)
+ link = &(*link)->rb_left;
+ else
+ link = &(*link)->rb_right;
+ }
+
+ rb_link_node(&new->node, parent, link);
+ rb_insert_color(&new->node, &iommu->dma_list);
+}
+
+static struct vgpu_vfio_dma *vgpu_find_dma(struct vfio_iommu_vgpu *iommu,
+ dma_addr_t start, size_t size)
+{
+ struct rb_node *node = iommu->dma_list.rb_node;
+
+ while (node) {
+ struct vgpu_vfio_dma *dma = rb_entry(node, struct vgpu_vfio_dma, node);
+
+ if (start + size <= dma->iova)
+ node = node->rb_left;
+ else if (start >= dma->iova + dma->size)
+ node = node->rb_right;
+ else
+ return dma;
+ }
+
+ return NULL;
+}
+
+static void vgpu_unlink_dma(struct vfio_iommu_vgpu *iommu, struct vgpu_vfio_dma *old)
+{
+ rb_erase(&old->node, &iommu->dma_list);
+}
+
+static void vgpu_dump_dma(struct vfio_iommu_vgpu *iommu)
+{
+ struct vgpu_vfio_dma *c, *n;
+ uint32_t i = 0;
+
+ rbtree_postorder_for_each_entry_safe(c, n, &iommu->dma_list, node)
+ printk(KERN_INFO "%s: dma[%d] iova:0x%llx, vaddr:0x%lx, size:0x%lx\n",
+ __FUNCTION__, i++, c->iova, c->vaddr, c->size);
+}
+
+static int vgpu_dma_do_track(struct vfio_iommu_vgpu * vgpu_iommu,
+ struct vfio_iommu_type1_dma_map *map)
+{
+ dma_addr_t iova = map->iova;
+ unsigned long vaddr = map->vaddr;
+ int ret = 0, prot = 0;
+ struct vgpu_vfio_dma *vgpu_dma;
+
+ mutex_lock(&vgpu_iommu->lock);
+
+ if (vgpu_find_dma(vgpu_iommu, map->iova, map->size)) {
+ mutex_unlock(&vgpu_iommu->lock);
+ return -EEXIST;
+ }
+
+ vgpu_dma = kzalloc(sizeof(*vgpu_dma), GFP_KERNEL);
+
+ if (!vgpu_dma) {
+ mutex_unlock(&vgpu_iommu->lock);
+ return -ENOMEM;
+ }
+
+ vgpu_dma->iova = iova;
+ vgpu_dma->vaddr = vaddr;
+ vgpu_dma->prot = prot;
+ vgpu_dma->size = map->size;
+
+ vgpu_link_dma(vgpu_iommu, vgpu_dma);
+
+ mutex_unlock(&vgpu_iommu->lock);
+ return ret;
+}
+
+static int vgpu_dma_do_untrack(struct vfio_iommu_vgpu * vgpu_iommu,
+ struct vfio_iommu_type1_dma_unmap *unmap)
+{
+ struct vgpu_vfio_dma *vgpu_dma;
+ size_t unmapped = 0;
+ int ret = 0;
+
+ mutex_lock(&vgpu_iommu->lock);
+
+ vgpu_dma = vgpu_find_dma(vgpu_iommu, unmap->iova, 0);
+ if (vgpu_dma && vgpu_dma->iova != unmap->iova) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ vgpu_dma = vgpu_find_dma(vgpu_iommu, unmap->iova + unmap->size - 1, 0);
+ if (vgpu_dma && vgpu_dma->iova + vgpu_dma->size != unmap->iova + unmap->size) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ while (( vgpu_dma = vgpu_find_dma(vgpu_iommu, unmap->iova, unmap->size))) {
+ unmapped += vgpu_dma->size;
+ vgpu_unlink_dma(vgpu_iommu, vgpu_dma);
+ }
+
+unlock:
+ mutex_unlock(&vgpu_iommu->lock);
+ unmap->size = unmapped;
+
+ return ret;
+}
+
+/* Ugly hack to quickly test single deivce ... */
+
+static struct vfio_iommu_vgpu *_local_iommu = NULL;
+
+int vgpu_map_virtual_bar
+(
+ uint64_t virt_bar_addr,
+ uint64_t phys_bar_addr,
+ uint32_t len,
+ uint32_t flags
+)
+{
+ struct vfio_iommu_vgpu *vgpu_iommu = _local_iommu;
+ unsigned long remote_vaddr = 0;
+ struct vgpu_vfio_dma *vgpu_dma = NULL;
+ struct vm_area_struct *remote_vma = NULL;
+ struct mm_struct *mm = vgpu_iommu->vm_mm;
+ int ret = 0;
+
+ printk(KERN_INFO "%s: >>>>\n", __FUNCTION__);
+
+ mutex_lock(&vgpu_iommu->lock);
+
+ vgpu_dump_dma(vgpu_iommu);
+
+ down_write(&mm->mmap_sem);
+
+ vgpu_dma = vgpu_find_dma(vgpu_iommu, virt_bar_addr, len /* size */);
+ if (!vgpu_dma) {
+ printk(KERN_INFO "%s: fail locate guest physical:0x%llx\n",
+ __FUNCTION__, virt_bar_addr);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ remote_vaddr = vgpu_dma->vaddr + virt_bar_addr - vgpu_dma->iova;
+
+ remote_vma = find_vma(mm, remote_vaddr);
+
+ if (remote_vma == NULL) {
+ printk(KERN_INFO "%s: fail locate vma, physical addr:0x%llx\n",
+ __FUNCTION__, virt_bar_addr);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ else {
+ printk(KERN_INFO "%s: locate vma, addr:0x%lx\n",
+ __FUNCTION__, remote_vma->vm_start);
+ }
+
+ remote_vma->vm_page_prot = pgprot_noncached(remote_vma->vm_page_prot);
+
+ remote_vma->vm_pgoff = phys_bar_addr >> PAGE_SHIFT;
+
+ ret = remap_pfn_range(remote_vma, virt_bar_addr, remote_vma->vm_pgoff,
+ len, remote_vma->vm_page_prot);
+
+ if (ret) {
+ printk(KERN_INFO "%s: fail to remap vma:%d\n", __FUNCTION__, ret);
+ goto unlock;
+ }
+
+unlock:
+
+ up_write(&mm->mmap_sem);
+ mutex_unlock(&vgpu_iommu->lock);
+ printk(KERN_INFO "%s: <<<<\n", __FUNCTION__);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(vgpu_map_virtual_bar);
+
+int vgpu_dma_do_translate(dma_addr_t *gfn_buffer, uint32_t count)
+{
+ int i = 0, ret = 0, prot = 0;
+ unsigned long remote_vaddr = 0, pfn = 0;
+ struct vfio_iommu_vgpu *vgpu_iommu = _local_iommu;
+ struct vgpu_vfio_dma *vgpu_dma;
+ struct page *page[1];
+ // unsigned long * addr = NULL;
+ struct mm_struct *mm = vgpu_iommu->vm_mm;
+
+ prot = IOMMU_READ | IOMMU_WRITE;
+
+ printk(KERN_INFO "%s: >>>>\n", __FUNCTION__);
+
+ mutex_lock(&vgpu_iommu->lock);
+
+ vgpu_dump_dma(vgpu_iommu);
+
+ for (i = 0; i < count; i++) {
+ dma_addr_t iova = gfn_buffer[i] << PAGE_SHIFT;
+ vgpu_dma = vgpu_find_dma(vgpu_iommu, iova, 0 /* size */);
+
+ if (!vgpu_dma) {
+ printk(KERN_INFO "%s: fail locate iova[%d]:0x%llx\n", __FUNCTION__, i, iova);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ remote_vaddr = vgpu_dma->vaddr + iova - vgpu_dma->iova;
+ printk(KERN_INFO "%s: find dma iova[%d]:0x%llx, vaddr:0x%lx, size:0x%lx, remote_vaddr:0x%lx\n",
+ __FUNCTION__, i, vgpu_dma->iova,
+ vgpu_dma->vaddr, vgpu_dma->size, remote_vaddr);
+
+ if (get_user_pages_unlocked(NULL, mm, remote_vaddr, 1, 1, 0, page) == 1) {
+ pfn = page_to_pfn(page[0]);
+ printk(KERN_INFO "%s: pfn[%d]:0x%lx\n", __FUNCTION__, i, pfn);
+ // addr = vmap(page, 1, VM_MAP, PAGE_KERNEL);
+ }
+ else {
+ printk(KERN_INFO "%s: fail to pin pfn[%d]\n", __FUNCTION__, i);
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ gfn_buffer[i] = pfn;
+ // vunmap(addr);
+
+ }
+
+unlock:
+ mutex_unlock(&vgpu_iommu->lock);
+ printk(KERN_INFO "%s: <<<<\n", __FUNCTION__);
+ return ret;
+}
+
+EXPORT_SYMBOL(vgpu_dma_do_translate);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void *vfio_iommu_vgpu_open(unsigned long arg)
+{
+ struct vfio_iommu_vgpu *iommu;
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+
+ if (!iommu)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&iommu->lock);
+
+ printk(KERN_INFO "%s", __FUNCTION__);
+
+ /* TODO: Keep track the v2 vs. v1, for now only assume
+ * we are v2 due to QEMU code */
+ _local_iommu = iommu;
+ return iommu;
+}
+
+static void vfio_iommu_vgpu_release(void *iommu_data)
+{
+ struct vfio_iommu_vgpu *iommu = iommu_data;
+ kfree(iommu);
+ printk(KERN_INFO "%s", __FUNCTION__);
+}
+
+static long vfio_iommu_vgpu_ioctl(void *iommu_data,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ unsigned long minsz;
+ struct vfio_iommu_vgpu *vgpu_iommu = iommu_data;
+
+ switch (cmd) {
+ case VFIO_CHECK_EXTENSION:
+ {
+ if ((arg == VFIO_TYPE1_IOMMU) || (arg == VFIO_TYPE1v2_IOMMU))
+ return 1;
+ else
+ return 0;
+ }
+
+ case VFIO_IOMMU_GET_INFO:
+ {
+ struct vfio_iommu_type1_info info;
+ minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = 0;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+ }
+ case VFIO_IOMMU_MAP_DMA:
+ {
+ // TODO
+ struct vfio_iommu_type1_dma_map map;
+ minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
+
+ if (copy_from_user(&map, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (map.argsz < minsz)
+ return -EINVAL;
+
+ printk(KERN_INFO "VGPU-IOMMU:MAP_DMA flags:%d, vaddr:0x%llx, iova:0x%llx, size:0x%llx\n",
+ map.flags, map.vaddr, map.iova, map.size);
+
+ /*
+ * TODO: Tracking code is mostly duplicated from TYPE1 IOMMU, ideally,
+ * this should be merged into one single file and reuse data
+ * structure
+ *
+ */
+ ret = vgpu_dma_do_track(vgpu_iommu, &map);
+ break;
+ }
+ case VFIO_IOMMU_UNMAP_DMA:
+ {
+ // TODO
+ struct vfio_iommu_type1_dma_unmap unmap;
+
+ minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
+
+ if (copy_from_user(&unmap, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (unmap.argsz < minsz)
+ return -EINVAL;
+
+ ret = vgpu_dma_do_untrack(vgpu_iommu, &unmap);
+ break;
+ }
+ default:
+ {
+ printk(KERN_INFO "%s cmd default ", __FUNCTION__);
+ ret = -ENOTTY;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static int vfio_iommu_vgpu_attach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu_vgpu *iommu = iommu_data;
+ struct vgpu_device *vgpu_dev = NULL;
+
+ printk(KERN_INFO "%s", __FUNCTION__);
+
+ vgpu_dev = get_vgpu_device_from_group(iommu_group);
+ if (vgpu_dev) {
+ iommu->vgpu_dev = vgpu_dev;
+ iommu->group = iommu_group;
+
+ /* IOMMU shares the same life cylce as VM MM */
+ iommu->vm_mm = current->mm;
+
+ printk(KERN_INFO "%s index %d", __FUNCTION__, vgpu_dev->minor);
+ return 0;
+ }
+ iommu->group = iommu_group;
+ return 1;
+}
+
+static void vfio_iommu_vgpu_detach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu_vgpu *iommu = iommu_data;
+
+ printk(KERN_INFO "%s", __FUNCTION__);
+ iommu->vm_mm = NULL;
+ iommu->group = NULL;
+
+ return;
+}
+
+
+static const struct vfio_iommu_driver_ops vfio_iommu_vgpu_driver_ops = {
+ .name = "vgpu_vfio",
+ .owner = THIS_MODULE,
+ .open = vfio_iommu_vgpu_open,
+ .release = vfio_iommu_vgpu_release,
+ .ioctl = vfio_iommu_vgpu_ioctl,
+ .attach_group = vfio_iommu_vgpu_attach_group,
+ .detach_group = vfio_iommu_vgpu_detach_group,
+};
+
+
+int vgpu_vfio_iommu_init(void)
+{
+ int rc = vfio_register_iommu_driver(&vfio_iommu_vgpu_driver_ops);
+
+ printk(KERN_INFO "%s\n", __FUNCTION__);
+ if (rc < 0) {
+ printk(KERN_ERR "Error: failed to register vfio iommu, err:%d\n", rc);
+ }
+
+ return rc;
+}
+
+void vgpu_vfio_iommu_exit(void)
+{
+ // unregister vgpu_vfio driver
+ vfio_unregister_iommu_driver(&vfio_iommu_vgpu_driver_ops);
+ printk(KERN_INFO "%s\n", __FUNCTION__);
+}
+
+
+module_init(vgpu_vfio_iommu_init);
+module_exit(vgpu_vfio_iommu_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
new file mode 100644
@@ -0,0 +1,550 @@
+/*
+ * VGPU core
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author: Neo Jia <cjia@nvidia.com>
+ * Kirti Wankhede <kwankhede@nvidia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+#include <linux/vfio.h>
+#include <linux/iommu.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <linux/vgpu.h>
+
+#include "vgpu_private.h"
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "NVIDIA Corporation"
+#define DRIVER_DESC "VGPU driver"
+
+/*
+ * #defines
+ */
+
+#define VGPU_CLASS_NAME "vgpu"
+
+#define VGPU_DEV_NAME "vgpu"
+
+// TODO remove these defines
+// minor number reserved for control device
+#define VGPU_CONTROL_DEVICE 0
+
+#define VGPU_CONTROL_DEVICE_NAME "vgpuctl"
+
+/*
+ * Global Structures
+ */
+
+static struct vgpu {
+ dev_t vgpu_devt;
+ struct class *class;
+ struct cdev vgpu_cdev;
+ struct list_head vgpu_devices_list; // Head entry for the doubly linked vgpu_device list
+ struct mutex vgpu_devices_lock;
+ struct idr vgpu_idr;
+ struct list_head gpu_devices_list;
+ struct mutex gpu_devices_lock;
+} vgpu;
+
+
+/*
+ * Function prototypes
+ */
+
+static void vgpu_device_destroy(struct vgpu_device *vgpu_dev);
+
+unsigned int vgpu_poll(struct file *file, poll_table *wait);
+long vgpu_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long i_arg);
+int vgpu_mmap(struct file *file, struct vm_area_struct *vma);
+
+int vgpu_open(struct inode *inode, struct file *file);
+int vgpu_close(struct inode *inode, struct file *file);
+ssize_t vgpu_read(struct file *file, char __user * buf,
+ size_t len, loff_t * ppos);
+ssize_t vgpu_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos);
+
+/*
+ * Functions
+ */
+
+struct vgpu_device *get_vgpu_device_from_group(struct iommu_group *group)
+{
+
+ struct vgpu_device *vdev = NULL;
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_for_each_entry(vdev, &vgpu.vgpu_devices_list, list) {
+ if (vdev->group) {
+ if (iommu_group_id(vdev->group) == iommu_group_id(group)) {
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return vdev;
+ }
+ }
+ }
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(get_vgpu_device_from_group);
+
+int vgpu_register_device(struct pci_dev *dev, const struct gpu_device_ops *ops)
+{
+ int ret = 0;
+ struct gpu_device *gpu_dev, *tmp;
+
+ if (!dev)
+ return -EINVAL;
+
+ gpu_dev = kzalloc(sizeof(*gpu_dev), GFP_KERNEL);
+ if (!gpu_dev)
+ return -ENOMEM;
+
+ gpu_dev->dev = dev;
+ gpu_dev->ops = ops;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+
+ /* Check for duplicates */
+ list_for_each_entry(tmp, &vgpu.gpu_devices_list, gpu_next) {
+ if (tmp->dev == dev) {
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ kfree(gpu_dev);
+ return -EINVAL;
+ }
+ }
+
+ ret = vgpu_create_pci_device_files(dev);
+ if (ret) {
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ kfree(gpu_dev);
+ return ret;
+ }
+ list_add(&gpu_dev->gpu_next, &vgpu.gpu_devices_list);
+
+ printk(KERN_INFO "VGPU: Registered dev 0x%x 0x%x, class 0x%x\n", dev->vendor, dev->device, dev->class);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(vgpu_register_device);
+
+void vgpu_unregister_device(struct pci_dev *dev)
+{
+ struct gpu_device *gpu_dev;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ list_for_each_entry(gpu_dev, &vgpu.gpu_devices_list, gpu_next) {
+ if (gpu_dev->dev == dev) {
+ printk(KERN_INFO "VGPU: Unregistered dev 0x%x 0x%x, class 0x%x\n", dev->vendor, dev->device, dev->class);
+ vgpu_remove_pci_device_files(dev);
+ list_del(&gpu_dev->gpu_next);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ kfree(gpu_dev);
+ return;
+ }
+ }
+ mutex_unlock(&vgpu.gpu_devices_lock);
+}
+EXPORT_SYMBOL(vgpu_unregister_device);
+
+
+/*
+ * Static functions
+ */
+
+static struct file_operations vgpu_fops = {
+ .owner = THIS_MODULE,
+};
+
+static void vgpu_device_destroy(struct vgpu_device *vgpu_dev)
+{
+ if (vgpu_dev->dev) {
+ device_destroy(vgpu.class, vgpu_dev->dev->devt);
+ vgpu_dev->dev = NULL;
+ }
+}
+
+/*
+ * Helper Functions
+ */
+
+static struct vgpu_device *vgpu_device_alloc(uuid_le uuid, int instance, char *name)
+{
+ struct vgpu_device *vgpu_dev = NULL;
+
+ vgpu_dev = kzalloc(sizeof(*vgpu_dev), GFP_KERNEL);
+ if (!vgpu_dev)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&vgpu_dev->kref);
+ memcpy(&vgpu_dev->vm_uuid, &uuid, sizeof(uuid_le));
+ vgpu_dev->vgpu_instance = instance;
+ strcpy(vgpu_dev->dev_name, name);
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_add(&vgpu_dev->list, &vgpu.vgpu_devices_list);
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+
+ return vgpu_dev;
+}
+
+static void vgpu_device_free(struct vgpu_device *vgpu_dev)
+{
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_del(&vgpu_dev->list);
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ kfree(vgpu_dev);
+}
+
+struct vgpu_device *vgpu_drv_get_vgpu_device_by_uuid(uuid_le uuid, int instance)
+{
+ struct vgpu_device *vdev = NULL;
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_for_each_entry(vdev, &vgpu.vgpu_devices_list, list) {
+ if ((uuid_le_cmp(vdev->vm_uuid, uuid) == 0) &&
+ (vdev->vgpu_instance == instance)) {
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return vdev;
+ }
+ }
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return NULL;
+}
+
+struct vgpu_device *find_vgpu_device(struct device *dev)
+{
+ struct vgpu_device *vdev = NULL;
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_for_each_entry(vdev, &vgpu.vgpu_devices_list, list) {
+ if (vdev->dev == dev) {
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return vdev;
+ }
+ }
+
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return NULL;
+}
+
+int create_vgpu_device(struct pci_dev *pdev, uuid_le vm_uuid, uint32_t instance, uint32_t vgpu_id)
+{
+ int minor;
+ char name[64];
+ int numChar = 0;
+ int retval = 0;
+
+ struct iommu_group *group = NULL;
+ struct device *dev = NULL;
+ struct vgpu_device *vgpu_dev = NULL;
+
+ struct gpu_device *gpu_dev;
+
+ printk(KERN_INFO "VGPU: %s: device ", __FUNCTION__);
+
+ numChar = sprintf(name, "%pUb-%d", vm_uuid.b, instance);
+ name[numChar] = '\0';
+
+ vgpu_dev = vgpu_device_alloc(vm_uuid, instance, name);
+ if (IS_ERR(vgpu_dev)) {
+ return PTR_ERR(vgpu_dev);
+ }
+
+ // check if VM device is present
+ // if not present, create with devt=0 and parent=NULL
+ // create device for instance with devt= MKDEV(vgpu.major, minor)
+ // and parent=VM device
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+
+ vgpu_dev->vgpu_id = vgpu_id;
+
+ // TODO on removing control device change the 3rd parameter to 0
+ minor = idr_alloc(&vgpu.vgpu_idr, vgpu_dev, 1, MINORMASK + 1, GFP_KERNEL);
+ if (minor < 0) {
+ retval = minor;
+ goto create_failed;
+ }
+
+ dev = device_create(vgpu.class, NULL, MKDEV(MAJOR(vgpu.vgpu_devt), minor), NULL, "%s", name);
+ if (IS_ERR(dev)) {
+ retval = PTR_ERR(dev);
+ goto create_failed1;
+ }
+
+ vgpu_dev->dev = dev;
+ vgpu_dev->minor = minor;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ list_for_each_entry(gpu_dev, &vgpu.gpu_devices_list, gpu_next) {
+ if (gpu_dev->dev == pdev) {
+ vgpu_dev->gpu_dev = gpu_dev;
+ if (gpu_dev->ops->vgpu_create) {
+ retval = gpu_dev->ops->vgpu_create(pdev, vgpu_dev->vm_uuid,
+ instance, vgpu_id);
+ if (retval)
+ {
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ goto create_failed2;
+ }
+ }
+ break;
+ }
+ }
+ mutex_unlock(&vgpu.gpu_devices_lock);
+
+ if (!vgpu_dev->gpu_dev) {
+ retval = -EINVAL;
+ goto create_failed2;
+ }
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+
+ printk(KERN_INFO "UUID %pUb \n", vgpu_dev->vm_uuid.b);
+
+ group = iommu_group_alloc();
+ if (IS_ERR(group)) {
+ printk(KERN_ERR "VGPU: failed to allocate group!\n");
+ retval = PTR_ERR(group);
+ goto create_failed2;
+ }
+
+ retval = iommu_group_add_device(group, dev);
+ if (retval) {
+ printk(KERN_ERR "VGPU: failed to add dev to group!\n");
+ iommu_group_put(group);
+ goto create_failed2;
+ }
+
+ retval = vgpu_group_init(vgpu_dev, group);
+ if (retval) {
+ printk(KERN_ERR "VGPU: failed vgpu_group_init \n");
+ iommu_group_put(group);
+ iommu_group_remove_device(dev);
+ goto create_failed2;
+ }
+
+ vgpu_dev->group = group;
+ printk(KERN_INFO "VGPU: group_id = %d \n", iommu_group_id(group));
+
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ return retval;
+
+create_failed2:
+ vgpu_device_destroy(vgpu_dev);
+
+create_failed1:
+ idr_remove(&vgpu.vgpu_idr, minor);
+
+create_failed:
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ vgpu_device_free(vgpu_dev);
+
+ return retval;
+}
+
+void destroy_vgpu_device(struct vgpu_device *vgpu_dev)
+{
+ struct device *dev = vgpu_dev->dev;
+
+ if (!dev) {
+ return;
+ }
+
+ printk(KERN_INFO "VGPU: destroying device %s ", vgpu_dev->dev_name);
+ if (vgpu_dev->gpu_dev->ops->vgpu_destroy) {
+ int retval = 0;
+ retval = vgpu_dev->gpu_dev->ops->vgpu_destroy(vgpu_dev->gpu_dev->dev,
+ vgpu_dev->vm_uuid,
+ vgpu_dev->vgpu_instance);
+ /* if vendor driver doesn't return success that means vendor driver doesn't
+ * support hot-unplug */
+ if (retval)
+ return;
+ }
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+
+ vgpu_group_free(vgpu_dev);
+ iommu_group_put(dev->iommu_group);
+ iommu_group_remove_device(dev);
+ vgpu_device_destroy(vgpu_dev);
+ idr_remove(&vgpu.vgpu_idr, vgpu_dev->minor);
+
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ vgpu_device_free(vgpu_dev);
+}
+
+void destroy_vgpu_device_by_uuid(uuid_le uuid, int instance)
+{
+ struct vgpu_device *vdev, *vgpu_dev = NULL;
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+
+ // search VGPU device
+ list_for_each_entry(vdev, &vgpu.vgpu_devices_list, list) {
+ if ((uuid_le_cmp(vdev->vm_uuid, uuid) == 0) &&
+ (vdev->vgpu_instance == instance)) {
+ vgpu_dev = vdev;
+ break;
+ }
+ }
+
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ if (vgpu_dev)
+ destroy_vgpu_device(vgpu_dev);
+}
+
+void get_vgpu_supported_types(struct device *dev, char *str)
+{
+ struct gpu_device *gpu_dev;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ list_for_each_entry(gpu_dev, &vgpu.gpu_devices_list, gpu_next) {
+ if (&gpu_dev->dev->dev == dev) {
+ if (gpu_dev->ops->vgpu_supported_config)
+ gpu_dev->ops->vgpu_supported_config(gpu_dev->dev, str);
+ break;
+ }
+ }
+ mutex_unlock(&vgpu.gpu_devices_lock);
+}
+
+int vgpu_start_callback(struct vgpu_device *vgpu_dev)
+{
+ int ret = 0;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ if (vgpu_dev->gpu_dev->ops->vgpu_start)
+ ret = vgpu_dev->gpu_dev->ops->vgpu_start(vgpu_dev->vm_uuid);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ return ret;
+}
+
+int vgpu_shutdown_callback(struct vgpu_device *vgpu_dev)
+{
+ int ret = 0;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ if (vgpu_dev->gpu_dev->ops->vgpu_shutdown)
+ ret = vgpu_dev->gpu_dev->ops->vgpu_shutdown(vgpu_dev->vm_uuid);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ return ret;
+}
+
+int vgpu_set_irqs_callback(struct vgpu_device *vgpu_dev, uint32_t flags,
+ unsigned index, unsigned start, unsigned count,
+ void *data)
+{
+ int ret = 0;
+
+ mutex_lock(&vgpu.gpu_devices_lock);
+ if (vgpu_dev->gpu_dev->ops->vgpu_set_irqs)
+ ret = vgpu_dev->gpu_dev->ops->vgpu_set_irqs(vgpu_dev, flags,
+ index, start, count, data);
+ mutex_unlock(&vgpu.gpu_devices_lock);
+ return ret;
+}
+
+char *vgpu_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "vgpu/%s", dev_name(dev));
+}
+
+static struct class vgpu_class = {
+ .name = VGPU_CLASS_NAME,
+ .owner = THIS_MODULE,
+ .class_attrs = vgpu_class_attrs,
+ .dev_groups = vgpu_dev_groups,
+ .devnode = vgpu_devnode,
+};
+
+static int __init vgpu_init(void)
+{
+ int rc = 0;
+
+ memset(&vgpu, 0 , sizeof(vgpu));
+
+ idr_init(&vgpu.vgpu_idr);
+ mutex_init(&vgpu.vgpu_devices_lock);
+ INIT_LIST_HEAD(&vgpu.vgpu_devices_list);
+ mutex_init(&vgpu.gpu_devices_lock);
+ INIT_LIST_HEAD(&vgpu.gpu_devices_list);
+
+ // get major number from kernel
+ rc = alloc_chrdev_region(&vgpu.vgpu_devt, 0, MINORMASK, VGPU_DEV_NAME);
+
+ if (rc < 0) {
+ printk(KERN_ERR "Error: failed to register vgpu drv, err:%d\n", rc);
+ return rc;
+ }
+
+ cdev_init(&vgpu.vgpu_cdev, &vgpu_fops);
+ cdev_add(&vgpu.vgpu_cdev, vgpu.vgpu_devt, MINORMASK);
+
+ printk(KERN_ALERT "major_number:%d is allocated for vgpu\n", MAJOR(vgpu.vgpu_devt));
+
+ rc = class_register(&vgpu_class);
+ if (rc < 0) {
+ printk(KERN_ERR "Error: failed to register vgpu class\n");
+ goto failed1;
+ }
+
+ vgpu.class = &vgpu_class;
+
+ return rc;
+
+failed1:
+ cdev_del(&vgpu.vgpu_cdev);
+ unregister_chrdev_region(vgpu.vgpu_devt, MINORMASK);
+
+ return rc;
+}
+
+static void __exit vgpu_exit(void)
+{
+ // TODO: Release all unclosed fd
+ struct vgpu_device *vdev = NULL, *tmp;
+
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ list_for_each_entry_safe(vdev, tmp, &vgpu.vgpu_devices_list, list) {
+ printk(KERN_INFO "VGPU: exit destroying device %s ", vdev->dev_name);
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+ destroy_vgpu_device(vdev);
+ mutex_lock(&vgpu.vgpu_devices_lock);
+ }
+ mutex_unlock(&vgpu.vgpu_devices_lock);
+
+ idr_destroy(&vgpu.vgpu_idr);
+ cdev_del(&vgpu.vgpu_cdev);
+ unregister_chrdev_region(vgpu.vgpu_devt, MINORMASK);
+ class_destroy(vgpu.class);
+ vgpu.class = NULL;
+}
+
+module_init(vgpu_init)
+module_exit(vgpu_exit)
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
new file mode 100644
@@ -0,0 +1,47 @@
+/*
+ * VGPU interal definition
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author:
+ *
+ * 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 VGPU_PRIVATE_H
+#define VGPU_PRIVATE_H
+
+int vgpu_group_init(struct vgpu_device *vgpu_dev, struct iommu_group * group);
+
+int vgpu_group_free(struct vgpu_device *vgpu_dev);
+
+struct vgpu_device *find_vgpu_device(struct device *dev);
+
+struct vgpu_device *vgpu_drv_get_vgpu_device_by_uuid(uuid_le uuid, int instance);
+
+int create_vgpu_device(struct pci_dev *pdev, uuid_le vm_uuid, uint32_t instance, uint32_t vgpu_id);
+void destroy_vgpu_device(struct vgpu_device *vgpu_dev);
+void destroy_vgpu_device_by_uuid(uuid_le uuid, int instance);
+
+/* Function prototypes for vgpu_sysfs */
+
+extern struct class_attribute vgpu_class_attrs[];
+extern const struct attribute_group *vgpu_dev_groups[];
+
+int vgpu_create_status_file(struct vgpu_device *vgpu_dev);
+void vgpu_notify_status_file(struct vgpu_device *vgpu_dev);
+void vgpu_remove_status_file(struct vgpu_device *vgpu_dev);
+
+int vgpu_create_pci_device_files(struct pci_dev *dev);
+void vgpu_remove_pci_device_files(struct pci_dev *dev);
+
+void get_vgpu_supported_types(struct device *dev, char *str);
+int vgpu_start_callback(struct vgpu_device *vgpu_dev);
+int vgpu_shutdown_callback(struct vgpu_device *vgpu_dev);
+
+int vgpu_set_irqs_callback(struct vgpu_device *vgpu_dev, uint32_t flags,
+ unsigned index, unsigned start, unsigned count,
+ void *data);
+
+#endif /* VGPU_PRIVATE_H */
new file mode 100644
@@ -0,0 +1,322 @@
+/*
+ * File attributes for vGPU devices
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author: Neo Jia <cjia@nvidia.com>
+ * Kirti Wankhede <kwankhede@nvidia.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <linux/uuid.h>
+#include <linux/vfio.h>
+#include <linux/vgpu.h>
+
+#include "vgpu_private.h"
+
+/* Prototypes */
+
+static ssize_t vgpu_supported_types_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR_RO(vgpu_supported_types);
+
+static ssize_t vgpu_create_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR_WO(vgpu_create);
+
+static ssize_t vgpu_destroy_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR_WO(vgpu_destroy);
+
+
+/* Static functions */
+
+static bool is_uuid_sep(char sep)
+{
+ if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
+ return true;
+ return false;
+}
+
+
+static int uuid_parse(const char *str, uuid_le *uuid)
+{
+ int i;
+
+ if (strlen(str) < 36)
+ return -1;
+
+ for (i = 0; i < 16; i++) {
+ if (!isxdigit(str[0]) || !isxdigit(str[1])) {
+ printk(KERN_ERR "%s err", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ uuid->b[i] = (hex_to_bin(str[0]) << 4) | hex_to_bin(str[1]);
+ str += 2;
+ if (is_uuid_sep(*str))
+ str++;
+ }
+
+ return 0;
+}
+
+
+/* Functions */
+static ssize_t vgpu_supported_types_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ char *str;
+ ssize_t n;
+
+ str = kzalloc(sizeof(*str) * 512, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ get_vgpu_supported_types(dev, str);
+
+ n = sprintf(buf,"%s\n", str);
+ kfree(str);
+
+ return n;
+}
+
+static ssize_t vgpu_create_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *vm_uuid_str, *instance_str, *str;
+ uuid_le vm_uuid;
+ uint32_t instance, vgpu_id;
+ struct pci_dev *pdev;
+
+ str = kstrndup(buf, count, GFP_KERNEL);
+
+ if (!str)
+ return -ENOMEM;
+
+ if ((vm_uuid_str = strsep(&str, ":")) == NULL) {
+ printk(KERN_ERR "%s Empty UUID or string %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ if (str == NULL) {
+ printk(KERN_ERR "%s vgpu type and instance not specified %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ if ((instance_str = strsep(&str, ":")) == NULL) {
+ printk(KERN_ERR "%s Empty instance or string %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ if (str == NULL) {
+ printk(KERN_ERR "%s vgpu type not specified %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+
+ }
+
+ instance = (unsigned int)simple_strtoul(instance_str, NULL, 0);
+
+ vgpu_id = (unsigned int)simple_strtoul(str, NULL, 0);
+
+ if (uuid_parse(vm_uuid_str, &vm_uuid) < 0) {
+ printk(KERN_ERR "%s UUID parse error %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ if (dev_is_pci(dev)) {
+ pdev = to_pci_dev(dev);
+
+ if (create_vgpu_device(pdev, vm_uuid, instance, vgpu_id) < 0) {
+ printk(KERN_ERR "%s vgpu create error \n", __FUNCTION__);
+ return -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t vgpu_destroy_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *vm_uuid_str, *str;
+ uuid_le vm_uuid;
+ unsigned int instance;
+
+ str = kstrndup(buf, count, GFP_KERNEL);
+
+ if (!str)
+ return -ENOMEM;
+
+ if ((vm_uuid_str = strsep(&str, ":")) == NULL) {
+ printk(KERN_ERR "%s Empty UUID or string %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ if (str == NULL) {
+ printk(KERN_ERR "%s instance not specified %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ instance = (unsigned int)simple_strtoul(str, NULL, 0);
+
+ if (uuid_parse(vm_uuid_str, &vm_uuid) < 0) {
+ printk(KERN_ERR "%s UUID parse error %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s UUID %pUb - %d \n", __FUNCTION__, vm_uuid.b, instance);
+
+ destroy_vgpu_device_by_uuid(vm_uuid, instance);
+
+ return count;
+}
+
+static ssize_t
+vgpu_vm_uuid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct vgpu_device *drv = find_vgpu_device(dev);
+
+ if (drv)
+ return sprintf(buf, "%pUb \n", drv->vm_uuid.b);
+
+ return sprintf(buf, " \n");
+}
+
+static DEVICE_ATTR_RO(vgpu_vm_uuid);
+
+static ssize_t
+vgpu_group_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct vgpu_device *drv = find_vgpu_device(dev);
+
+ if (drv && drv->group)
+ return sprintf(buf, "%d \n", iommu_group_id(drv->group));
+
+ return sprintf(buf, " \n");
+}
+
+static DEVICE_ATTR_RO(vgpu_group_id);
+
+
+static struct attribute *vgpu_dev_attrs[] = {
+ &dev_attr_vgpu_vm_uuid.attr,
+ &dev_attr_vgpu_group_id.attr,
+ NULL,
+};
+
+static const struct attribute_group vgpu_dev_group = {
+ .attrs = vgpu_dev_attrs,
+};
+
+const struct attribute_group *vgpu_dev_groups[] = {
+ &vgpu_dev_group,
+ NULL,
+};
+
+
+ssize_t vgpu_start_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *vm_uuid_str;
+ uuid_le vm_uuid;
+ struct vgpu_device *vgpu_dev = NULL;
+ int ret;
+
+ vm_uuid_str = kstrndup(buf, count, GFP_KERNEL);
+
+ if (!vm_uuid_str)
+ return -ENOMEM;
+
+ if (uuid_parse(vm_uuid_str, &vm_uuid) < 0) {
+ printk(KERN_ERR "%s UUID parse error %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+
+ vgpu_dev = vgpu_drv_get_vgpu_device_by_uuid(vm_uuid, 0);
+
+ if (vgpu_dev && vgpu_dev->dev) {
+ kobject_uevent(&vgpu_dev->dev->kobj, KOBJ_ONLINE);
+
+ ret = vgpu_start_callback(vgpu_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s vgpu_start callback failed %d \n", __FUNCTION__, ret);
+ return ret;
+ }
+ }
+
+ return count;
+}
+
+ssize_t vgpu_shutdown_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *vm_uuid_str;
+ uuid_le vm_uuid;
+ struct vgpu_device *vgpu_dev = NULL;
+ int ret;
+
+ vm_uuid_str = kstrndup(buf, count, GFP_KERNEL);
+
+ if (!vm_uuid_str)
+ return -ENOMEM;
+
+ if (uuid_parse(vm_uuid_str, &vm_uuid) < 0) {
+ printk(KERN_ERR "%s UUID parse error %s \n", __FUNCTION__, buf);
+ return -EINVAL;
+ }
+ vgpu_dev = vgpu_drv_get_vgpu_device_by_uuid(vm_uuid, 0);
+
+ if (vgpu_dev && vgpu_dev->dev) {
+ kobject_uevent(&vgpu_dev->dev->kobj, KOBJ_OFFLINE);
+
+ ret = vgpu_shutdown_callback(vgpu_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s vgpu_shutdown callback failed %d \n", __FUNCTION__, ret);
+ return ret;
+ }
+ }
+
+ return count;
+}
+
+struct class_attribute vgpu_class_attrs[] = {
+ __ATTR_WO(vgpu_start),
+ __ATTR_WO(vgpu_shutdown),
+ __ATTR_NULL
+};
+
+int vgpu_create_pci_device_files(struct pci_dev *dev)
+{
+ int retval;
+
+ retval = sysfs_create_file(&dev->dev.kobj, &dev_attr_vgpu_supported_types.attr);
+ if (retval) {
+ printk(KERN_ERR "VGPU-VFIO: failed to create vgpu_supported_types sysfs entry\n");
+ return retval;
+ }
+
+ retval = sysfs_create_file(&dev->dev.kobj, &dev_attr_vgpu_create.attr);
+ if (retval) {
+ printk(KERN_ERR "VGPU-VFIO: failed to create vgpu_create sysfs entry\n");
+ return retval;
+ }
+
+ retval = sysfs_create_file(&dev->dev.kobj, &dev_attr_vgpu_destroy.attr);
+ if (retval) {
+ printk(KERN_ERR "VGPU-VFIO: failed to create vgpu_destroy sysfs entry\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+
+void vgpu_remove_pci_device_files(struct pci_dev *dev)
+{
+ sysfs_remove_file(&dev->dev.kobj, &dev_attr_vgpu_supported_types.attr);
+ sysfs_remove_file(&dev->dev.kobj, &dev_attr_vgpu_create.attr);
+ sysfs_remove_file(&dev->dev.kobj, &dev_attr_vgpu_destroy.attr);
+}
+
new file mode 100644
@@ -0,0 +1,521 @@
+/*
+ * VGPU VFIO device
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author: Neo Jia <cjia@nvidia.com>
+ * Kirti Wankhede <kwankhede@nvidia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+#include <linux/vfio.h>
+#include <linux/iommu.h>
+#include <linux/vgpu.h>
+
+#include "vgpu_private.h"
+
+#define VFIO_PCI_OFFSET_SHIFT 40
+
+#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
+
+struct vfio_vgpu_device {
+ struct iommu_group *group;
+ struct vgpu_device *vgpu_dev;
+};
+
+static int vgpu_dev_open(void *device_data)
+{
+ printk(KERN_INFO "%s ", __FUNCTION__);
+ return 0;
+}
+
+static void vgpu_dev_close(void *device_data)
+{
+
+}
+
+static uint64_t resource_len(struct vgpu_device *vgpu_dev, int bar_index)
+{
+ uint64_t size = 0;
+
+ switch (bar_index) {
+ case VFIO_PCI_BAR0_REGION_INDEX:
+ size = 16 * 1024 * 1024;
+ break;
+ case VFIO_PCI_BAR1_REGION_INDEX:
+ size = 256 * 1024 * 1024;
+ break;
+ case VFIO_PCI_BAR2_REGION_INDEX:
+ size = 32 * 1024 * 1024;
+ break;
+ case VFIO_PCI_BAR5_REGION_INDEX:
+ size = 128;
+ break;
+ default:
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+static int vgpu_get_irq_count(struct vfio_vgpu_device *vdev, int irq_type)
+{
+ return 1;
+}
+
+static long vgpu_dev_unlocked_ioctl(void *device_data,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct vfio_vgpu_device *vdev = device_data;
+ unsigned long minsz;
+
+ switch (cmd)
+ {
+ case VFIO_DEVICE_GET_INFO:
+ {
+ struct vfio_device_info info;
+ printk(KERN_INFO "%s VFIO_DEVICE_GET_INFO cmd index = %d", __FUNCTION__, vdev->vgpu_dev->minor);
+ minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = VFIO_DEVICE_FLAGS_PCI;
+ info.num_regions = VFIO_PCI_NUM_REGIONS;
+ info.num_irqs = VFIO_PCI_NUM_IRQS;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+ }
+
+ case VFIO_DEVICE_GET_REGION_INFO:
+ {
+ struct vfio_region_info info;
+
+ printk(KERN_INFO "%s VFIO_DEVICE_GET_REGION_INFO cmd", __FUNCTION__);
+
+ minsz = offsetofend(struct vfio_region_info, offset);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ switch (info.index) {
+ case VFIO_PCI_CONFIG_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = 0x100; // 4K
+ // info.size = sizeof(vdev->vgpu_dev->config_space);
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+ case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = resource_len(vdev->vgpu_dev, info.index);
+ if (!info.size) {
+ info.flags = 0;
+ break;
+ }
+
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+
+ if ((info.index == VFIO_PCI_BAR1_REGION_INDEX) ||
+ (info.index == VFIO_PCI_BAR2_REGION_INDEX)) {
+ info.flags |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+ }
+
+ /* TODO: provides configurable setups to
+ * GPU vendor
+ */
+
+ if (info.index == VFIO_PCI_BAR1_REGION_INDEX)
+ info.flags = VFIO_REGION_INFO_FLAG_MMAP;
+
+ break;
+ case VFIO_PCI_VGA_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = 0xc0000;
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+
+ case VFIO_PCI_ROM_REGION_INDEX:
+ default:
+ return -EINVAL;
+ }
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ }
+ case VFIO_DEVICE_GET_IRQ_INFO:
+ {
+ struct vfio_irq_info info;
+
+ printk(KERN_INFO "%s VFIO_DEVICE_GET_IRQ_INFO cmd", __FUNCTION__);
+ minsz = offsetofend(struct vfio_irq_info, count);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+ return -EINVAL;
+
+ switch (info.index) {
+ case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
+ case VFIO_PCI_REQ_IRQ_INDEX:
+ break;
+ /* pass thru to return error */
+ default:
+ return -EINVAL;
+ }
+
+ info.count = VFIO_PCI_NUM_IRQS;
+
+ info.flags = VFIO_IRQ_INFO_EVENTFD;
+ info.count = vgpu_get_irq_count(vdev, info.index);
+
+ if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+ info.flags |= (VFIO_IRQ_INFO_MASKABLE |
+ VFIO_IRQ_INFO_AUTOMASKED);
+ else
+ info.flags |= VFIO_IRQ_INFO_NORESIZE;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+ }
+
+ case VFIO_DEVICE_SET_IRQS:
+ {
+ struct vfio_irq_set hdr;
+ u8 *data = NULL;
+ int ret = 0;
+
+ minsz = offsetofend(struct vfio_irq_set, count);
+
+ if (copy_from_user(&hdr, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
+ VFIO_IRQ_SET_ACTION_TYPE_MASK))
+ return -EINVAL;
+
+ if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
+ size_t size;
+ int max = vgpu_get_irq_count(vdev, hdr.index);
+
+ if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
+ size = sizeof(uint8_t);
+ else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
+ size = sizeof(int32_t);
+ else
+ return -EINVAL;
+
+ if (hdr.argsz - minsz < hdr.count * size ||
+ hdr.start >= max || hdr.start + hdr.count > max)
+ return -EINVAL;
+
+ data = memdup_user((void __user *)(arg + minsz),
+ hdr.count * size);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ }
+ ret = vgpu_set_irqs_callback(vdev->vgpu_dev, hdr.flags, hdr.index,
+ hdr.start, hdr.count, data);
+ kfree(data);
+
+
+ return ret;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+
+ssize_t vgpu_dev_config_rw(struct vfio_vgpu_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct vgpu_device *vgpu_dev = vdev->vgpu_dev;
+ int cfg_size = sizeof(vgpu_dev->config_space);
+ int ret = 0;
+ uint64_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+
+ if (pos < 0 || pos >= cfg_size ||
+ pos + count > cfg_size) {
+ printk(KERN_ERR "%s pos 0x%llx out of range\n", __FUNCTION__, pos);
+ ret = -EFAULT;
+ goto config_rw_exit;
+ }
+
+ if (iswrite) {
+ char *user_data = kmalloc(count, GFP_KERNEL);
+
+ if (user_data == NULL) {
+ ret = -ENOMEM;
+ goto config_rw_exit;
+ }
+
+ if (copy_from_user(user_data, buf, count)) {
+ ret = -EFAULT;
+ kfree(user_data);
+ goto config_rw_exit;
+ }
+
+ /* FIXME: Need to save the BAR value properly */
+ switch (pos) {
+ case PCI_BASE_ADDRESS_0:
+ vgpu_dev->bar[0].start = *((uint32_t *)user_data);
+ break;
+ case PCI_BASE_ADDRESS_1:
+ vgpu_dev->bar[1].start = *((uint32_t *)user_data);
+ break;
+ case PCI_BASE_ADDRESS_2:
+ vgpu_dev->bar[2].start = *((uint32_t *)user_data);
+ break;
+ }
+
+ if (vgpu_dev->gpu_dev->ops->write) {
+ ret = vgpu_dev->gpu_dev->ops->write(vgpu_dev,
+ user_data,
+ count,
+ vgpu_emul_space_config,
+ pos);
+ }
+
+ kfree(user_data);
+ }
+ else
+ {
+ char *ret_data = kmalloc(count, GFP_KERNEL);
+
+ if (ret_data == NULL) {
+ ret = -ENOMEM;
+ goto config_rw_exit;
+ }
+
+ memset(ret_data, 0, count);
+
+ if (vgpu_dev->gpu_dev->ops->read) {
+ ret = vgpu_dev->gpu_dev->ops->read(vgpu_dev,
+ ret_data,
+ count,
+ vgpu_emul_space_config,
+ pos);
+ }
+
+ if (ret > 0 ) {
+ if (copy_to_user(buf, ret_data, ret)) {
+ ret = -EFAULT;
+ kfree(ret_data);
+ goto config_rw_exit;
+ }
+ }
+ kfree(ret_data);
+ }
+
+config_rw_exit:
+
+ return ret;
+}
+
+ssize_t vgpu_dev_bar_rw(struct vfio_vgpu_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct vgpu_device *vgpu_dev = vdev->vgpu_dev;
+ loff_t offset = *ppos & VFIO_PCI_OFFSET_MASK;
+ loff_t pos;
+ int bar_index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ uint64_t end;
+ int ret = 0;
+
+ if (!vgpu_dev->bar[bar_index].start) {
+ ret = -EINVAL;
+ goto bar_rw_exit;
+ }
+
+ end = resource_len(vgpu_dev, bar_index);
+
+ if (offset >= end) {
+ ret = -EINVAL;
+ goto bar_rw_exit;
+ }
+
+ pos = vgpu_dev->bar[bar_index].start + offset;
+ if (iswrite) {
+ char *user_data = kmalloc(count, GFP_KERNEL);
+
+ if (user_data == NULL) {
+ ret = -ENOMEM;
+ goto bar_rw_exit;
+ }
+
+ if (copy_from_user(user_data, buf, count)) {
+ ret = -EFAULT;
+ kfree(user_data);
+ goto bar_rw_exit;
+ }
+
+ if (vgpu_dev->gpu_dev->ops->write) {
+ ret = vgpu_dev->gpu_dev->ops->write(vgpu_dev,
+ user_data,
+ count,
+ vgpu_emul_space_mmio,
+ pos);
+ }
+
+ kfree(user_data);
+ }
+ else
+ {
+ char *ret_data = kmalloc(count, GFP_KERNEL);
+
+ if (ret_data == NULL) {
+ ret = -ENOMEM;
+ goto bar_rw_exit;
+ }
+
+ memset(ret_data, 0, count);
+
+ if (vgpu_dev->gpu_dev->ops->read) {
+ ret = vgpu_dev->gpu_dev->ops->read(vgpu_dev,
+ ret_data,
+ count,
+ vgpu_emul_space_mmio,
+ pos);
+ }
+
+ if (ret > 0 ) {
+ if (copy_to_user(buf, ret_data, ret)) {
+ ret = -EFAULT;
+ }
+ }
+ kfree(ret_data);
+ }
+
+bar_rw_exit:
+ return ret;
+}
+
+
+static ssize_t vgpu_dev_rw(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ struct vfio_vgpu_device *vdev = device_data;
+
+ if (index >= VFIO_PCI_NUM_REGIONS)
+ return -EINVAL;
+
+ switch (index) {
+ case VFIO_PCI_CONFIG_REGION_INDEX:
+ return vgpu_dev_config_rw(vdev, buf, count, ppos, iswrite);
+
+
+ case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+ return vgpu_dev_bar_rw(vdev, buf, count, ppos, iswrite);
+
+ case VFIO_PCI_ROM_REGION_INDEX:
+ case VFIO_PCI_VGA_REGION_INDEX:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+
+static ssize_t vgpu_dev_read(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+
+ if (count)
+ ret = vgpu_dev_rw(device_data, buf, count, ppos, false);
+
+ return ret;
+}
+
+static ssize_t vgpu_dev_write(void *device_data, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+
+ if (count)
+ ret = vgpu_dev_rw(device_data, (char *)buf, count, ppos, true);
+
+ return ret;
+}
+
+/* Just create an invalid mapping without providing a fault handler */
+
+static int vgpu_dev_mmap(void *device_data, struct vm_area_struct *vma)
+{
+ printk(KERN_INFO "%s ", __FUNCTION__);
+ return 0;
+}
+
+static const struct vfio_device_ops vgpu_vfio_dev_ops = {
+ .name = "vfio-vgpu-grp",
+ .open = vgpu_dev_open,
+ .release = vgpu_dev_close,
+ .ioctl = vgpu_dev_unlocked_ioctl,
+ .read = vgpu_dev_read,
+ .write = vgpu_dev_write,
+ .mmap = vgpu_dev_mmap,
+};
+
+int vgpu_group_init(struct vgpu_device *vgpu_dev, struct iommu_group *group)
+{
+ struct vfio_vgpu_device *vdev;
+ int ret = 0;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ return -ENOMEM;
+ }
+
+ vdev->group = group;
+ vdev->vgpu_dev = vgpu_dev;
+
+ ret = vfio_add_group_dev(vgpu_dev->dev, &vgpu_vfio_dev_ops, vdev);
+ if (ret)
+ kfree(vdev);
+
+ return ret;
+}
+
+
+int vgpu_group_free(struct vgpu_device *vgpu_dev)
+{
+ struct vfio_vgpu_device *vdev;
+
+ vdev = vfio_del_group_dev(vgpu_dev->dev);
+ if (!vdev)
+ return -1;
+
+ kfree(vdev);
+ return 0;
+}
+
new file mode 100644
@@ -0,0 +1,157 @@
+/*
+ * VGPU definition
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ * Author:
+ *
+ * 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 VGPU_H
+#define VGPU_H
+
+// Common Data structures
+
+struct pci_bar_info {
+ uint64_t start;
+ uint64_t end;
+ int flags;
+};
+
+enum vgpu_emul_space_e {
+ vgpu_emul_space_config = 0, /*!< PCI configuration space */
+ vgpu_emul_space_io = 1, /*!< I/O register space */
+ vgpu_emul_space_mmio = 2 /*!< Memory-mapped I/O space */
+};
+
+struct gpu_device;
+
+/*
+ * VGPU device
+ */
+struct vgpu_device {
+ struct kref kref;
+ struct device *dev;
+ int minor;
+ struct gpu_device *gpu_dev;
+ struct iommu_group *group;
+#define DEVICE_NAME_LEN (64)
+ char dev_name[DEVICE_NAME_LEN];
+ uuid_le vm_uuid;
+ uint32_t vgpu_instance;
+ uint32_t vgpu_id;
+ atomic_t usage_count;
+ char config_space[0x100]; // 4KB PCI cfg space
+ struct pci_bar_info bar[VFIO_PCI_NUM_REGIONS];
+ struct device_attribute *dev_attr_vgpu_status;
+ int vgpu_device_status;
+
+ struct list_head list;
+};
+
+
+/**
+ * struct gpu_device_ops - Structure to be registered for each physical GPU to
+ * register the device to vgpu module.
+ *
+ * @owner: The module owner.
+ * @vgpu_supported_config: Called to get information about supported vgpu types.
+ * @dev : pci device structure of physical GPU.
+ * @config: should return string listing supported config
+ * Returns integer: success (0) or error (< 0)
+ * @vgpu_create: Called to allocate basic resouces in graphics
+ * driver for a particular vgpu.
+ * @dev: physical pci device structure on which vgpu
+ * should be created
+ * @vm_uuid: VM's uuid for which VM it is intended to
+ * @instance: vgpu instance in that VM
+ * @vgpu_id: This represents the type of vgpu to be
+ * created
+ * Returns integer: success (0) or error (< 0)
+ * @vgpu_destroy: Called to free resources in graphics driver for
+ * a vgpu instance of that VM.
+ * @dev: physical pci device structure to which
+ * this vgpu points to.
+ * @vm_uuid: VM's uuid for which the vgpu belongs to.
+ * @instance: vgpu instance in that VM
+ * Returns integer: success (0) or error (< 0)
+ * If VM is running and vgpu_destroy is called that
+ * means the vGPU is being hotunpluged. Return error
+ * if VM is running and graphics driver doesn't
+ * support vgpu hotplug.
+ * @vgpu_start: Called to do initiate vGPU initialization
+ * process in graphics driver when VM boots before
+ * qemu starts.
+ * @vm_uuid: VM's UUID which is booting.
+ * Returns integer: success (0) or error (< 0)
+ * @vgpu_shutdown: Called to teardown vGPU related resources for
+ * the VM
+ * @vm_uuid: VM's UUID which is shutting down .
+ * Returns integer: success (0) or error (< 0)
+ * @read: Read emulation callback
+ * @vdev: vgpu device structure
+ * @buf: read buffer
+ * @count: number bytes to read
+ * @address_space: specifies for which address space
+ * the request is: pci_config_space, IO register
+ * space or MMIO space.
+ * Retuns number on bytes read on success or error.
+ * @write: Write emulation callback
+ * @vdev: vgpu device structure
+ * @buf: write buffer
+ * @count: number bytes to be written
+ * @address_space: specifies for which address space
+ * the request is: pci_config_space, IO register
+ * space or MMIO space.
+ * Retuns number on bytes written on success or error.
+ * @vgpu_set_irqs: Called to send about interrupts configuration
+ * information that qemu set.
+ * @vdev: vgpu device structure
+ * @flags, index, start, count and *data : same as
+ * that of struct vfio_irq_set of
+ * VFIO_DEVICE_SET_IRQS API.
+ *
+ * Physical GPU that support vGPU should be register with vgpu module with
+ * gpu_device_ops structure.
+ */
+
+struct gpu_device_ops {
+ struct module *owner;
+ int (*vgpu_supported_config)(struct pci_dev *dev, char *config);
+ int (*vgpu_create)(struct pci_dev *dev, uuid_le vm_uuid,
+ uint32_t instance, uint32_t vgpu_id);
+ int (*vgpu_destroy)(struct pci_dev *dev, uuid_le vm_uuid,
+ uint32_t instance);
+ int (*vgpu_start)(uuid_le vm_uuid);
+ int (*vgpu_shutdown)(uuid_le vm_uuid);
+ ssize_t (*read) (struct vgpu_device *vdev, char *buf, size_t count,
+ uint32_t address_space, loff_t pos);
+ ssize_t (*write)(struct vgpu_device *vdev, char *buf, size_t count,
+ uint32_t address_space,loff_t pos);
+ int (*vgpu_set_irqs)(struct vgpu_device *vdev, uint32_t flags,
+ unsigned index, unsigned start, unsigned count,
+ void *data);
+
+};
+
+/*
+ * Physical GPU
+ */
+struct gpu_device {
+ struct pci_dev *dev;
+ const struct gpu_device_ops *ops;
+ struct list_head gpu_next;
+};
+
+extern int vgpu_register_device(struct pci_dev *dev, const struct gpu_device_ops *ops);
+extern void vgpu_unregister_device(struct pci_dev *dev);
+
+extern int vgpu_map_virtual_bar(uint64_t virt_bar_addr, uint64_t phys_bar_addr, uint32_t len, uint32_t flags);
+extern int vgpu_dma_do_translate(dma_addr_t * gfn_buffer, uint32_t count);
+
+struct vgpu_device *get_vgpu_device_from_group(struct iommu_group *group);
+
+#endif /* VGPU_H */
+