diff mbox series

[1/5] qom: Allow objects to be allocated with increased alignment

Message ID 20200915174635.2333553-2-richard.henderson@linaro.org
State New
Headers show
Series qom: Allow object to be aligned | expand

Commit Message

Richard Henderson Sept. 15, 2020, 5:46 p.m. UTC
It turns out that some hosts have a default malloc alignment less
than that required for vectors.

We assume that, with compiler annotation on CPUArchState, that we
can properly align the vector portion of the guest state.  Fix the
alignment of the allocation by using qemu_memalloc when required.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Daniel P. Berrangé" <berrange@redhat.com>
Cc: Eduardo Habkost <ehabkost@redhat.com>
---
 include/qom/object.h |  4 ++++
 qom/object.c         | 16 +++++++++++++---
 2 files changed, 17 insertions(+), 3 deletions(-)

Comments

Eduardo Habkost Sept. 15, 2020, 6:07 p.m. UTC | #1
On Tue, Sep 15, 2020 at 10:46:31AM -0700, Richard Henderson wrote:
> It turns out that some hosts have a default malloc alignment less

> than that required for vectors.

> 

> We assume that, with compiler annotation on CPUArchState, that we

> can properly align the vector portion of the guest state.  Fix the

> alignment of the allocation by using qemu_memalloc when required.

> 

> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> ---

> Cc: Paolo Bonzini <pbonzini@redhat.com>

> Cc: "Daniel P. Berrangé" <berrange@redhat.com>

> Cc: Eduardo Habkost <ehabkost@redhat.com>

> ---

>  include/qom/object.h |  4 ++++

>  qom/object.c         | 16 +++++++++++++---

>  2 files changed, 17 insertions(+), 3 deletions(-)

> 

> diff --git a/include/qom/object.h b/include/qom/object.h

> index 056f67ab3b..d52d0781a3 100644

> --- a/include/qom/object.h

> +++ b/include/qom/object.h

> @@ -770,6 +770,9 @@ struct Object

