2020-01-29 05:58:11

by Jacob Pan

[permalink] [raw]
Subject: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

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.

Signed-off-by: Liu Yi L <[email protected]>
Signed-off-by: Jacob Pan <[email protected]>
---
drivers/iommu/iommu.c | 22 ++++++++++++++++++++++
include/linux/iommu.h | 6 ++++++
2 files changed, 28 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 7dd51c5d2ba1..9e5de9abebdf 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1696,6 +1696,28 @@ 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, the offsetofend()
+ * will identify the new sizes.
+ */
+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)
+{
+ 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 9718c109ea0a..416fe02160ba 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -500,6 +500,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);
@@ -885,6 +886,11 @@ static inline int iommu_page_response(struct device *dev,
return -ENODEV;
}

+static 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


2020-01-29 21:43:22

by Alex Williamson

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Tue, 28 Jan 2020 22:02:04 -0800
Jacob Pan <[email protected]> wrote:

> 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.
>
> Signed-off-by: Liu Yi L <[email protected]>
> Signed-off-by: Jacob Pan <[email protected]>
> ---
> drivers/iommu/iommu.c | 22 ++++++++++++++++++++++
> include/linux/iommu.h | 6 ++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index 7dd51c5d2ba1..9e5de9abebdf 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1696,6 +1696,28 @@ 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, the offsetofend()
> + * will identify the new sizes.
> + */
> +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)
> +{

Seems like this is asking for a bounds check,

if (type >= NR_IOMMU_UAPI_TYPE || version > IOMMU_UAPI_VERSION)
return -EINVAL;

If we add new types in future versions, I assume we'd back fill the
table with -EINVAL as well (rather than zero). Thanks,

Alex

> + 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 9718c109ea0a..416fe02160ba 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -500,6 +500,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);
> @@ -885,6 +886,11 @@ static inline int iommu_page_response(struct device *dev,
> return -ENODEV;
> }
>
> +static int iommu_uapi_get_data_size(int type, int version)
> +{
> + return -ENODEV;
> +}
> +
> static inline int iommu_group_id(struct iommu_group *group)
> {
> return -ENODEV;

2020-01-29 22:22:34

by Alex Williamson

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Wed, 29 Jan 2020 14:40:46 -0700
Alex Williamson <[email protected]> wrote:

> On Tue, 28 Jan 2020 22:02:04 -0800
> Jacob Pan <[email protected]> wrote:
>
> > 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.
> >
> > Signed-off-by: Liu Yi L <[email protected]>
> > Signed-off-by: Jacob Pan <[email protected]>
> > ---
> > drivers/iommu/iommu.c | 22 ++++++++++++++++++++++
> > include/linux/iommu.h | 6 ++++++
> > 2 files changed, 28 insertions(+)
> >
> > diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> > index 7dd51c5d2ba1..9e5de9abebdf 100644
> > --- a/drivers/iommu/iommu.c
> > +++ b/drivers/iommu/iommu.c
> > @@ -1696,6 +1696,28 @@ 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, the offsetofend()
> > + * will identify the new sizes.
> > + */
> > +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)},

This seems prone to errors in future revisions. Both of the above
reference the end of fields within an anonymous union. When a new
field is added, it's not necessarily the newest field that needs to be
listed here, but the largest at the time. So should the current
version always use sizeof instead (or name the union so we can
reference it)? I'm not sure of an error proof way to make sure we keep
the N-1 version consistent when we add a new version though. More
comments?

Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
excessive with this new versioning scheme? Per rule #2 I'm not sure if
we're allowed to repurpose those padding bytes, but if we add fields to
the end of the structure as the scheme suggests, we're stuck with not
being able to expand the union for new fields.

Thanks,
Alex

> > + /* IOMMU_UAPI_PAGE_RESP */
> > + {offsetofend(struct iommu_page_response, code)},
> > +};
> > +
> > +int iommu_uapi_get_data_size(int type, int version)
> > +{
>
> Seems like this is asking for a bounds check,
>
> if (type >= NR_IOMMU_UAPI_TYPE || version > IOMMU_UAPI_VERSION)
> return -EINVAL;
>
> If we add new types in future versions, I assume we'd back fill the
> table with -EINVAL as well (rather than zero). Thanks,
>
> Alex
>
> > + 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 9718c109ea0a..416fe02160ba 100644
> > --- a/include/linux/iommu.h
> > +++ b/include/linux/iommu.h
> > @@ -500,6 +500,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);
> > @@ -885,6 +886,11 @@ static inline int iommu_page_response(struct device *dev,
> > return -ENODEV;
> > }
> >
> > +static int iommu_uapi_get_data_size(int type, int version)
> > +{
> > + return -ENODEV;
> > +}
> > +
> > static inline int iommu_group_id(struct iommu_group *group)
> > {
> > return -ENODEV;
>

2020-01-31 17:53:42

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Wed, 29 Jan 2020 14:40:46 -0700
Alex Williamson <[email protected]> wrote:

> On Tue, 28 Jan 2020 22:02:04 -0800
> Jacob Pan <[email protected]> wrote:
>
> > 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.
> >
> > Signed-off-by: Liu Yi L <[email protected]>
> > Signed-off-by: Jacob Pan <[email protected]>
> > ---
> > drivers/iommu/iommu.c | 22 ++++++++++++++++++++++
> > include/linux/iommu.h | 6 ++++++
> > 2 files changed, 28 insertions(+)
> >
> > diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> > index 7dd51c5d2ba1..9e5de9abebdf 100644
> > --- a/drivers/iommu/iommu.c
> > +++ b/drivers/iommu/iommu.c
> > @@ -1696,6 +1696,28 @@ 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, the
> > offsetofend()
> > + * will identify the new sizes.
> > + */
> > +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)
> > +{
>
> Seems like this is asking for a bounds check,
>
> if (type >= NR_IOMMU_UAPI_TYPE || version > IOMMU_UAPI_VERSION)
> return -EINVAL;
>
yes, agreed.
> If we add new types in future versions, I assume we'd back fill the
> table with -EINVAL as well (rather than zero). Thanks,
>
right, if the array increase due to new types, the older version with
the new type should be filled with -EINVAL.
Let me document this in the rules of extensions.

> Alex
>
> > + 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 9718c109ea0a..416fe02160ba 100644
> > --- a/include/linux/iommu.h
> > +++ b/include/linux/iommu.h
> > @@ -500,6 +500,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); @@ -885,6 +886,11 @@ static inline int
> > iommu_page_response(struct device *dev, return -ENODEV;
> > }
> >
> > +static int iommu_uapi_get_data_size(int type, int version)
> > +{
> > + return -ENODEV;
> > +}
> > +
> > static inline int iommu_group_id(struct iommu_group *group)
> > {
> > return -ENODEV;
>

[Jacob Pan]

2020-01-31 19:48:08

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Wed, 29 Jan 2020 15:19:51 -0700
Alex Williamson <[email protected]> wrote:

> On Wed, 29 Jan 2020 14:40:46 -0700
> Alex Williamson <[email protected]> wrote:
>
> > On Tue, 28 Jan 2020 22:02:04 -0800
> > Jacob Pan <[email protected]> wrote:
> >
> > > 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.
> > >
> > > Signed-off-by: Liu Yi L <[email protected]>
> > > Signed-off-by: Jacob Pan <[email protected]>
> > > ---
> > > drivers/iommu/iommu.c | 22 ++++++++++++++++++++++
> > > include/linux/iommu.h | 6 ++++++
> > > 2 files changed, 28 insertions(+)
> > >
> > > diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> > > index 7dd51c5d2ba1..9e5de9abebdf 100644
> > > --- a/drivers/iommu/iommu.c
> > > +++ b/drivers/iommu/iommu.c
> > > @@ -1696,6 +1696,28 @@ 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, the
> > > offsetofend()
> > > + * will identify the new sizes.
> > > + */
> > > +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)},
>
> This seems prone to errors in future revisions. Both of the above
> reference the end of fields within an anonymous union. When a new
> field is added, it's not necessarily the newest field that needs to be
> listed here, but the largest at the time.
> So should the current
> version always use sizeof instead (or name the union so we can
> reference it)? I'm not sure of an error proof way to make sure we
> keep the N-1 version consistent when we add a new version though.
> More comments?
>
yes. we must be very careful the new size has to be the largest of the
union not the newest. I agree that using sizeof() would make current
version size straightforward. But we have to find the size for N-1
with offsetofend, seems more risk to the existing N-1 code when bump up
version.

I was thinking this array also serve as documentation of the revision
history, but as you pointed out offsetofend may not be the newest
member of the union. So it cannot document which member was added in
which version. Seems more comments is the only way.

How about comments as below with example future extensions?

/**
* 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.
*
* Current version: V1
* +--------------+---------------+
* | Type / | V1 |
* | UAPI Version | |
* +==============+===============+
* | BIND_GPASID | vtd |
* +--------------+---------------+
* | CACHE_INVAL | addr_info |
* +--------------+---------------+
* | PAGE_RESP | code |
* +--------------+---------------+
*
* Future extension examples:
*
* V2 adds new members to bind_gpasid and cache_invalidate API but not
* page response.
* +--------------+---------------+---------------+
* | 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|
* +--------------+---------------+---------------+---------------+
*

> Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
> excessive with this new versioning scheme? Per rule #2 I'm not sure
> if we're allowed to repurpose those padding bytes, but if we add
> fields to the end of the structure as the scheme suggests, we're
> stuck with not being able to expand the union for new fields.
>
> Thanks,
> Alex
>
> > > + /* IOMMU_UAPI_PAGE_RESP */
> > > + {offsetofend(struct iommu_page_response, code)},
> > > +};
> > > +
> > > +int iommu_uapi_get_data_size(int type, int version)
> > > +{
> >
> > Seems like this is asking for a bounds check,
> >
> > if (type >= NR_IOMMU_UAPI_TYPE || version > IOMMU_UAPI_VERSION)
> > return -EINVAL;
> >
> > If we add new types in future versions, I assume we'd back fill the
> > table with -EINVAL as well (rather than zero). Thanks,
> >
> > Alex
> >
> > > + 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 9718c109ea0a..416fe02160ba 100644
> > > --- a/include/linux/iommu.h
> > > +++ b/include/linux/iommu.h
> > > @@ -500,6 +500,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); @@ -885,6 +886,11 @@ static inline int
> > > iommu_page_response(struct device *dev, return -ENODEV;
> > > }
> > >
> > > +static int iommu_uapi_get_data_size(int type, int version)
> > > +{
> > > + return -ENODEV;
> > > +}
> > > +
> > > static inline int iommu_group_id(struct iommu_group *group)
> > > {
> > > return -ENODEV;
> >
>

[Jacob Pan]

2020-01-31 23:50:22

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

Hi Alex,
Sorry I missed this part in the previous reply. Comments below.

On Wed, 29 Jan 2020 15:19:51 -0700
Alex Williamson <[email protected]> wrote:

> Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
> excessive with this new versioning scheme? Per rule #2 I'm not sure
> if we're allowed to repurpose those padding bytes,
We can still use the padding bytes as long as there is a new flag bit
to indicate the validity of the new filed within the padding.
I should have made it clear in rule #2 when mentioning the flags bits.
Should define what extension constitutes.
How about this?
"
* 2. Data structures are open to extension but closed to modification.
* Extension should leverage the padding bytes first where a new
* flag bit is required to indicate the validity of each new member.
* The above rule for padding bytes also applies to adding new union
* members.
* After padding bytes are exhausted, new fields must be added at the
* end of each data structure with 64bit alignment. Flag bits can be
* added without size change but existing ones cannot be altered.
*
"
So if we add new field by doing re-purpose of padding bytes, size
lookup result will remain the same. New code would recognize the new
flag, old code stays the same.

VFIO layer checks for UAPI compatibility and size to copy, version
sanity check and flag usage are done in the IOMMU code.

> but if we add
> fields to the end of the structure as the scheme suggests, we're
> stuck with not being able to expand the union for new fields.
Good point, it does sound contradictory. I hope the rewritten rule #2
address that.
Adding data after the union should be extremely rare. Do you see any
issues with the example below?

offsetofend() can still find the right size.
e.g.
V1
struct iommu_gpasid_bind_data {
__u32 version;
#define IOMMU_PASID_FORMAT_INTEL_VTD 1
__u32 format;
#define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
__u64 flags;
__u64 gpgd;
__u64 hpasid;
__u64 gpasid;
__u32 addr_width;
__u8 padding[12];
/* Vendor specific data */
union {
struct iommu_gpasid_bind_data_vtd vtd;
};
};

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)}, ...
};

