IOMMU UAPI can be extended in the future by adding new fields at the end of each user data structure. Since we use a unified UAPI version for compatibility checking, a lookup function is needed to find the correct user data size to copy from user.
This patch adds a helper function based on a 2D lookup with version and type as input arguments. --- v2: Clarify size lookup array extension rules, backfill -EINVAL if new version introduce new union members. --- Signed-off-by: Liu Yi L <yi.l....@intel.com> Signed-off-by: Jacob Pan <jacob.jun....@linux.intel.com> --- drivers/iommu/iommu.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 6 +++++ 2 files changed, 78 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index c476b58e0ffb..e91ced212653 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1724,6 +1724,78 @@ int iommu_sva_unbind_gpasid(struct iommu_domain *domain, struct device *dev, } EXPORT_SYMBOL_GPL(iommu_sva_unbind_gpasid); + +/** + * Maintain a UAPI version to user data structure size lookup for each + * API function types we support. e.g. bind guest pasid, cache invalidation. + * As data structures being extended with new members, offsetofend() is + * used to identify the size. In case of adding a new member to a union, + * offsetofend() applies to the largest member which may not be the newest. + * + * When new types are introduced with new versions, the new types for older + * version must be filled with -EINVAL. + * + * The table below documents UAPI revision history with the name of the + * newest member of each data structure. The largest member of a union was + * used for the initial version of each type. + * + * +--------------+---------------+ + * | Type / | V1 | + * | UAPI Version | | + * +==============+===============+ + * | BIND_GPASID | vtd | + * +--------------+---------------+ + * | CACHE_INVAL | addr_info | + * +--------------+---------------+ + * | PAGE_RESP | code | + * +--------------+---------------+ + * + * Examples of future extensions: + * V2 addes new member to the union + * +--------------+---------------+---------------+ + * | Type / | V1 | V2 | + * | UAPI Version | | | + * +==============+===============+===============+ + * | BIND_GPASID | vtd | smmu_v3 | + * +--------------+---------------+---------------+ + * | CACHE_INVAL | addr_info | new_info | + * +--------------+---------------+---------------+ + * | PAGE_RESP | code | N/A | + * +--------------+---------------+---------------+ + * + * V3 introduces a new UAPI data type: NEW_TYPE but with no new members + * added to the existing types. + * +--------------+---------------+---------------+---------------+ + * | Type / | V1 | V2 | V3 | + * | UAPI Version | | | | + * +==============+===============+===============+===============+ + * | BIND_GPASID | vtd | smmu_v3 | N/A | + * +--------------+---------------+---------------+---------------+ + * | CACHE_INVAL | addr_info | new_info | N/A | + * +--------------+---------------+---------------+---------------+ + * | PAGE_RESP | code | N/A | N/A | + * +--------------+---------------+---------------+---------------+ + * | NEW_TYPE | -EINVAL | -EINVAL | largest_member| + * +--------------+---------------+---------------+---------------+ + */ +const static int iommu_uapi_data_size[NR_IOMMU_UAPI_TYPE][IOMMU_UAPI_VERSION] = { + /* IOMMU_UAPI_BIND_GPASID */ + {offsetofend(struct iommu_gpasid_bind_data, vtd)}, + /* IOMMU_UAPI_CACHE_INVAL */ + {offsetofend(struct iommu_cache_invalidate_info, addr_info)}, + /* IOMMU_UAPI_PAGE_RESP */ + {offsetofend(struct iommu_page_response, code)}, +}; + +int iommu_uapi_get_data_size(int type, int version) +{ + if (type >= NR_IOMMU_UAPI_TYPE || version > IOMMU_UAPI_VERSION) + return -EINVAL; + + return iommu_uapi_data_size[type][version - 1]; +} +EXPORT_SYMBOL_GPL(iommu_uapi_get_data_size); + static void __iommu_detach_device(struct iommu_domain *domain, struct device *dev) { diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d1b5f4d98569..4908919a98f1 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -510,6 +510,7 @@ extern int iommu_report_device_fault(struct device *dev, struct iommu_fault_event *evt); extern int iommu_page_response(struct device *dev, struct iommu_page_response *msg); +extern int iommu_uapi_get_data_size(int type, int version); extern int iommu_group_id(struct iommu_group *group); extern struct iommu_group *iommu_group_get_for_dev(struct device *dev); @@ -897,6 +898,11 @@ static inline int iommu_page_response(struct device *dev, return -ENODEV; } +static inline int iommu_uapi_get_data_size(int type, int version) +{ + return -ENODEV; +} + static inline int iommu_group_id(struct iommu_group *group) { return -ENODEV; -- 2.7.4 _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu