qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v8 6/6] Add common functions for SET_IRQS and GET_RE


From: Kirti Wankhede
Subject: [Qemu-devel] [PATCH v8 6/6] Add common functions for SET_IRQS and GET_REGION_INFO ioctls
Date: Tue, 11 Oct 2016 01:58:37 +0530

Add common functions for SET_IRQS and to add capability buffer for
GET_REGION_INFO ioctls

Signed-off-by: Kirti Wankhede <address@hidden>
Signed-off-by: Neo Jia <address@hidden>
Change-Id: Id9e976a2c08b9b2b37da77dac4365ae8f6024b4a
---
 drivers/vfio/pci/vfio_pci.c | 103 +++++++++++++++------------------------
 drivers/vfio/vfio.c         | 116 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/vfio.h        |   7 +++
 3 files changed, 162 insertions(+), 64 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 188b1ff03f5f..f312cbb0eebc 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -478,12 +478,12 @@ static int vfio_pci_for_each_slot_or_bus(struct pci_dev 
*pdev,
 }
 
 static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev,
+                               struct vfio_region_info *info,
                                struct vfio_info_cap *caps)
 {
-       struct vfio_info_cap_header *header;
        struct vfio_region_info_cap_sparse_mmap *sparse;
        size_t end, size;
-       int nr_areas = 2, i = 0;
+       int nr_areas = 2, i = 0, ret;
 
        end = pci_resource_len(vdev->pdev, vdev->msix_bar);
 
@@ -494,13 +494,10 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device 
*vdev,
 
        size = sizeof(*sparse) + (nr_areas * sizeof(*sparse->areas));
 
-       header = vfio_info_cap_add(caps, size,
-                                  VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1);
-       if (IS_ERR(header))
-               return PTR_ERR(header);
+       sparse = kzalloc(size, GFP_KERNEL);
+       if (!sparse)
+               return -ENOMEM;
 
-       sparse = container_of(header,
-                             struct vfio_region_info_cap_sparse_mmap, header);
        sparse->nr_areas = nr_areas;
 
        if (vdev->msix_offset & PAGE_MASK) {
@@ -516,24 +513,14 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device 
*vdev,
                i++;
        }
 
-       return 0;
-}
-
-static int region_type_cap(struct vfio_pci_device *vdev,
-                          struct vfio_info_cap *caps,
-                          unsigned int type, unsigned int subtype)
-{
-       struct vfio_info_cap_header *header;
-       struct vfio_region_info_cap_type *cap;
+       info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
 
-       header = vfio_info_cap_add(caps, sizeof(*cap),
-                                  VFIO_REGION_INFO_CAP_TYPE, 1);
-       if (IS_ERR(header))
-               return PTR_ERR(header);
+       ret = vfio_info_add_capability(info, caps,
+                                     VFIO_REGION_INFO_CAP_SPARSE_MMAP, sparse);
+       kfree(sparse);
 
-       cap = container_of(header, struct vfio_region_info_cap_type, header);
-       cap->type = type;
-       cap->subtype = subtype;
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -628,7 +615,8 @@ static long vfio_pci_ioctl(void *device_data,
                            IORESOURCE_MEM && info.size >= PAGE_SIZE) {
                                info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
                                if (info.index == vdev->msix_bar) {
-                                       ret = msix_sparse_mmap_cap(vdev, &caps);
+                                       ret = msix_sparse_mmap_cap(vdev, &info,
+                                                                  &caps);
                                        if (ret)
                                                return ret;
                                }
@@ -676,6 +664,9 @@ static long vfio_pci_ioctl(void *device_data,
 
                        break;
                default:
+               {
+                       struct vfio_region_info_cap_type cap_type;
+
                        if (info.index >=
                            VFIO_PCI_NUM_REGIONS + vdev->num_regions)
                                return -EINVAL;
@@ -684,29 +675,26 @@ static long vfio_pci_ioctl(void *device_data,
 
                        info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
                        info.size = vdev->region[i].size;
-                       info.flags = vdev->region[i].flags;
+                       info.flags = vdev->region[i].flags |
+                                    VFIO_REGION_INFO_FLAG_CAPS;
 
-                       ret = region_type_cap(vdev, &caps,
-                                             vdev->region[i].type,
-                                             vdev->region[i].subtype);
+                       cap_type.type = vdev->region[i].type;
+                       cap_type.subtype = vdev->region[i].subtype;
+
+                       ret = vfio_info_add_capability(&info, &caps,
+                                                     VFIO_REGION_INFO_CAP_TYPE,
+                                                     &cap_type);
                        if (ret)
                                return ret;
+
+               }
                }
 
-               if (caps.size) {
-                       info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
-                       if (info.argsz < sizeof(info) + caps.size) {
-                               info.argsz = sizeof(info) + caps.size;
-                               info.cap_offset = 0;
-                       } else {
-                               vfio_info_cap_shift(&caps, sizeof(info));
-                               if (copy_to_user((void __user *)arg +
-                                                 sizeof(info), caps.buf,
-                                                 caps.size)) {
-                                       kfree(caps.buf);
-                                       return -EFAULT;
-                               }
-                               info.cap_offset = sizeof(info);
+               if (info.cap_offset) {
+                       if (copy_to_user((void __user *)arg + info.cap_offset,
+                                        caps.buf, caps.size)) {
+                               kfree(caps.buf);
+                               return -EFAULT;
                        }
 
                        kfree(caps.buf);
@@ -754,35 +742,22 @@ static long vfio_pci_ioctl(void *device_data,
        } else if (cmd == VFIO_DEVICE_SET_IRQS) {
                struct vfio_irq_set hdr;
                u8 *data = NULL;
-               int ret = 0;
+               int max, ret = 0, data_size = 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 = vfio_pci_get_irq_count(vdev, hdr.index);
+               max = vfio_pci_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;
+               ret = vfio_set_irqs_validate_and_prepare(&hdr, max, &data_size);
+               if (ret)
+                       return ret;
 
+               if (data_size) {
                        data = memdup_user((void __user *)(arg + minsz),
-                                          hdr.count * size);
+                                           data_size);
                        if (IS_ERR(data))
                                return PTR_ERR(data);
                }
@@ -790,7 +765,7 @@ static long vfio_pci_ioctl(void *device_data,
                mutex_lock(&vdev->igate);
 
                ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
-                                             hdr.start, hdr.count, data);
+                               hdr.start, hdr.count, data);
 
                mutex_unlock(&vdev->igate);
                kfree(data);
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index e3e342861e04..0185d5fb2c85 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -1782,6 +1782,122 @@ void vfio_info_cap_shift(struct vfio_info_cap *caps, 
size_t offset)
 }
 EXPORT_SYMBOL_GPL(vfio_info_cap_shift);
 
+static int sparse_mmap_cap(struct vfio_info_cap *caps, void *cap_type)
+{
+       struct vfio_info_cap_header *header;
+       struct vfio_region_info_cap_sparse_mmap *sparse_cap, *sparse = cap_type;
+       size_t size;
+
+       size = sizeof(*sparse) + sparse->nr_areas *  sizeof(*sparse->areas);
+       header = vfio_info_cap_add(caps, size,
+                                  VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1);
+       if (IS_ERR(header))
+               return PTR_ERR(header);
+
+       sparse_cap = container_of(header,
+                       struct vfio_region_info_cap_sparse_mmap, header);
+       sparse_cap->nr_areas = sparse->nr_areas;
+       memcpy(sparse_cap->areas, sparse->areas,
+              sparse->nr_areas * sizeof(*sparse->areas));
+       return 0;
+}
+
+static int region_type_cap(struct vfio_info_cap *caps, void *cap_type)
+{
+       struct vfio_info_cap_header *header;
+       struct vfio_region_info_cap_type *type_cap, *cap = cap_type;
+
+       header = vfio_info_cap_add(caps, sizeof(*cap),
+                                  VFIO_REGION_INFO_CAP_TYPE, 1);
+       if (IS_ERR(header))
+               return PTR_ERR(header);
+
+       type_cap = container_of(header, struct vfio_region_info_cap_type,
+                               header);
+       type_cap->type = cap->type;
+       type_cap->subtype = cap->subtype;
+       return 0;
+}
+
+int vfio_info_add_capability(struct vfio_region_info *info,
+                            struct vfio_info_cap *caps,
+                            int cap_type_id,
+                            void *cap_type)
+{
+       int ret;
+
+       if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS) || !cap_type)
+               return 0;
+
+       switch (cap_type_id) {
+       case VFIO_REGION_INFO_CAP_SPARSE_MMAP:
+               ret = sparse_mmap_cap(caps, cap_type);
+               if (ret)
+                       return ret;
+               break;
+
+       case VFIO_REGION_INFO_CAP_TYPE:
+               ret = region_type_cap(caps, cap_type);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (caps->size) {
+               if (info->argsz < sizeof(*info) + caps->size) {
+                       info->argsz = sizeof(*info) + caps->size;
+                       info->cap_offset = 0;
+               } else {
+                       vfio_info_cap_shift(caps, sizeof(*info));
+                       info->cap_offset = sizeof(*info);
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(vfio_info_add_capability);
+
+int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr, int num_irqs,
+                                      int *data_size)
+{
+       unsigned long minsz;
+
+       minsz = offsetofend(struct vfio_irq_set, count);
+
+       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 (data_size)
+               *data_size = 0;
+
+       if (!(hdr->flags & VFIO_IRQ_SET_DATA_NONE)) {
+               size_t size;
+
+               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 >= num_irqs) ||
+                   (hdr->start + hdr->count > num_irqs))
+                       return -EINVAL;
+
+               if (!data_size)
+                       return -EINVAL;
+
+               *data_size = hdr->count * size;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(vfio_set_irqs_validate_and_prepare);
+
 static struct vfio_group *vfio_group_from_dev(struct device *dev)
 {
        struct vfio_device *device;
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 0bd25ba6223d..5641dab72ded 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -108,6 +108,13 @@ extern struct vfio_info_cap_header *vfio_info_cap_add(
                struct vfio_info_cap *caps, size_t size, u16 id, u16 version);
 extern void vfio_info_cap_shift(struct vfio_info_cap *caps, size_t offset);
 
+extern int vfio_info_add_capability(struct vfio_region_info *info,
+                                   struct vfio_info_cap *caps,
+                                   int cap_type_id, void *cap_type);
+
+extern int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr,
+                                             int num_irqs, int *data_size);
+
 struct pci_dev;
 #ifdef CONFIG_EEH
 extern void vfio_spapr_pci_eeh_open(struct pci_dev *pdev);
-- 
2.7.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]