V2, Add new_member at the end (forget padding for now).
struct iommu_gpasid_bind_data {
__u32 version;
#define IOMMU_PASID_FORMAT_INTEL_VTD 1
__u32 format;
#define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
#define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
__u64 flags;
__u64 gpgd;
__u64 hpasid;
__u64 gpasid;
__u32 addr_width;
__u8 padding[12];
/* Vendor specific data */
union {
struct iommu_gpasid_bind_data_vtd vtd;
};
__u64 new_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), offsetofend(struct iommu_gpasid_bind_data,new_member)},

};

V3, Add smmu to the union,larger than vtd

struct iommu_gpasid_bind_data {
__u32 version;
#define IOMMU_PASID_FORMAT_INTEL_VTD 1
#define IOMMU_PASID_FORMAT_INTEL_SMMU 2
__u32 format;
#define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
#define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
#define IOMMU_SVA_SMMU_SUPP (1 << 2) /* SMMU data supported */
__u64 flags;
__u64 gpgd;
__u64 hpasid;
__u64 gpasid;
__u32 addr_width;
__u8 padding[12];
/* Vendor specific data */
union {
struct iommu_gpasid_bind_data_vtd vtd;
struct iommu_gpasid_bind_data_smmu smmu;
};
__u64 new_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),
offsetofend(struct iommu_gpasid_bind_data, new_member),
offsetofend(struct iommu_gpasid_bind_data, new_member)},
...
};

2020-02-03 19:13:41

by Alex Williamson

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Fri, 31 Jan 2020 15:51:25 -0800
Jacob Pan <[email protected]> wrote:

> Hi Alex,
> Sorry I missed this part in the previous reply. Comments below.
>
> On Wed, 29 Jan 2020 15:19:51 -0700
> Alex Williamson <[email protected]> wrote:
>
> > Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
> > excessive with this new versioning scheme? Per rule #2 I'm not sure
> > if we're allowed to repurpose those padding bytes,
> We can still use the padding bytes as long as there is a new flag bit
> to indicate the validity of the new filed within the padding.
> I should have made it clear in rule #2 when mentioning the flags bits.
> Should define what extension constitutes.
> How about this?
> "
> * 2. Data structures are open to extension but closed to modification.
> * Extension should leverage the padding bytes first where a new
> * flag bit is required to indicate the validity of each new member.
> * The above rule for padding bytes also applies to adding new union
> * members.
> * After padding bytes are exhausted, new fields must be added at the
> * end of each data structure with 64bit alignment. Flag bits can be
> * added without size change but existing ones cannot be altered.
> *
> "
> So if we add new field by doing re-purpose of padding bytes, size
> lookup result will remain the same. New code would recognize the new
> flag, old code stays the same.
>
> VFIO layer checks for UAPI compatibility and size to copy, version
> sanity check and flag usage are done in the IOMMU code.
>
> > but if we add
> > fields to the end of the structure as the scheme suggests, we're
> > stuck with not being able to expand the union for new fields.
> Good point, it does sound contradictory. I hope the rewritten rule #2
> address that.
> Adding data after the union should be extremely rare. Do you see any
> issues with the example below?
>
> offsetofend() can still find the right size.
> e.g.
> V1
> struct iommu_gpasid_bind_data {
> __u32 version;
> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> __u32 format;
> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> __u64 flags;
> __u64 gpgd;
> __u64 hpasid;
> __u64 gpasid;
> __u32 addr_width;
> __u8 padding[12];
> /* Vendor specific data */
> union {
> struct iommu_gpasid_bind_data_vtd vtd;
> };
> };
>
> 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)}, ...
> };
>
> V2, Add new_member at the end (forget padding for now).
> struct iommu_gpasid_bind_data {
> __u32 version;
> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> __u32 format;
> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> __u64 flags;
> __u64 gpgd;
> __u64 hpasid;
> __u64 gpasid;
> __u32 addr_width;
> __u8 padding[12];
> /* Vendor specific data */
> union {
> struct iommu_gpasid_bind_data_vtd vtd;
> };
> __u64 new_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), offsetofend(struct iommu_gpasid_bind_data,new_member)},
>
> };
>
> V3, Add smmu to the union,larger than vtd
>
> struct iommu_gpasid_bind_data {
> __u32 version;
> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> #define IOMMU_PASID_FORMAT_INTEL_SMMU 2
> __u32 format;
> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> #define IOMMU_SVA_SMMU_SUPP (1 << 2) /* SMMU data supported */
> __u64 flags;
> __u64 gpgd;
> __u64 hpasid;
> __u64 gpasid;
> __u32 addr_width;
> __u8 padding[12];
> /* Vendor specific data */
> union {
> struct iommu_gpasid_bind_data_vtd vtd;
> struct iommu_gpasid_bind_data_smmu smmu;
> };
> __u64 new_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),
> offsetofend(struct iommu_gpasid_bind_data, new_member),
> offsetofend(struct iommu_gpasid_bind_data, new_member)},
> ...
> };
>

How are you not breaking rule #3, "Versions are backward compatible"
with this? If the kernel is at version 3 and userspace is at version 2
then new_member exists at different offsets of the structure. The
kernels iommu_uapi_data_size for V2 changed between version 2 and 3.
Thanks,

Alex

2020-02-03 20:38:33

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Mon, 3 Feb 2020 11:27:08 -0700
Alex Williamson <[email protected]> wrote:

> On Fri, 31 Jan 2020 15:51:25 -0800
> Jacob Pan <[email protected]> wrote:
>
> > Hi Alex,
> > Sorry I missed this part in the previous reply. Comments below.
> >
> > On Wed, 29 Jan 2020 15:19:51 -0700
> > Alex Williamson <[email protected]> wrote:
> >
> > > Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
> > > excessive with this new versioning scheme? Per rule #2 I'm not
> > > sure if we're allowed to repurpose those padding bytes,
> > We can still use the padding bytes as long as there is a new flag
> > bit to indicate the validity of the new filed within the padding.
> > I should have made it clear in rule #2 when mentioning the flags
> > bits. Should define what extension constitutes.
> > How about this?
> > "
> > * 2. Data structures are open to extension but closed to
> > modification.
> > * Extension should leverage the padding bytes first where a new
> > * flag bit is required to indicate the validity of each new
> > member.
> > * The above rule for padding bytes also applies to adding new
> > union
> > * members.
> > * After padding bytes are exhausted, new fields must be added
> > at the
> > * end of each data structure with 64bit alignment. Flag bits
> > can be
> > * added without size change but existing ones cannot be altered.
> > *
> > "
> > So if we add new field by doing re-purpose of padding bytes, size
> > lookup result will remain the same. New code would recognize the new
> > flag, old code stays the same.
> >
> > VFIO layer checks for UAPI compatibility and size to copy, version
> > sanity check and flag usage are done in the IOMMU code.
> >
> > > but if we add
> > > fields to the end of the structure as the scheme suggests, we're
> > > stuck with not being able to expand the union for new fields.
> > Good point, it does sound contradictory. I hope the rewritten rule
> > #2 address that.
> > Adding data after the union should be extremely rare. Do you see any
> > issues with the example below?
> >
> > offsetofend() can still find the right size.
> > e.g.
> > V1
> > struct iommu_gpasid_bind_data {
> > __u32 version;
> > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > __u32 format;
> > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > __u64 flags;
> > __u64 gpgd;
> > __u64 hpasid;
> > __u64 gpasid;
> > __u32 addr_width;
> > __u8 padding[12];
> > /* Vendor specific data */
> > union {
> > struct iommu_gpasid_bind_data_vtd vtd;
> > };
> > };
> >
> > 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)}, ...
> > };
> >
> > V2, Add new_member at the end (forget padding for now).
> > struct iommu_gpasid_bind_data {
> > __u32 version;
> > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > __u32 format;
> > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> > __u64 flags;
> > __u64 gpgd;
> > __u64 hpasid;
> > __u64 gpasid;
> > __u32 addr_width;
> > __u8 padding[12];
> > /* Vendor specific data */
> > union {
> > struct iommu_gpasid_bind_data_vtd vtd;
> > };
> > __u64 new_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), offsetofend(struct
> > iommu_gpasid_bind_data,new_member)},
> >
> > };
> >
> > V3, Add smmu to the union,larger than vtd
> >
> > struct iommu_gpasid_bind_data {
> > __u32 version;
> > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > #define IOMMU_PASID_FORMAT_INTEL_SMMU 2
> > __u32 format;
> > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> > #define IOMMU_SVA_SMMU_SUPP (1 << 2) /* SMMU data supported
> > */ __u64 flags;
> > __u64 gpgd;
> > __u64 hpasid;
> > __u64 gpasid;
> > __u32 addr_width;
> > __u8 padding[12];
> > /* Vendor specific data */
> > union {
> > struct iommu_gpasid_bind_data_vtd vtd;
> > struct iommu_gpasid_bind_data_smmu smmu;
> > };
> > __u64 new_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),
> > offsetofend(struct iommu_gpasid_bind_data, new_member),
> > offsetofend(struct iommu_gpasid_bind_data, new_member)},
> > ...
> > };
> >
>
> How are you not breaking rule #3, "Versions are backward compatible"
> with this? If the kernel is at version 3 and userspace is at version
> 2 then new_member exists at different offsets of the structure. The
> kernels iommu_uapi_data_size for V2 changed between version 2 and 3.
> Thanks,
>
You are right. if we want to add new member to the end of the structure
as well as expanding union, I think we have to fix the size of the
union. Would this work? (just an example for one struct)


