Some group-wide operations must validate every member before changing any device. Separate iommu_group_for_each_dev() calls cannot provide that guarantee because group membership may change between traversals.
Add iommu_group_update_devices() to keep the group membership mutex held across a validation pass and a non-failing update pass. This provides all-or-none validation without exposing IOMMU group internals to callers. Signed-off-by: Zhanpeng Zhang <[email protected]> --- drivers/iommu/iommu.c | 33 +++++++++++++++++++++++++++++++++ include/linux/iommu.h | 13 +++++++++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index da269d10f6bf..9a6c4a7e7df6 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1436,6 +1436,39 @@ int iommu_group_for_each_dev(struct iommu_group *group, void *data, } EXPORT_SYMBOL_GPL(iommu_group_for_each_dev); +/** + * iommu_group_update_devices - Check and update every device in a group + * @group: the group + * @data: caller data passed to both callbacks + * @check: validates whether one device can be updated + * @update: updates one device after every check has succeeded + * + * Keep group membership stable while first checking every device and then + * applying an update which cannot fail. No device is updated if a check fails. + */ +int iommu_group_update_devices(struct iommu_group *group, void *data, + int (*check)(struct device *, void *), + void (*update)(struct device *, void *)) +{ + struct group_device *device; + int ret = 0; + + mutex_lock(&group->mutex); + for_each_group_device(group, device) { + ret = check(device->dev, data); + if (ret) + goto unlock; + } + + for_each_group_device(group, device) + update(device->dev, data); + +unlock: + mutex_unlock(&group->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(iommu_group_update_devices); + /** * iommu_group_get - Return the group for a device and increment reference * @dev: get the group that this device belongs to diff --git a/include/linux/iommu.h b/include/linux/iommu.h index e771b4a92f5b..befba0683e06 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -988,6 +988,9 @@ extern int iommu_group_add_device(struct iommu_group *group, extern void iommu_group_remove_device(struct device *dev); extern int iommu_group_for_each_dev(struct iommu_group *group, void *data, int (*fn)(struct device *, void *)); +int iommu_group_update_devices(struct iommu_group *group, void *data, + int (*check)(struct device *, void *), + void (*update)(struct device *, void *)); extern struct iommu_group *iommu_group_get(struct device *dev); struct iommu_group *iommu_group_get_by_id(int id); void iommu_group_put_by_id(struct iommu_group *group); @@ -1399,6 +1402,16 @@ static inline int iommu_group_for_each_dev(struct iommu_group *group, return -ENODEV; } +static inline int iommu_group_update_devices(struct iommu_group *group, + void *data, + int (*check)(struct device *, + void *), + void (*update)(struct device *, + void *)) +{ + return -ENODEV; +} + static inline struct iommu_group *iommu_group_get(struct device *dev) { return NULL; -- 2.50.1 (Apple Git-155)

