vfio: vfio_register_notifier: classify iommu notifier
authorJike Song <jike.song@intel.com>
Thu, 1 Dec 2016 05:20:05 +0000 (13:20 +0800)
committerAlex Williamson <alex.williamson@redhat.com>
Thu, 1 Dec 2016 16:38:47 +0000 (09:38 -0700)
Currently vfio_register_notifier assumes that there is only one
notifier chain, which is in vfio_iommu. However, the user might
also be interested in events other than vfio_iommu, for example,
vfio_group. Refactor vfio_{un}register_notifier implementation
to make it feasible.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Reviewed-by: Kirti Wankhede <kwankhede@nvidia.com>
Signed-off-by: Jike Song <jike.song@intel.com>
[aw: merge with commit 816ca69ea9c7 ("vfio: Fix handling of error returned by 'vfio_group_get_from_dev()'"), remove typedef]
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/vfio.c
drivers/vfio/vfio_iommu_type1.c
include/linux/vfio.h

index 7b39313abf0d759c97d26d83224e5d1e3802bb1f..6cc2a9ffac10b9005486eca9bc21ec0f910d6744 100644 (file)
@@ -2008,59 +2008,44 @@ err_unpin_pages:
 }
 EXPORT_SYMBOL(vfio_unpin_pages);
 
-int vfio_register_notifier(struct device *dev, struct notifier_block *nb)
+static int vfio_register_iommu_notifier(struct vfio_group *group,
+                                       unsigned long *events,
+                                       struct notifier_block *nb)
 {
        struct vfio_container *container;
-       struct vfio_group *group;
        struct vfio_iommu_driver *driver;
        int ret;
 
-       if (!dev || !nb)
-               return -EINVAL;
-
-       group = vfio_group_get_from_dev(dev);
-       if (!group)
-               return -ENODEV;
-
        ret = vfio_group_add_container_user(group);
        if (ret)
-               goto err_register_nb;
+               return -EINVAL;
 
        container = group->container;
        down_read(&container->group_lock);
 
        driver = container->iommu_driver;
        if (likely(driver && driver->ops->register_notifier))
-               ret = driver->ops->register_notifier(container->iommu_data, nb);
+               ret = driver->ops->register_notifier(container->iommu_data,
+                                                    events, nb);
        else
                ret = -ENOTTY;
 
        up_read(&container->group_lock);
        vfio_group_try_dissolve_container(group);
 
-err_register_nb:
-       vfio_group_put(group);
        return ret;
 }
-EXPORT_SYMBOL(vfio_register_notifier);
 
-int vfio_unregister_notifier(struct device *dev, struct notifier_block *nb)
+static int vfio_unregister_iommu_notifier(struct vfio_group *group,
+                                         struct notifier_block *nb)
 {
        struct vfio_container *container;
-       struct vfio_group *group;
        struct vfio_iommu_driver *driver;
        int ret;
 
-       if (!dev || !nb)
-               return -EINVAL;
-
-       group = vfio_group_get_from_dev(dev);
-       if (!group)
-               return -ENODEV;
-
        ret = vfio_group_add_container_user(group);
        if (ret)
-               goto err_unregister_nb;
+               return -EINVAL;
 
        container = group->container;
        down_read(&container->group_lock);
@@ -2075,7 +2060,56 @@ int vfio_unregister_notifier(struct device *dev, struct notifier_block *nb)
        up_read(&container->group_lock);
        vfio_group_try_dissolve_container(group);
 
-err_unregister_nb:
+       return ret;
+}
+
+int vfio_register_notifier(struct device *dev, enum vfio_notify_type type,
+                          unsigned long *events, struct notifier_block *nb)
+{
+       struct vfio_group *group;
+       int ret;
+
+       if (!dev || !nb || !events || (*events == 0))
+               return -EINVAL;
+
+       group = vfio_group_get_from_dev(dev);
+       if (!group)
+               return -ENODEV;
+
+       switch (type) {
+       case VFIO_IOMMU_NOTIFY:
+               ret = vfio_register_iommu_notifier(group, events, nb);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       vfio_group_put(group);
+       return ret;
+}
+EXPORT_SYMBOL(vfio_register_notifier);
+
+int vfio_unregister_notifier(struct device *dev, enum vfio_notify_type type,
+                            struct notifier_block *nb)
+{
+       struct vfio_group *group;
+       int ret;
+
+       if (!dev || !nb)
+               return -EINVAL;
+
+       group = vfio_group_get_from_dev(dev);
+       if (!group)
+               return -ENODEV;
+
+       switch (type) {
+       case VFIO_IOMMU_NOTIFY:
+               ret = vfio_unregister_iommu_notifier(group, nb);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
        vfio_group_put(group);
        return ret;
 }
index 51810a95416ea2b0d5a2d47beca50ea08309503a..b88ad1e5e7ffd6b78a9a0cc16a33d35b9810fdda 100644 (file)
@@ -1584,10 +1584,18 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
 }
 
 static int vfio_iommu_type1_register_notifier(void *iommu_data,
+                                             unsigned long *events,
                                              struct notifier_block *nb)
 {
        struct vfio_iommu *iommu = iommu_data;
 
+       /* clear known events */
+       *events &= ~VFIO_IOMMU_NOTIFY_DMA_UNMAP;
+
+       /* refuse to register if still events remaining */
+       if (*events)
+               return -EINVAL;
+
        return blocking_notifier_chain_register(&iommu->notifier, nb);
 }
 
index 15ff0421b42395c5cec0c27ada2f8a053eb04da9..0e5201f207d3d03a2c3e49502ded1745c1f12699 100644 (file)
@@ -81,6 +81,7 @@ struct vfio_iommu_driver_ops {
        int             (*unpin_pages)(void *iommu_data,
                                       unsigned long *user_pfn, int npage);
        int             (*register_notifier)(void *iommu_data,
+                                            unsigned long *events,
                                             struct notifier_block *nb);
        int             (*unregister_notifier)(void *iommu_data,
                                               struct notifier_block *nb);
@@ -107,12 +108,20 @@ extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn,
 extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn,
                            int npage);
 
-#define VFIO_IOMMU_NOTIFY_DMA_UNMAP    (1)
+/* each type has independent events */
+enum vfio_notify_type {
+       VFIO_IOMMU_NOTIFY = 0,
+};
+
+/* events for VFIO_IOMMU_NOTIFY */
+#define VFIO_IOMMU_NOTIFY_DMA_UNMAP    BIT(0)
 
 extern int vfio_register_notifier(struct device *dev,
+                                 enum vfio_notify_type type,
+                                 unsigned long *required_events,
                                  struct notifier_block *nb);
-
 extern int vfio_unregister_notifier(struct device *dev,
+                                   enum vfio_notify_type type,
                                    struct notifier_block *nb);
 
 /*