@@ -344,6 +348,11 @@ struct iommu_gpasid_bind_data_vtd {
* @gpasid: Process address space ID used for the guest mm in guest
IOMMU
* @addr_width: Guest virtual address width
* @padding: Reserved for future use (should be zero)
+ * @dummy Reserve space for vendor specific data in the union. New
+ * members added to the union cannot exceed the size of
dummy.
+ * The fixed size union is needed to allow further
expansion
+ * after the end of the union while still maintain backward
+ * compatibility.
* @vtd: Intel VT-d specific data
*
* Guest to host PASID mapping can be an identity or non-identity,
where guest @@ -365,6 +374,7 @@ struct iommu_gpasid_bind_data {
__u8 padding[12];
/* Vendor specific data */
union {
+ __u8 dummy[128];
struct iommu_gpasid_bind_data_vtd vtd;
};
};

> Alex
>

[Jacob Pan]

2020-02-03 21:13:57

by Alex Williamson

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Mon, 3 Feb 2020 12:41:43 -0800
Jacob Pan <[email protected]> wrote:

> On Mon, 3 Feb 2020 11:27:08 -0700
> Alex Williamson <[email protected]> wrote:
>
> > On Fri, 31 Jan 2020 15:51:25 -0800
> > Jacob Pan <[email protected]> wrote:
> >
> > > Hi Alex,
> > > Sorry I missed this part in the previous reply. Comments below.
> > >
> > > On Wed, 29 Jan 2020 15:19:51 -0700
> > > Alex Williamson <[email protected]> wrote:
> > >
> > > > Also, is the 12-bytes of padding in struct iommu_gpasid_bind_data
> > > > excessive with this new versioning scheme? Per rule #2 I'm not
> > > > sure if we're allowed to repurpose those padding bytes,
> > > We can still use the padding bytes as long as there is a new flag
> > > bit to indicate the validity of the new filed within the padding.
> > > I should have made it clear in rule #2 when mentioning the flags
> > > bits. Should define what extension constitutes.
> > > How about this?
> > > "
> > > * 2. Data structures are open to extension but closed to
> > > modification.
> > > * Extension should leverage the padding bytes first where a new
> > > * flag bit is required to indicate the validity of each new
> > > member.
> > > * The above rule for padding bytes also applies to adding new
> > > union
> > > * members.
> > > * After padding bytes are exhausted, new fields must be added
> > > at the
> > > * end of each data structure with 64bit alignment. Flag bits
> > > can be
> > > * added without size change but existing ones cannot be altered.
> > > *
> > > "
> > > So if we add new field by doing re-purpose of padding bytes, size
> > > lookup result will remain the same. New code would recognize the new
> > > flag, old code stays the same.
> > >
> > > VFIO layer checks for UAPI compatibility and size to copy, version
> > > sanity check and flag usage are done in the IOMMU code.
> > >
> > > > but if we add
> > > > fields to the end of the structure as the scheme suggests, we're
> > > > stuck with not being able to expand the union for new fields.
> > > Good point, it does sound contradictory. I hope the rewritten rule
> > > #2 address that.
> > > Adding data after the union should be extremely rare. Do you see any
> > > issues with the example below?
> > >
> > > offsetofend() can still find the right size.
> > > e.g.
> > > V1
> > > struct iommu_gpasid_bind_data {
> > > __u32 version;
> > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > __u32 format;
> > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > > __u64 flags;
> > > __u64 gpgd;
> > > __u64 hpasid;
> > > __u64 gpasid;
> > > __u32 addr_width;
> > > __u8 padding[12];
> > > /* Vendor specific data */
> > > union {
> > > struct iommu_gpasid_bind_data_vtd vtd;
> > > };
> > > };
> > >
> > > 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)}, ...
> > > };
> > >
> > > V2, Add new_member at the end (forget padding for now).
> > > struct iommu_gpasid_bind_data {
> > > __u32 version;
> > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > __u32 format;
> > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > > #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> > > __u64 flags;
> > > __u64 gpgd;
> > > __u64 hpasid;
> > > __u64 gpasid;
> > > __u32 addr_width;
> > > __u8 padding[12];
> > > /* Vendor specific data */
> > > union {
> > > struct iommu_gpasid_bind_data_vtd vtd;
> > > };
> > > __u64 new_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), offsetofend(struct
> > > iommu_gpasid_bind_data,new_member)},
> > >
> > > };
> > >
> > > V3, Add smmu to the union,larger than vtd
> > >
> > > struct iommu_gpasid_bind_data {
> > > __u32 version;
> > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > #define IOMMU_PASID_FORMAT_INTEL_SMMU 2
> > > __u32 format;
> > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */
> > > #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new member added */
> > > #define IOMMU_SVA_SMMU_SUPP (1 << 2) /* SMMU data supported
> > > */ __u64 flags;
> > > __u64 gpgd;
> > > __u64 hpasid;
> > > __u64 gpasid;
> > > __u32 addr_width;
> > > __u8 padding[12];
> > > /* Vendor specific data */
> > > union {
> > > struct iommu_gpasid_bind_data_vtd vtd;
> > > struct iommu_gpasid_bind_data_smmu smmu;
> > > };
> > > __u64 new_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),
> > > offsetofend(struct iommu_gpasid_bind_data, new_member),
> > > offsetofend(struct iommu_gpasid_bind_data, new_member)},
> > > ...
> > > };
> > >
> >
> > How are you not breaking rule #3, "Versions are backward compatible"
> > with this? If the kernel is at version 3 and userspace is at version
> > 2 then new_member exists at different offsets of the structure. The
> > kernels iommu_uapi_data_size for V2 changed between version 2 and 3.
> > Thanks,
> >
> You are right. if we want to add new member to the end of the structure
> as well as expanding union, I think we have to fix the size of the
> union. Would this work? (just an example for one struct)
>
>
> @@ -344,6 +348,11 @@ struct iommu_gpasid_bind_data_vtd {
> * @gpasid: Process address space ID used for the guest mm in guest
> IOMMU
> * @addr_width: Guest virtual address width
> * @padding: Reserved for future use (should be zero)
> + * @dummy Reserve space for vendor specific data in the union. New
> + * members added to the union cannot exceed the size of
> dummy.
> + * The fixed size union is needed to allow further
> expansion
> + * after the end of the union while still maintain backward
> + * compatibility.
> * @vtd: Intel VT-d specific data
> *
> * Guest to host PASID mapping can be an identity or non-identity,
> where guest @@ -365,6 +374,7 @@ struct iommu_gpasid_bind_data {
> __u8 padding[12];
> /* Vendor specific data */
> union {
> + __u8 dummy[128];
> struct iommu_gpasid_bind_data_vtd vtd;
> };
> };

It's not the most space efficient thing and we're just guessing at what
might need to be added into that union in future, but it works... until
it doesn't ;) One might also argue that we could inflate the padding
field even further to serve the same purpose. The only other route I
can think of would be to let the user specify the offset of the
variable size data from the start of the structure, for example similar
to how we're laying out vfio migration region or how we do capabilities
in vfio ioctls. This is where passing an argsz for each ioctl comes in
handy so we're not limited to a structure, we can link various
structures together in a chain. Of course that requires work on both
the user and kernel side to pack and unpack, but it leaves a lot of
flexibility in extending it. Thanks,

Alex

2020-02-03 22:38:30

by Jacob Pan

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Mon, 3 Feb 2020 14:12:36 -0700
Alex Williamson <[email protected]> wrote:

> On Mon, 3 Feb 2020 12:41:43 -0800
> Jacob Pan <[email protected]> wrote:
>
> > On Mon, 3 Feb 2020 11:27:08 -0700
> > Alex Williamson <[email protected]> wrote:
> >
> > > On Fri, 31 Jan 2020 15:51:25 -0800
> > > Jacob Pan <[email protected]> wrote:
> > >
> > > > Hi Alex,
> > > > Sorry I missed this part in the previous reply. Comments below.
> > > >
> > > > On Wed, 29 Jan 2020 15:19:51 -0700
> > > > Alex Williamson <[email protected]> wrote:
> > > >
> > > > > Also, is the 12-bytes of padding in struct
> > > > > iommu_gpasid_bind_data excessive with this new versioning
> > > > > scheme? Per rule #2 I'm not sure if we're allowed to
> > > > > repurpose those padding bytes,
> > > > We can still use the padding bytes as long as there is a new
> > > > flag bit to indicate the validity of the new filed within the
> > > > padding. I should have made it clear in rule #2 when mentioning
> > > > the flags bits. Should define what extension constitutes.
> > > > How about this?
> > > > "
> > > > * 2. Data structures are open to extension but closed to
> > > > modification.
> > > > * Extension should leverage the padding bytes first where a
> > > > new
> > > > * flag bit is required to indicate the validity of each new
> > > > member.
> > > > * The above rule for padding bytes also applies to adding
> > > > new union
> > > > * members.
> > > > * After padding bytes are exhausted, new fields must be
> > > > added at the
> > > > * end of each data structure with 64bit alignment. Flag bits
> > > > can be
> > > > * added without size change but existing ones cannot be
> > > > altered. *
> > > > "
> > > > So if we add new field by doing re-purpose of padding bytes,
> > > > size lookup result will remain the same. New code would
> > > > recognize the new flag, old code stays the same.
> > > >
> > > > VFIO layer checks for UAPI compatibility and size to copy,
> > > > version sanity check and flag usage are done in the IOMMU code.
> > > >
> > > > > but if we add
> > > > > fields to the end of the structure as the scheme suggests,
> > > > > we're stuck with not being able to expand the union for new
> > > > > fields.
> > > > Good point, it does sound contradictory. I hope the rewritten
> > > > rule #2 address that.
> > > > Adding data after the union should be extremely rare. Do you
> > > > see any issues with the example below?
> > > >
> > > > offsetofend() can still find the right size.
> > > > e.g.
> > > > V1
> > > > struct iommu_gpasid_bind_data {
> > > > __u32 version;
> > > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > > __u32 format;
> > > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
> > > > valid */ __u64 flags;
> > > > __u64 gpgd;
> > > > __u64 hpasid;
> > > > __u64 gpasid;
> > > > __u32 addr_width;
> > > > __u8 padding[12];
> > > > /* Vendor specific data */
> > > > union {
> > > > struct iommu_gpasid_bind_data_vtd vtd;
> > > > };
> > > > };
> > > >
> > > > 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)}, ...
> > > > };
> > > >
> > > > V2, Add new_member at the end (forget padding for now).
> > > > struct iommu_gpasid_bind_data {
> > > > __u32 version;
> > > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > > __u32 format;
> > > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
> > > > valid */ #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new
> > > > member added */ __u64 flags;
> > > > __u64 gpgd;
> > > > __u64 hpasid;
> > > > __u64 gpasid;
> > > > __u32 addr_width;
> > > > __u8 padding[12];
> > > > /* Vendor specific data */
> > > > union {
> > > > struct iommu_gpasid_bind_data_vtd vtd;
> > > > };
> > > > __u64 new_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), offsetofend(struct
> > > > iommu_gpasid_bind_data,new_member)},
> > > >
> > > > };
> > > >
> > > > V3, Add smmu to the union,larger than vtd
> > > >
> > > > struct iommu_gpasid_bind_data {
> > > > __u32 version;
> > > > #define IOMMU_PASID_FORMAT_INTEL_VTD 1
> > > > #define IOMMU_PASID_FORMAT_INTEL_SMMU 2
> > > > __u32 format;
> > > > #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
> > > > valid */ #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new
> > > > member added */ #define IOMMU_SVA_SMMU_SUPP (1 << 2) /*
> > > > SMMU data supported */ __u64 flags;
> > > > __u64 gpgd;
> > > > __u64 hpasid;
> > > > __u64 gpasid;
> > > > __u32 addr_width;
> > > > __u8 padding[12];
> > > > /* Vendor specific data */
> > > > union {
> > > > struct iommu_gpasid_bind_data_vtd vtd;
> > > > struct iommu_gpasid_bind_data_smmu smmu;
> > > > };
> > > > __u64 new_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),
> > > > offsetofend(struct iommu_gpasid_bind_data, new_member),
> > > > offsetofend(struct iommu_gpasid_bind_data, new_member)},
> > > > ...
> > > > };
> > > >
> > >
> > > How are you not breaking rule #3, "Versions are backward
> > > compatible" with this? If the kernel is at version 3 and
> > > userspace is at version 2 then new_member exists at different
> > > offsets of the structure. The kernels iommu_uapi_data_size for
> > > V2 changed between version 2 and 3. Thanks,
> > >
> > You are right. if we want to add new member to the end of the
> > structure as well as expanding union, I think we have to fix the
> > size of the union. Would this work? (just an example for one struct)
> >
> >
> > @@ -344,6 +348,11 @@ struct iommu_gpasid_bind_data_vtd {
> > * @gpasid: Process address space ID used for the guest mm in
> > guest IOMMU
> > * @addr_width: Guest virtual address width
> > * @padding: Reserved for future use (should be zero)
> > + * @dummy Reserve space for vendor specific data in the
> > union. New
> > + * members added to the union cannot exceed the size of
> > dummy.
> > + * The fixed size union is needed to allow further
> > expansion
> > + * after the end of the union while still maintain
> > backward
> > + * compatibility.
> > * @vtd: Intel VT-d specific data
> > *
> > * Guest to host PASID mapping can be an identity or non-identity,
> > where guest @@ -365,6 +374,7 @@ struct iommu_gpasid_bind_data {
> > __u8 padding[12];
> > /* Vendor specific data */
> > union {
> > + __u8 dummy[128];
> > struct iommu_gpasid_bind_data_vtd vtd;
> > };
> > };
>
> It's not the most space efficient thing and we're just guessing at
> what might need to be added into that union in future, but it
> works... until it doesn't ;) One might also argue that we could
> inflate the padding field even further to serve the same purpose.
That was our initial intention, the padding field is already inflated
to accommodate any foreseeable extensions :)

Extensions beyond union was deemed unlikely that is why we use the
union at the end.

The intention of this patchset is not to change that, but rather
clarify and simplify the version checking.

> The only other route I can think of would be to let the user specify
> the offset of the variable size data from the start of the structure,
> for example similar to how we're laying out vfio migration region or
> how we do capabilities in vfio ioctls. This is where passing an
> argsz for each ioctl comes in handy so we're not limited to a
> structure, we can link various structures together in a chain. Of
> course that requires work on both the user and kernel side to pack
> and unpack, but it leaves a lot of flexibility in extending it.
> Thanks,
>
Yeah, that would work as well. I just feel IOMMU UAPI is unlikely to get
updated frequently, should be much less than adding new capabilities.
I think argsz could be viewed as the version field set by the
user, minsz is what kernel current code supports.

So let me summarize the options we have
1. Disallow adding new members to each structure other than reuse
padding bits or adding union members at the end.
2. Allow extension of the structures beyond union, but union size has
to be fixed with reserved spaces
3. Adopt VFIO argsz scheme, I don't think we need version for each
struct anymore. argsz implies the version that user is using assuming
UAPI data is extension only.

Jean, Eric, any comments? My preference is #1. In the apocalyptic event
when we run out of padding, perhaps we can introduce a new API_v2 :)

> Alex
>

[Jacob Pan]

2020-02-06 10:47:07

by Eric Auger

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

Hi Jacob,
On 2/3/20 11:41 PM, Jacob Pan wrote:
> On Mon, 3 Feb 2020 14:12:36 -0700
> Alex Williamson <[email protected]> wrote:
>
>> On Mon, 3 Feb 2020 12:41:43 -0800
>> Jacob Pan <[email protected]> wrote:
>>
>>> On Mon, 3 Feb 2020 11:27:08 -0700
>>> Alex Williamson <[email protected]> wrote:
>>>
>>>> On Fri, 31 Jan 2020 15:51:25 -0800
>>>> Jacob Pan <[email protected]> wrote:
>>>>
>>>>> Hi Alex,
>>>>> Sorry I missed this part in the previous reply. Comments below.
>>>>>
>>>>> On Wed, 29 Jan 2020 15:19:51 -0700
>>>>> Alex Williamson <[email protected]> wrote:
>>>>>
>>>>>> Also, is the 12-bytes of padding in struct
>>>>>> iommu_gpasid_bind_data excessive with this new versioning
>>>>>> scheme? Per rule #2 I'm not sure if we're allowed to
>>>>>> repurpose those padding bytes,
>>>>> We can still use the padding bytes as long as there is a new
>>>>> flag bit to indicate the validity of the new filed within the
>>>>> padding. I should have made it clear in rule #2 when mentioning
>>>>> the flags bits. Should define what extension constitutes.
>>>>> How about this?
>>>>> "
>>>>> * 2. Data structures are open to extension but closed to
>>>>> modification.
>>>>> * Extension should leverage the padding bytes first where a
>>>>> new
>>>>> * flag bit is required to indicate the validity of each new
>>>>> member.
>>>>> * The above rule for padding bytes also applies to adding
>>>>> new union
>>>>> * members.
>>>>> * After padding bytes are exhausted, new fields must be
>>>>> added at the
>>>>> * end of each data structure with 64bit alignment. Flag bits
>>>>> can be
>>>>> * added without size change but existing ones cannot be
>>>>> altered. *
>>>>> "
>>>>> So if we add new field by doing re-purpose of padding bytes,
>>>>> size lookup result will remain the same. New code would
>>>>> recognize the new flag, old code stays the same.
>>>>>
>>>>> VFIO layer checks for UAPI compatibility and size to copy,
>>>>> version sanity check and flag usage are done in the IOMMU code.
>>>>>
>>>>>> but if we add
>>>>>> fields to the end of the structure as the scheme suggests,
>>>>>> we're stuck with not being able to expand the union for new
>>>>>> fields.
>>>>> Good point, it does sound contradictory. I hope the rewritten
>>>>> rule #2 address that.
>>>>> Adding data after the union should be extremely rare. Do you
>>>>> see any issues with the example below?
>>>>>
>>>>> offsetofend() can still find the right size.
>>>>> e.g.
>>>>> V1
>>>>> struct iommu_gpasid_bind_data {
>>>>> __u32 version;
>>>>> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
>>>>> __u32 format;
>>>>> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
>>>>> valid */ __u64 flags;
>>>>> __u64 gpgd;
>>>>> __u64 hpasid;
>>>>> __u64 gpasid;
>>>>> __u32 addr_width;
>>>>> __u8 padding[12];
>>>>> /* Vendor specific data */
>>>>> union {
>>>>> struct iommu_gpasid_bind_data_vtd vtd;
>>>>> };
>>>>> };
>>>>>
>>>>> 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)}, ...
>>>>> };
>>>>>
>>>>> V2, Add new_member at the end (forget padding for now).
>>>>> struct iommu_gpasid_bind_data {
>>>>> __u32 version;
>>>>> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
>>>>> __u32 format;
>>>>> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
>>>>> valid */ #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new
>>>>> member added */ __u64 flags;
>>>>> __u64 gpgd;
>>>>> __u64 hpasid;
>>>>> __u64 gpasid;
>>>>> __u32 addr_width;
>>>>> __u8 padding[12];
>>>>> /* Vendor specific data */
>>>>> union {
>>>>> struct iommu_gpasid_bind_data_vtd vtd;
>>>>> };
>>>>> __u64 new_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), offsetofend(struct
>>>>> iommu_gpasid_bind_data,new_member)},
>>>>>
>>>>> };
>>>>>
>>>>> V3, Add smmu to the union,larger than vtd
>>>>>
>>>>> struct iommu_gpasid_bind_data {
>>>>> __u32 version;
>>>>> #define IOMMU_PASID_FORMAT_INTEL_VTD 1
>>>>> #define IOMMU_PASID_FORMAT_INTEL_SMMU 2
>>>>> __u32 format;
>>>>> #define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID
>>>>> valid */ #define IOMMU_NEW_MEMBER_VAL (1 << 1) /* new
>>>>> member added */ #define IOMMU_SVA_SMMU_SUPP (1 << 2) /*
>>>>> SMMU data supported */ __u64 flags;
>>>>> __u64 gpgd;
>>>>> __u64 hpasid;
>>>>> __u64 gpasid;
>>>>> __u32 addr_width;
>>>>> __u8 padding[12];
>>>>> /* Vendor specific data */
>>>>> union {
>>>>> struct iommu_gpasid_bind_data_vtd vtd;
>>>>> struct iommu_gpasid_bind_data_smmu smmu;
>>>>> };
>>>>> __u64 new_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),
>>>>> offsetofend(struct iommu_gpasid_bind_data, new_member),
>>>>> offsetofend(struct iommu_gpasid_bind_data, new_member)},
>>>>> ...
>>>>> };
>>>>>
>>>>
>>>> How are you not breaking rule #3, "Versions are backward
>>>> compatible" with this? If the kernel is at version 3 and
>>>> userspace is at version 2 then new_member exists at different
>>>> offsets of the structure. The kernels iommu_uapi_data_size for
>>>> V2 changed between version 2 and 3. Thanks,
>>>>
>>> You are right. if we want to add new member to the end of the
>>> structure as well as expanding union, I think we have to fix the
>>> size of the union. Would this work? (just an example for one struct)
>>>
>>>
>>> @@ -344,6 +348,11 @@ struct iommu_gpasid_bind_data_vtd {
>>> * @gpasid: Process address space ID used for the guest mm in
>>> guest IOMMU
>>> * @addr_width: Guest virtual address width
>>> * @padding: Reserved for future use (should be zero)
>>> + * @dummy Reserve space for vendor specific data in the
>>> union. New
>>> + * members added to the union cannot exceed the size of
>>> dummy.
>>> + * The fixed size union is needed to allow further
>>> expansion
>>> + * after the end of the union while still maintain
>>> backward
>>> + * compatibility.
>>> * @vtd: Intel VT-d specific data
>>> *
>>> * Guest to host PASID mapping can be an identity or non-identity,
>>> where guest @@ -365,6 +374,7 @@ struct iommu_gpasid_bind_data {
>>> __u8 padding[12];
>>> /* Vendor specific data */
>>> union {
>>> + __u8 dummy[128];
>>> struct iommu_gpasid_bind_data_vtd vtd;
>>> };
>>> };
>>
>> It's not the most space efficient thing and we're just guessing at
>> what might need to be added into that union in future, but it
>> works... until it doesn't ;) One might also argue that we could
>> inflate the padding field even further to serve the same purpose.
> That was our initial intention, the padding field is already inflated
> to accommodate any foreseeable extensions :)
>
> Extensions beyond union was deemed unlikely that is why we use the
> union at the end.
>
> The intention of this patchset is not to change that, but rather
> clarify and simplify the version checking.
>
>> The only other route I can think of would be to let the user specify
>> the offset of the variable size data from the start of the structure,
>> for example similar to how we're laying out vfio migration region or
>> how we do capabilities in vfio ioctls. This is where passing an
>> argsz for each ioctl comes in handy so we're not limited to a
>> structure, we can link various structures together in a chain. Of
>> course that requires work on both the user and kernel side to pack
>> and unpack, but it leaves a lot of flexibility in extending it.
>> Thanks,
>>
> Yeah, that would work as well. I just feel IOMMU UAPI is unlikely to get
> updated frequently, should be much less than adding new capabilities.
> I think argsz could be viewed as the version field set by the
> user, minsz is what kernel current code supports.
>
> So let me summarize the options we have
> 1. Disallow adding new members to each structure other than reuse
> padding bits or adding union members at the end.
> 2. Allow extension of the structures beyond union, but union size has
> to be fixed with reserved spaces
> 3. Adopt VFIO argsz scheme, I don't think we need version for each
> struct anymore. argsz implies the version that user is using assuming
> UAPI data is extension only.
>
> Jean, Eric, any comments? My preference is #1. In the apocalyptic event
> when we run out of padding, perhaps we can introduce a new API_v2 :)
I had #1 in mind too.