>   * @instance_size: The size of the object (derivative of #Object).  If

>   *   @instance_size is 0, then the size of the object will be the size of the

>   *   parent object.

> + * @instance_align: The required alignment of the object.  If @instance_align

> + *   is 0, then normal malloc alignment is sufficient; if non-zero, then we

> + *   must use qemu_memalign for allocation.

>   * @instance_init: This function is called to initialize an object.  The parent

>   *   class will have already been initialized so the type is only responsible

>   *   for initializing its own members.

> @@ -807,6 +810,7 @@ struct TypeInfo

>      const char *parent;

>  

>      size_t instance_size;

> +    size_t instance_align;

>      void (*instance_init)(Object *obj);

>      void (*instance_post_init)(Object *obj);

>      void (*instance_finalize)(Object *obj);

> diff --git a/qom/object.c b/qom/object.c

> index 387efb25eb..2e53cb44a6 100644

> --- a/qom/object.c

> +++ b/qom/object.c

> @@ -50,6 +50,7 @@ struct TypeImpl

>      size_t class_size;

>  

>      size_t instance_size;

> +    size_t instance_align;

>  

>      void (*class_init)(ObjectClass *klass, void *data);

>      void (*class_base_init)(ObjectClass *klass, void *data);

> @@ -114,6 +115,7 @@ static TypeImpl *type_new(const TypeInfo *info)

>  

>      ti->class_size = info->class_size;

>      ti->instance_size = info->instance_size;

> +    ti->instance_align = info->instance_align;

>  

>      ti->class_init = info->class_init;

>      ti->class_base_init = info->class_base_init;

> @@ -691,13 +693,21 @@ static void object_finalize(void *data)

>  static Object *object_new_with_type(Type type)

>  {

>      Object *obj;

> +    size_t size, align;

>  

>      g_assert(type != NULL);

>      type_initialize(type);

>  

> -    obj = g_malloc(type->instance_size);

> -    object_initialize_with_type(obj, type->instance_size, type);

> -    obj->free = g_free;

> +    size = type->instance_size;

> +    align = type->instance_align;

> +    if (align) {


If we check for (align > G_MEM_ALIGN) instead, we will be able to
set instance_align automatically at OBJECT_DEFINE_TYPE*.

> +        obj = qemu_memalign(align, size);

> +    } else {

> +        obj = g_malloc(size);

> +    }

> +

> +    object_initialize_with_type(obj, size, type);

> +    obj->free = (align ? qemu_vfree : g_free);

>  

>      return obj;

>  }

> -- 

> 2.25.1

> 


-- 
Eduardo
Richard Henderson Sept. 15, 2020, 7:09 p.m. UTC | #2
On 9/15/20 11:07 AM, Eduardo Habkost wrote:
> On Tue, Sep 15, 2020 at 10:46:31AM -0700, Richard Henderson wrote:

>> It turns out that some hosts have a default malloc alignment less

>> than that required for vectors.

>>

>> We assume that, with compiler annotation on CPUArchState, that we

>> can properly align the vector portion of the guest state.  Fix the

>> alignment of the allocation by using qemu_memalloc when required.

>>

>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

>> ---

>> Cc: Paolo Bonzini <pbonzini@redhat.com>

>> Cc: "Daniel P. Berrangé" <berrange@redhat.com>

>> Cc: Eduardo Habkost <ehabkost@redhat.com>

>> ---

>>  include/qom/object.h |  4 ++++

>>  qom/object.c         | 16 +++++++++++++---

>>  2 files changed, 17 insertions(+), 3 deletions(-)

>>

>> diff --git a/include/qom/object.h b/include/qom/object.h

>> index 056f67ab3b..d52d0781a3 100644

>> --- a/include/qom/object.h

>> +++ b/include/qom/object.h

>> @@ -770,6 +770,9 @@ struct Object

>>   * @instance_size: The size of the object (derivative of #Object).  If

>>   *   @instance_size is 0, then the size of the object will be the size of the

>>   *   parent object.

>> + * @instance_align: The required alignment of the object.  If @instance_align

>> + *   is 0, then normal malloc alignment is sufficient; if non-zero, then we

>> + *   must use qemu_memalign for allocation.

>>   * @instance_init: This function is called to initialize an object.  The parent

>>   *   class will have already been initialized so the type is only responsible

>>   *   for initializing its own members.

>> @@ -807,6 +810,7 @@ struct TypeInfo

>>      const char *parent;

>>  

>>      size_t instance_size;

>> +    size_t instance_align;

>>      void (*instance_init)(Object *obj);

>>      void (*instance_post_init)(Object *obj);

>>      void (*instance_finalize)(Object *obj);

>> diff --git a/qom/object.c b/qom/object.c

>> index 387efb25eb..2e53cb44a6 100644

>> --- a/qom/object.c

>> +++ b/qom/object.c

>> @@ -50,6 +50,7 @@ struct TypeImpl

>>      size_t class_size;

>>  

>>      size_t instance_size;

>> +    size_t instance_align;

>>  

>>      void (*class_init)(ObjectClass *klass, void *data);

>>      void (*class_base_init)(ObjectClass *klass, void *data);

>> @@ -114,6 +115,7 @@ static TypeImpl *type_new(const TypeInfo *info)

>>  

>>      ti->class_size = info->class_size;

>>      ti->instance_size = info->instance_size;

>> +    ti->instance_align = info->instance_align;

>>  

>>      ti->class_init = info->class_init;

>>      ti->class_base_init = info->class_base_init;

>> @@ -691,13 +693,21 @@ static void object_finalize(void *data)

>>  static Object *object_new_with_type(Type type)

>>  {

>>      Object *obj;

>> +    size_t size, align;

>>  

>>      g_assert(type != NULL);

>>      type_initialize(type);

>>  

>> -    obj = g_malloc(type->instance_size);

>> -    object_initialize_with_type(obj, type->instance_size, type);

>> -    obj->free = g_free;

>> +    size = type->instance_size;

>> +    align = type->instance_align;

>> +    if (align) {

> 

> If we check for (align > G_MEM_ALIGN) instead, we will be able to

> set instance_align automatically at OBJECT_DEFINE_TYPE*.


I agree a value check would be good here, as well as setting this by default.

As for the value check itself...

I see that G_MEM_ALIGN isn't actually defined in an interesting or even correct
way.  E.g. it doesn't take the long double type into account.

The usual mechanism is

struct s {
  char pad;
  union {
    long l;
    void *p;
    double d;
    long double ld;
  } u;
};

offsetof(s, u)

since all of these types are required to be taken into account by the system
malloc.

E.g it doesn't take other host guarantees into account, e.g. i386-linux
guarantees 16-byte alignment.  This possibly dubious ABI change was made 20+
years ago with the introduction of SSE and is now set in stone.

Glibc has a "malloc-alignment.h" internal header that defaults to

  MIN(2 * sizeof(size_t), __alignof__(long double))

and is overridden for i386.  Sadly, it doesn't export MALLOC_ALIGNMENT.

Musl has two different malloc implementations.  One has UNIT = 16; the other
has SIZE_ALIGN = 4*sizeof(size_t).  Both have a minimum value of 16, and this
is not target-specific.

Any further comments on the subject, or should I put together something that
computes the MAX of the above?


r~
Eduardo Habkost Sept. 15, 2020, 8:19 p.m. UTC | #3
On Tue, Sep 15, 2020 at 12:09:59PM -0700, Richard Henderson wrote:
> On 9/15/20 11:07 AM, Eduardo Habkost wrote:

> > On Tue, Sep 15, 2020 at 10:46:31AM -0700, Richard Henderson wrote:

> >> It turns out that some hosts have a default malloc alignment less

> >> than that required for vectors.

> >>

> >> We assume that, with compiler annotation on CPUArchState, that we

> >> can properly align the vector portion of the guest state.  Fix the

> >> alignment of the allocation by using qemu_memalloc when required.

> >>

> >> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> >> ---

> >> Cc: Paolo Bonzini <pbonzini@redhat.com>

> >> Cc: "Daniel P. Berrangé" <berrange@redhat.com>

> >> Cc: Eduardo Habkost <ehabkost@redhat.com>

> >> ---

> >>  include/qom/object.h |  4 ++++

> >>  qom/object.c         | 16 +++++++++++++---

> >>  2 files changed, 17 insertions(+), 3 deletions(-)

> >>

> >> diff --git a/include/qom/object.h b/include/qom/object.h

> >> index 056f67ab3b..d52d0781a3 100644

> >> --- a/include/qom/object.h

> >> +++ b/include/qom/object.h

> >> @@ -770,6 +770,9 @@ struct Object

> >>   * @instance_size: The size of the object (derivative of #Object).  If

> >>   *   @instance_size is 0, then the size of the object will be the size of the

> >>   *   parent object.

> >> + * @instance_align: The required alignment of the object.  If @instance_align

> >> + *   is 0, then normal malloc alignment is sufficient; if non-zero, then we

> >> + *   must use qemu_memalign for allocation.

> >>   * @instance_init: This function is called to initialize an object.  The parent

> >>   *   class will have already been initialized so the type is only responsible

> >>   *   for initializing its own members.

> >> @@ -807,6 +810,7 @@ struct TypeInfo

> >>      const char *parent;

> >>  

> >>      size_t instance_size;

> >> +    size_t instance_align;

> >>      void (*instance_init)(Object *obj);

> >>      void (*instance_post_init)(Object *obj);

> >>      void (*instance_finalize)(Object *obj);

> >> diff --git a/qom/object.c b/qom/object.c

> >> index 387efb25eb..2e53cb44a6 100644

> >> --- a/qom/object.c

> >> +++ b/qom/object.c

> >> @@ -50,6 +50,7 @@ struct TypeImpl

> >>      size_t class_size;

> >>  

> >>      size_t instance_size;

> >> +    size_t instance_align;

> >>  

> >>      void (*class_init)(ObjectClass *klass, void *data);

> >>      void (*class_base_init)(ObjectClass *klass, void *data);

> >> @@ -114,6 +115,7 @@ static TypeImpl *type_new(const TypeInfo *info)

> >>  

> >>      ti->class_size = info->class_size;

> >>      ti->instance_size = info->instance_size;

> >> +    ti->instance_align = info->instance_align;

> >>  

> >>      ti->class_init = info->class_init;

> >>      ti->class_base_init = info->class_base_init;

> >> @@ -691,13 +693,21 @@ static void object_finalize(void *data)

> >>  static Object *object_new_with_type(Type type)

> >>  {

> >>      Object *obj;

> >> +    size_t size, align;

> >>  

> >>      g_assert(type != NULL);

> >>      type_initialize(type);

> >>  

> >> -    obj = g_malloc(type->instance_size);

> >> -    object_initialize_with_type(obj, type->instance_size, type);

> >> -    obj->free = g_free;

> >> +    size = type->instance_size;

> >> +    align = type->instance_align;

> >> +    if (align) {

> > 

> > If we check for (align > G_MEM_ALIGN) instead, we will be able to

> > set instance_align automatically at OBJECT_DEFINE_TYPE*.

> 

> I agree a value check would be good here, as well as setting this by default.

> 

> As for the value check itself...

> 

> I see that G_MEM_ALIGN isn't actually defined in an interesting or even correct

> way.  E.g. it doesn't take the long double type into account.

> 

> The usual mechanism is

> 

> struct s {

>   char pad;

>   union {

>     long l;

>     void *p;

>     double d;

>     long double ld;

>   } u;

> };

> 

> offsetof(s, u)

> 

> since all of these types are required to be taken into account by the system

> malloc.


Due to the existence of G_MEM_ALIGN, I thought GLib alignment
guarantees could be weaker than malloc().  I've just learned that
GLib uses system malloc() since 2.46.

> 

> E.g it doesn't take other host guarantees into account, e.g. i386-linux

> guarantees 16-byte alignment.  This possibly dubious ABI change was made 20+

> years ago with the introduction of SSE and is now set in stone.

> 

> Glibc has a "malloc-alignment.h" internal header that defaults to

> 

>   MIN(2 * sizeof(size_t), __alignof__(long double))

> 

> and is overridden for i386.  Sadly, it doesn't export MALLOC_ALIGNMENT.

> 

> Musl has two different malloc implementations.  One has UNIT = 16; the other

> has SIZE_ALIGN = 4*sizeof(size_t).  Both have a minimum value of 16, and this

> is not target-specific.

> 

> Any further comments on the subject, or should I put together something that

> computes the MAX of the above?


Once we move to C11, we can just use max_align_t.

While we don't move to C11, why not just use
  __alignof__(union { long l; void *p; double d; long double ld;})
?

-- 
Eduardo
Richard Henderson Sept. 15, 2020, 8:51 p.m. UTC | #4
On 9/15/20 1:19 PM, Eduardo Habkost wrote:
> Once we move to C11, we can just use max_align_t.


Yes.

> While we don't move to C11, why not just use

>   __alignof__(union { long l; void *p; double d; long double ld;})

> ?


For i386, this is 4.


r~
Eduardo Habkost Sept. 15, 2020, 9:27 p.m. UTC | #5
On Tue, Sep 15, 2020 at 01:51:48PM -0700, Richard Henderson wrote:
> On 9/15/20 1:19 PM, Eduardo Habkost wrote:

> > Once we move to C11, we can just use max_align_t.

> 

> Yes.

> 

> > While we don't move to C11, why not just use

> >   __alignof__(union { long l; void *p; double d; long double ld;})

> > ?

> 

> For i386, this is 4.


Is i386-linux the only case where there are additional alignment
guarantees not covered by C99?

I would prefer a i386-linux-specific #ifdef for that case instead
of guessing based on undocumented libc internals.

-- 
Eduardo
Richard Henderson Sept. 15, 2020, 9:30 p.m. UTC | #6
On 9/15/20 2:27 PM, Eduardo Habkost wrote:
> On Tue, Sep 15, 2020 at 01:51:48PM -0700, Richard Henderson wrote:

>> On 9/15/20 1:19 PM, Eduardo Habkost wrote:

>>> Once we move to C11, we can just use max_align_t.

>>

>> Yes.

>>

>>> While we don't move to C11, why not just use

>>>   __alignof__(union { long l; void *p; double d; long double ld;})

>>> ?

>>

>> For i386, this is 4.

> 

> Is i386-linux the only case where there are additional alignment

> guarantees not covered by C99?


I think so.

> I would prefer a i386-linux-specific #ifdef for that case instead

> of guessing based on undocumented libc internals.


I was thinking abi, not internals.


r~
Eduardo Habkost Sept. 15, 2020, 10 p.m. UTC | #7
On Tue, Sep 15, 2020 at 02:30:34PM -0700, Richard Henderson wrote:
> On 9/15/20 2:27 PM, Eduardo Habkost wrote:

> > On Tue, Sep 15, 2020 at 01:51:48PM -0700, Richard Henderson wrote:

> >> On 9/15/20 1:19 PM, Eduardo Habkost wrote:

> >>> Once we move to C11, we can just use max_align_t.

> >>

> >> Yes.

> >>

> >>> While we don't move to C11, why not just use

> >>>   __alignof__(union { long l; void *p; double d; long double ld;})

> >>> ?

> >>

> >> For i386, this is 4.

> > 

> > Is i386-linux the only case where there are additional alignment

> > guarantees not covered by C99?

> 

> I think so.

> 

> > I would prefer a i386-linux-specific #ifdef for that case instead

> > of guessing based on undocumented libc internals.

> 

> I was thinking abi, not internals.


I see.  As long as we can point to the specification backing the
assumptions in the code, I'm OK with that.

To me, it seems simpler to start with something that works on all
hosts (the alignment guarantees provided by C99), and add
arch-specific optimizations later.

-- 
Eduardo
diff mbox series

Patch

diff --git a/include/qom/object.h b/include/qom/object.h
index 056f67ab3b..d52d0781a3 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -770,6 +770,9 @@  struct Object
  * @instance_size: The size of the object (derivative of #Object).  If
  *   @instance_size is 0, then the size of the object will be the size of the
  *   parent object.
+ * @instance_align: The required alignment of the object.  If @instance_align
+ *   is 0, then normal malloc alignment is sufficient; if non-zero, then we
+ *   must use qemu_memalign for allocation.
  * @instance_init: This function is called to initialize an object.  The parent
  *   class will have already been initialized so the type is only responsible
  *   for initializing its own members.
@@ -807,6 +810,7 @@  struct TypeInfo
     const char *parent;
 
     size_t instance_size;
+    size_t instance_align;
     void (*instance_init)(Object *obj);
     void (*instance_post_init)(Object *obj);
     void (*instance_finalize)(Object *obj);
diff --git a/qom/object.c b/qom/object.c
index 387efb25eb..2e53cb44a6 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -50,6 +50,7 @@  struct TypeImpl
     size_t class_size;
 
     size_t instance_size;
+    size_t instance_align;
 
     void (*class_init)(ObjectClass *klass, void *data);
     void (*class_base_init)(ObjectClass *klass, void *data);
@@ -114,6 +115,7 @@  static TypeImpl *type_new(const TypeInfo *info)
 
     ti->class_size = info->class_size;
     ti->instance_size = info->instance_size;
+    ti->instance_align = info->instance_align;
 
     ti->class_init = info->class_init;
     ti->class_base_init = info->class_base_init;
@@ -691,13 +693,21 @@  static void object_finalize(void *data)
 static Object *object_new_with_type(Type type)
 {
     Object *obj;
+    size_t size, align;
 
     g_assert(type != NULL);
     type_initialize(type);
 
-    obj = g_malloc(type->instance_size);
-    object_initialize_with_type(obj, type->instance_size, type);
-    obj->free = g_free;
+    size = type->instance_size;
+    align = type->instance_align;
+    if (align) {
+        obj = qemu_memalign(align, size);
+    } else {
+        obj = g_malloc(size);
+    }
+
+    object_initialize_with_type(obj, size, type);
+    obj->free = (align ? qemu_vfree : g_free);
 
     return obj;
 }