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: Alex Williamson <alex.williamson@xxxxxxxxxx> Cc: Kirti Wankhede <kwankhede@xxxxxxxxxx> Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx> Cc: Xiao Guangrong <guangrong.xiao@xxxxxxxxxxxxxxx> Signed-off-by: Jike Song <jike.song@xxxxxxxxx> --- drivers/vfio/vfio.c | 85 ++++++++++++++++++++++++++++++++++++++-------------- include/linux/vfio.h | 16 ++++++++-- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 0aac3ca..493bbad 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -2008,23 +2008,24 @@ int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, int npage) } 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; + /* clear known events */ + *events &= ~VFIO_IOMMU_NOTIFY_DMA_UNMAP; - group = vfio_group_get_from_dev(dev); - if (IS_ERR(group)) - return PTR_ERR(group); + /* refuse to register if still events remaining */ + if (*events) + return -EINVAL; ret = vfio_group_add_container_user(group); if (ret) - goto err_register_nb; + return -EINVAL; container = group->container; down_read(&container->group_lock); @@ -2038,29 +2039,19 @@ int vfio_register_notifier(struct device *dev, struct notifier_block *nb) 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 (IS_ERR(group)) - return PTR_ERR(group); - 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 +2066,57 @@ 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, vfio_notify_type_t 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 (IS_ERR(group)) + return PTR_ERR(group); + + 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, vfio_notify_type_t type, + struct notifier_block *nb) +{ + struct vfio_group *group; + int ret; + + if (!dev || !nb) + return -EINVAL; + + group = vfio_group_get_from_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + switch (type) { + case VFIO_IOMMU_NOTIFY: + ret = vfio_unregister_iommu_notifier(group, nb); + break; + default: + ret = -EINVAL; + } + vfio_group_put(group); return ret; } diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 15ff042..6f3ff31 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -107,14 +107,24 @@ 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) - +typedef unsigned short vfio_notify_type_t; extern int vfio_register_notifier(struct device *dev, + vfio_notify_type_t type, + unsigned long *required_events, struct notifier_block *nb); - extern int vfio_unregister_notifier(struct device *dev, + vfio_notify_type_t type, struct notifier_block *nb); +/* each type has independent events */ +enum vfio_notify_type { + VFIO_IOMMU_NOTIFY = (__force vfio_notify_type_t)0, +}; + +/* events for VFIO_IOMMU_NOTIFY */ +#define VFIO_IOMMU_NOTIFY_DMA_UNMAP BIT(0) + + /* * Sub-module helpers */ -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html