Thanks

Eric
>
>> Alex
>>
>
> [Jacob Pan]
>

2020-02-07 08:49:16

by Jean-Philippe Brucker

[permalink] [raw]
Subject: Re: [PATCH 3/3] iommu/uapi: Add helper function for size lookup

On Mon, Feb 03, 2020 at 02:41:02PM -0800, Jacob Pan wrote:
> Yeah, that would work as well. I just feel IOMMU UAPI is unlikely to get
> updated frequently, should be much less than adding new capabilities.
> I think argsz could be viewed as the version field set by the
> user, minsz is what kernel current code supports.
>
> So let me summarize the options we have
> 1. Disallow adding new members to each structure other than reuse
> padding bits or adding union members at the end.
> 2. Allow extension of the structures beyond union, but union size has
> to be fixed with reserved spaces
> 3. Adopt VFIO argsz scheme, I don't think we need version for each
> struct anymore. argsz implies the version that user is using assuming
> UAPI data is extension only.
>
> Jean, Eric, any comments? My preference is #1. In the apocalyptic event
> when we run out of padding, perhaps we can introduce a new API_v2 :)

I agree, new extensions will most likely want to extend the vendor
specific structures at the end rather than introduce new common fields, so
I prefer #1 which avoids fixing the union size.

Thanks,
Jean