Re: [PATCH v3 2/8] x86/virt/tdx: Remove 'struct field_mapping' and implement TD_SYSINFO_MAP() macro

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2024-08-29 at 10:20 +0300, Adrian Hunter wrote:
> On 27/08/24 10:14, Kai Huang wrote:
> 
> Subject could be "Simplify the reading of Global Metadata Fields"

OK.

> 
> > TL;DR: Remove the 'struct field_mapping' structure and use another way
> > to implement the TD_SYSINFO_MAP() macro to improve the current metadata
> > reading code, e.g., switching to use build-time check for metadata field
> > size over the existing runtime check.
> 
> Perhaps:
> 
>   Remove 'struct field_mapping' and let read_sys_metadata_field16() accept
>   the member variable address, simplifying the code in preparation for adding
>   support for more metadata structures and field sizes.

Fine to me.  I would like also to mention there are improvements in the new
code (as suggested by Dan), so I will say:

"..., simplifying and improving the code in preparation ...".

> 
> > 
> > The TDX module provides a set of "global metadata fields".  They report
> 
> Global Metadata Fields

OK. 

> 
> > things like TDX module version, supported features, and fields related
> > to create/run TDX guests and so on.
> > 
> > For now the kernel only reads "TD Memory Region" (TDMR) related global
> > metadata fields, and all of those fields are organized in one structure.
> 
> The patch is self-evidently simpler (21 insertions(+), 36 deletions(-))
> so there doesn't seem to be any need for further elaboration.  Perhaps
> just round it off and stop there.
> 
>   ... and all of those fields are organized in one structure,
>   but that will change in the near future.

I think the code change here is not only to reduce the LoC (i.e., simplify the
code), but also improve the code, so I would like to keep the things mentioned
by Dan in the changelog.

> 
> > 
> > The kernel currently uses 'struct field_mapping' to facilitate reading
> > multiple metadata fields into one structure.  The 'struct field_mapping'
> > records the mapping between the field ID and the offset of the structure
> > to fill out.  The kernel initializes an array of 'struct field_mapping'
> > for each structure member (using the TD_SYSINFO_MAP() macro) and then
> > reads all metadata fields in a loop using that array.
> > 
> > Currently the kernel only reads TDMR related metadata fields into one
> > structure, and the function to read one metadata field takes the pointer
> > of that structure and the offset:
> > 
> >   static int read_sys_metadata_field16(u64 field_id,
> >                                        int offset,
> >                                        struct tdx_sys_info_tdmr *ts)
> >   {...}
> > 
> > Future changes will need to read more metadata fields into different
> > structures.  To support this the above function will need to be changed
> > to take 'void *':
> > 
> >   static int read_sys_metadata_field16(u64 field_id,
> >                                        int offset,
> >                                        void *stbuf)
> >   {...}
> > 
> > This approach loses type-safety, as Dan suggested.  A better way is to
> > make it be ..
> > 
> >   static int read_sys_metadata_field16(u64 field_id, u16 *val) {...}
> > 
> > .. and let the caller calculate the buffer in a type-safe way [1].
> > 
> > Also, the using of the 'struct field_mapping' loses the ability to be
> > able to do build-time check about the metadata field size (encoded in
> > the field ID) due to the field ID is "indirectly" initialized to the
> > 'struct field_mapping' and passed to the function to read.  Thus the
> > current code uses runtime check instead.
> > 
> > Dan suggested to remove the 'struct field_mapping', unroll the loop,
> > skip the array, and call the read_sys_metadata_field16() directly with
> > build-time check [1][2].  And to improve the readability, reimplement
> > the TD_SYSINFO_MAP() [3].
> > 
> > The new TD_SYSINFO_MAP() isn't perfect.  It requires the function that
> > uses it to define a local variable @ret to carry the error code and set
> > the initial value to 0.  It also hard-codes the variable name of the
> > structure pointer used in the function.  But overall the pros of this
> > approach beat the cons.
> > 
> > Link: https://lore.kernel.org/kvm/a107b067-861d-43f4-86b5-29271cb93dad@xxxxxxxxx/T/#m7cfb3c146214d94b24e978eeb8708d92c0b14ac6 [1]
> > Link: https://lore.kernel.org/kvm/a107b067-861d-43f4-86b5-29271cb93dad@xxxxxxxxx/T/#mbe65f0903ff7835bc418a907f0d02d7a9e0b78be [2]
> > Link: https://lore.kernel.org/kvm/a107b067-861d-43f4-86b5-29271cb93dad@xxxxxxxxx/T/#m80cde5e6504b3af74d933ea0cbfc3ca9d24697d3 [3]
> 
> Probably just one link would suffice, say the permalink to Dan's
> comment:
> 
> https://lore.kernel.org/kvm/66b16121c48f4_4fc729424@xxxxxxxxxxxxxxxxxxxxxxxxx.notmuch/

[1] and [2] are actually replies to different patches, so I would like to keep
them.  [1] and [3] are replies to the same patch, so I could remove [3], but I
think keeping all of them is also fine since it's more easy to find the exact
comment that I want to address.

> 
> > Suggested-by: Dan Williams <dan.j.williams@xxxxxxxxx>
> > Signed-off-by: Kai Huang <kai.huang@xxxxxxxxx>
> > ---
> > 
> > v2 -> v3:
> >  - Remove 'struct field_mapping' and reimplement TD_SYSINFO_MAP().
> > 
> > ---
> >  arch/x86/virt/vmx/tdx/tdx.c | 57 ++++++++++++++-----------------------
> >  1 file changed, 21 insertions(+), 36 deletions(-)
> > 
> > diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> > index e979bf442929..7e75c1b10838 100644
> > --- a/arch/x86/virt/vmx/tdx/tdx.c
> > +++ b/arch/x86/virt/vmx/tdx/tdx.c
> > @@ -270,60 +270,45 @@ static int read_sys_metadata_field(u64 field_id, u64 *data)
> >  	return 0;
> >  }
> >  
> > -static int read_sys_metadata_field16(u64 field_id,
> > -				     int offset,
> > -				     struct tdx_sys_info_tdmr *ts)
> > +static int read_sys_metadata_field16(u64 field_id, u16 *val)
> >  {
> > -	u16 *ts_member = ((void *)ts) + offset;
> >  	u64 tmp;
> >  	int ret;
> >  
> > -	if (WARN_ON_ONCE(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
> > -			MD_FIELD_ID_ELE_SIZE_16BIT))
> > -		return -EINVAL;
> > +	BUILD_BUG_ON(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
> > +			MD_FIELD_ID_ELE_SIZE_16BIT);
> 
> This gets removed next patch, so why do it

This patch I didn't add the build-time check in the (new) TD_SYSINFO_MAP(), so
I changed the runtime check to build-time check here since we can actually do
it here.  I think even for this middle state patch we should do the build-time
check.  I can move it to the (new) TD_SYSINFO_MAP() though if that's better.

> 
> >  
> >  	ret = read_sys_metadata_field(field_id, &tmp);
> >  	if (ret)
> >  		return ret;
> >  
> > -	*ts_member = tmp;
> > +	*val = tmp;
> >  
> >  	return 0;
> >  }
> >  
> > -struct field_mapping {
> > -	u64 field_id;
> > -	int offset;
> > -};
> > -
> > -#define TD_SYSINFO_MAP(_field_id, _offset) \
> > -	{ .field_id = MD_FIELD_ID_##_field_id,	   \
> > -	  .offset   = offsetof(struct tdx_sys_info_tdmr, _offset) }
> > -
> > -/* Map TD_SYSINFO fields into 'struct tdx_sys_info_tdmr': */
> > -static const struct field_mapping fields[] = {
> > -	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs),
> > -	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr),
> > -	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]),
> > -	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]),
> > -	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]),
> > -};
> > +/*
> > + * Assumes locally defined @ret and @sysinfo_tdmr to convey the error
> > + * code and the 'struct tdx_sys_info_tdmr' instance to fill out.
> > + */
> > +#define TD_SYSINFO_MAP(_field_id, _member)						\
> 
> "MAP" made sense when it was in a struct whereas
> now it is reading.
> 
> > +	({										\
> > +		if (!ret)								\
> > +			ret = read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
> > +					&sysinfo_tdmr->_member);			\
> > +	})
> >  
> >  static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> >  {
> > -	int ret;
> > -	int i;
> > +	int ret = 0;
> >  
> > -	/* Populate 'sysinfo_tdmr' fields using the mapping structure above: */
> > -	for (i = 0; i < ARRAY_SIZE(fields); i++) {
> > -		ret = read_sys_metadata_field16(fields[i].field_id,
> > -						fields[i].offset,
> > -						sysinfo_tdmr);
> > -		if (ret)
> > -			return ret;
> > -	}
> > +	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs);
> > +	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> > +	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> > +	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> > +	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> 
> Another possibility is to put the macro at the invocation site:
> 
> #define READ_SYS_INFO(_field_id, _member)				\
> 	ret = ret ?: read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
> 					       &sysinfo_tdmr->_member)
> 
> 	READ_SYS_INFO(MAX_TDMRS,             max_tdmrs);
> 	READ_SYS_INFO(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> 	READ_SYS_INFO(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> 	READ_SYS_INFO(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> 	READ_SYS_INFO(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> 
> #undef READ_SYS_INFO
> 
> And so on in later patches:
> 
> #define READ_SYS_INFO(_field_id, _member)				\
> 	ret = ret ?: read_sys_metadata_field(MD_FIELD_ID_##_field_id,	\
> 					     &sysinfo_version->_member)
> 
> 	READ_SYS_INFO(MAJOR_VERSION,    major);
> 	READ_SYS_INFO(MINOR_VERSION,    minor);
> 	READ_SYS_INFO(UPDATE_VERSION,   update);
> 	READ_SYS_INFO(INTERNAL_VERSION, internal);
> 	READ_SYS_INFO(BUILD_NUM,        build_num);
> 	READ_SYS_INFO(BUILD_DATE,       build_date);
> 
> #undef READ_SYS_INFO
> 

This is fine to me.  But AFAICT Kirill doesn't like the "#undef" part.

Kirill, do you have comments?

Otherwise I will go with what Adrian suggested.




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux