diff mbox

[QEMU,RFC,v2,4/6] Migration: migrate QTAILQ

Message ID 1464112509-21806-5-git-send-email-duanj@linux.vnet.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jianjun Duan May 24, 2016, 5:55 p.m. UTC
A recursive structure has elements of the same type in itself. Currently
we cannot directly transfer a QTAILQ instance, or any recursive
structure such as lists in migration because of the limitation in the
migration code. Here we introduce a general approach to transfer such
structures. In our approach a recursive structure is tagged with VMS_CSTM.
We extend VMStateField with 3 fields: meta_data to store the meta data
about the recursive structure in question, extend_get to load the structure
from migration stream to memory, and extend_put to dump the structure into
the migration stream. This extension mirrors VMStateInfo. We then modify
vmstate_save_state and vmstate_load_state so that when VMS_CSTM is
encountered, extend_put and extend_get are called respectively with the
knowledge of the meta data.

To make it work for QTAILQ in qemu/queue.h, we created the meta data
format, extend_get and extend_put for it. We will use this approach to
transfer pending_events and ccs_list in spapr state.

We also create some macros in qemu/queue.h to get the layout information
about QTAILQ. This ensures that we do not depend on the implementation
details about QTAILQ in the migration code.

Signed-off-by: Jianjun Duan <duanj@linux.vnet.ibm.com>
---
 include/migration/vmstate.h | 59 +++++++++++++++++++++++++++++++
 include/qemu/queue.h        | 38 ++++++++++++++++++++
 migration/vmstate.c         | 84 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 181 insertions(+)

Comments

David Gibson May 25, 2016, 5:38 a.m. UTC | #1
On Tue, May 24, 2016 at 10:55:07AM -0700, Jianjun Duan wrote:
> A recursive structure has elements of the same type in itself. Currently
> we cannot directly transfer a QTAILQ instance, or any recursive
> structure such as lists in migration because of the limitation in the
> migration code. Here we introduce a general approach to transfer such
> structures. In our approach a recursive structure is tagged with VMS_CSTM.
> We extend VMStateField with 3 fields: meta_data to store the meta data
> about the recursive structure in question, extend_get to load the structure
> from migration stream to memory, and extend_put to dump the structure into
> the migration stream. This extension mirrors VMStateInfo. We then modify
> vmstate_save_state and vmstate_load_state so that when VMS_CSTM is
> encountered, extend_put and extend_get are called respectively with the
> knowledge of the meta data.

All the talk about recursive structures just obscures the issue.  The
point is you're building a way of migrating QTAILQs - just stick to
that.

> To make it work for QTAILQ in qemu/queue.h, we created the meta data
> format, extend_get and extend_put for it. We will use this approach to
> transfer pending_events and ccs_list in spapr state.
> 
> We also create some macros in qemu/queue.h to get the layout information
> about QTAILQ. This ensures that we do not depend on the implementation
> details about QTAILQ in the migration code.

I'm interested to see what Juan and Dave Gilbert think about this.


> 
> Signed-off-by: Jianjun Duan <duanj@linux.vnet.ibm.com>
> ---
>  include/migration/vmstate.h | 59 +++++++++++++++++++++++++++++++
>  include/qemu/queue.h        | 38 ++++++++++++++++++++
>  migration/vmstate.c         | 84 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 181 insertions(+)
> 
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 1622638..bf57b25 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -183,6 +183,8 @@ enum VMStateFlags {
>       * to determine the number of entries in the array. Only valid in
>       * combination with one of VMS_VARRAY*. */
>      VMS_MULTIPLY_ELEMENTS = 0x4000,
> +    /* For fields which need customized handling, such as QTAILQ in queue.h*/
> +    VMS_CSTM            = 0x8000,
>  };
>  
>  typedef struct {
> @@ -198,6 +200,14 @@ typedef struct {
>      const VMStateDescription *vmsd;
>      int version_id;
>      bool (*field_exists)(void *opaque, int version_id);
> +    /*
> +     * Following 3 fields are for VMStateField which needs customized handling,
> +     * such as QTAILQ in qemu/queue.h, lists, and tree.
> +     */
> +    const void *meta_data;
> +    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
> +    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
> +                       QJSON *vmdesc);
>  } VMStateField;
>  
>  struct VMStateDescription {
> @@ -654,6 +664,18 @@ extern const VMStateInfo vmstate_info_bitmap;
>      .offset       = offsetof(_state, _field),                        \
>  }
>  
> +/* For fields that need customized handling, such as queue, list */
> +#define VMSTATE_CSTM_V(_field, _state, _version, _meta_data, _get, _put)  \
> +{                                                                         \
> +    .name         = (stringify(_field)),                                  \
> +    .version_id   = (_version),                                           \
> +    .flags        = VMS_CSTM,                                             \
> +    .offset       = offsetof(_state, _field),                             \
> +    .meta_data    = &(_meta_data),                                        \
> +    .extend_get   = (_get),                                               \
> +    .extend_put   = (_put),                                               \
> +}
> +
>  /* _f : field name
>     _f_n : num of elements field_name
>     _n : num of elements
> @@ -970,4 +992,41 @@ int64_t self_announce_delay(int round)
>  
>  void dump_vmstate_json_to_file(FILE *out_fp);
>  
> +
> +/* Meta data for QTAILQ */
> +typedef struct QTAILQMetaData {
> +    /* the offset of tqh_first in QTAILQ_HEAD */
> +    size_t first;
> +    /* the offset of tqh_last in QTAILQ_HEAD */
> +    size_t last;
> +    /* the offset of tqe_next in QTAILQ_ENTRY */
> +    size_t next;
> +    /* the offset of tqe_prev in QTAILQ_ENTRY */
> +    size_t prev;
> +    /* the offset of QTAILQ_ENTRY in a QTAILQ element*/
> +    size_t entry;
> +    /* size of a QTAILQ element */
> +    size_t size;
> +    /* vmsd of a QTAILQ element */
> +    const VMStateDescription *vmsd;
> +} QTAILQMetaData;
> +
> +#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
> +    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
> +    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
> +    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
> +    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
> +    .entry = offsetof(_type, _next),                                   \
> +    .size = sizeof(_type),                                             \
> +    .vmsd = &(_vmsd),                                                  \
> +}
> +
> +int vmstate_get_qtailq(QEMUFile *f, const void *metadata, void *opaque);
> +void vmstate_put_qtailq(QEMUFile *f, const void *metadata,
> +                         void *opaque, QJSON *vmdesc);
> +
> +/* VMStateField for QTAILQ field */
> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _meta_data)               \
> +    VMSTATE_CSTM_V(_field, _state, _version, _meta_data, vmstate_get_qtailq, \
> +                   vmstate_put_qtailq)
>  #endif
> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
> index f781aa2..46962d7 100644
> --- a/include/qemu/queue.h
> +++ b/include/qemu/queue.h
> @@ -437,3 +437,41 @@ struct {                                                                \
>          (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
>  
>  #endif  /* !QEMU_SYS_QUEUE_H_ */
> +
> +/*
> + * Offsets of layout of a tail queue head.
> + */
> +#define QTAILQ_FIRST_OFFSET(head_type) \
> +        ((size_t) ((char *) &((head_type *)0)->tqh_first - (char *)0))
> +
> +#define QTAILQ_LAST_OFFSET(head_type) \
> +        ((size_t) ((char *) &((head_type *)0)->tqh_last - (char *)0))
> +
> +/*
> + * Offsets of layout of a tail queue element.
> + */
> +#define QTAILQ_NEXT_OFFSET(ele_type, field)                            \
> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_next -         \
> +                   (char *) &((ele_type *)0)->field))
> +
> +#define QTAILQ_PREV_OFFSET(ele_type, field) \
> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_prev -         \
> +                   (char *) &((ele_type *)0)->field))
> +/*
> + * Tail queue tranversal using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_FOREACH(elm, head, entry, first, next)              \
> +        for ((elm) = *((void **) ((char *) (head) + (first)));         \
> +             (elm);                                                    \
> +             (elm) = *((void **) ((char *) (elm) + (entry) + (next))))
> +/*
> + * Tail queue insertion using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry, first, last, next, prev) do { \
> +            *((void **) ((char *) (elm) + (entry) + (next))) = NULL;           \
> +            *((void **) ((char *) (elm) + (entry) + (prev))) =                 \
> +                *((void **) ((char *) (head) + (last)));                       \
> +            **((void ***)((char *) (head) + (last))) = (elm);                  \
> +            *((void **) ((char *) (head) + (last))) =                          \
> +                (void *) ((char *) (elm) + (entry) + (next));                  \
> +} while (/*CONSTCOND*/0)
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index bf3d5db..47cd052 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -5,6 +5,7 @@
>  #include "migration/vmstate.h"
>  #include "qemu/bitops.h"
>  #include "qemu/error-report.h"
> +#include "qemu/queue.h"
>  #include "trace.h"
>  #include "qjson.h"
>  
> @@ -121,6 +122,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>                  if (field->flags & VMS_STRUCT) {
>                      ret = vmstate_load_state(f, field->vmsd, addr,
>                                               field->vmsd->version_id);
> +                } else if (field->flags & VMS_CSTM) {
> +                    ret = field->extend_get(f, field->meta_data, addr);
>                  } else {
>                      ret = field->info->get(f, addr, size);
>  
> @@ -193,6 +196,8 @@ static const char *vmfield_get_type_name(VMStateField *field)
>  
>      if (field->flags & VMS_STRUCT) {
>          type = "struct";
> +    } else if (field->flags & VMS_CSTM) {
> +        type = "customized";
>      } else if (field->info->name) {
>          type = field->info->name;
>      }
> @@ -327,6 +332,8 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
>                  }
>                  if (field->flags & VMS_STRUCT) {
>                      vmstate_save_state(f, field->vmsd, addr, vmdesc_loop);
> +                } else if  (field->flags & VMS_CSTM) {
> +                    field->extend_put(f, field->meta_data, addr, vmdesc_loop);
>                  } else {
>                      field->info->put(f, addr, size);
>                  }
> @@ -916,3 +923,80 @@ const VMStateInfo vmstate_info_bitmap = {
>      .get = get_bitmap,
>      .put = put_bitmap,
>  };
> +
> +/* extend_get for QTAILQ */
> +int vmstate_get_qtailq(QEMUFile *f, const void *metadata, void *opaque)
> +{
> +    bool link;
> +    int ret = 0;
> +    size_t first, last, next, prev, entry, size;
> +    QTAILQMetaData *data = (QTAILQMetaData *)metadata;
> +    const VMStateDescription *vmsd = data->vmsd;
> +    int version_id = vmsd->version_id;
> +    void *elm;
> +
> +    first = data->first;
> +    last = data->last;
> +    next = data->next;
> +    prev = data->prev;
> +    entry = data->entry;
> +    size = data->size;
> +
> +    trace_vmstate_load_state(vmsd->name, version_id);
> +    if (version_id > vmsd->version_id) {
> +        trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
> +        return -EINVAL;
> +    }
> +    if (version_id < vmsd->minimum_version_id) {
> +        trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
> +        return -EINVAL;
> +    }
> +
> +    while (true) {
> +        vmstate_info_bool.get(f, &link, sizeof(bool));
> +        if (!link) {
> +            break;
> +        }
> +
> +        elm =  g_malloc(size);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        if (ret) {
> +            return ret;
> +        }
> +        QTAILQ_RAW_INSERT_TAIL(opaque, elm, entry, first, last, next, prev);
> +    }
> +
> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
> +    return ret;
> +}
> +
> +/* extend_get for QTAILQ */
> +void vmstate_put_qtailq(QEMUFile *f, const void *metadata, void *opaque,
> +                         QJSON *vmdesc)
> +{
> +    bool link = true;
> +    size_t first, next, entry;
> +    QTAILQMetaData *data = (QTAILQMetaData *)metadata;
> +    const VMStateDescription *vmsd = data->vmsd;
> +    void *elm;
> +
> +    first = data->first;
> +    next = data->next;
> +    entry = data->entry;
> +
> +    if (vmdesc) {
> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
> +        json_prop_int(vmdesc, "version", vmsd->version_id);
> +        json_start_array(vmdesc, "fields");
> +    }
> +
> +    QTAILQ_RAW_FOREACH(elm, opaque, entry, first, next) {
> +        vmstate_info_bool.put(f, &link, sizeof(bool));
> +        vmstate_save_state(f, vmsd, elm, vmdesc);
> +    }
> +    link = false;
> +    vmstate_info_bool.put(f, &link, sizeof(bool));
> +    if (vmdesc) {
> +        json_end_array(vmdesc);
> +    }
> +}
Paolo Bonzini May 25, 2016, 8:14 a.m. UTC | #2
On 24/05/2016 19:55, Jianjun Duan wrote:
> +/*
> + * Offsets of layout of a tail queue head.
> + */
> +#define QTAILQ_FIRST_OFFSET(head_type) \
> +        ((size_t) ((char *) &((head_type *)0)->tqh_first - (char *)0))
> +
> +#define QTAILQ_LAST_OFFSET(head_type) \
> +        ((size_t) ((char *) &((head_type *)0)->tqh_last - (char *)0))
> +
> +/*
> + * Offsets of layout of a tail queue element.
> + */
> +#define QTAILQ_NEXT_OFFSET(ele_type, field)                            \
> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_next -         \
> +                   (char *) &((ele_type *)0)->field))
> +
> +#define QTAILQ_PREV_OFFSET(ele_type, field) \
> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_prev -         \
> +                   (char *) &((ele_type *)0)->field))

Please use offsetof.

> +    /*
> +     * Following 3 fields are for VMStateField which needs customized handling,
> +     * such as QTAILQ in qemu/queue.h, lists, and tree.
> +     */
> +    const void *meta_data;
> +    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
> +    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
> +                       QJSON *vmdesc);
>  } VMStateField;

Do not add these two function pointers to VMStateField, instead add
QJSON* and VMStateField* arguments as needed to VMStateInfo's get and put.

> +#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
> +    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
> +    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
> +    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
> +    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
> +    .entry = offsetof(_type, _next),                                   \
> +    .size = sizeof(_type),                                             \
> +    .vmsd = &(_vmsd),                                                  \
> +}

.last and .prev are unnecessary, since they come just after .first and
.next (and their use is hidden behind QTAILQ_RAW_*).  .first and .next
can be placed in .offset and .num_offset respectively.  So you don't
really need an extra metadata struct.

If you prefer you could have something like

    union {
        size_t num_offset;    /* VMS_VARRAY */
        size_t size_offset;   /* VMS_VBUFFER */
        size_t next_offset;   /* VMS_TAILQ */
    } offsets;

Thanks,

Paolo
Jianjun Duan May 25, 2016, 4:37 p.m. UTC | #3
On 05/25/2016 01:14 AM, Paolo Bonzini wrote:
> 
> 
> On 24/05/2016 19:55, Jianjun Duan wrote:
>> +/*
>> + * Offsets of layout of a tail queue head.
>> + */
>> +#define QTAILQ_FIRST_OFFSET(head_type) \
>> +        ((size_t) ((char *) &((head_type *)0)->tqh_first - (char *)0))
>> +
>> +#define QTAILQ_LAST_OFFSET(head_type) \
>> +        ((size_t) ((char *) &((head_type *)0)->tqh_last - (char *)0))
>> +
>> +/*
>> + * Offsets of layout of a tail queue element.
>> + */
>> +#define QTAILQ_NEXT_OFFSET(ele_type, field)                            \
>> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_next -         \
>> +                   (char *) &((ele_type *)0)->field))
>> +
>> +#define QTAILQ_PREV_OFFSET(ele_type, field) \
>> +        ((size_t) ((char *) &((ele_type *)0)->field.tqe_prev -         \
>> +                   (char *) &((ele_type *)0)->field))
> 
> Please use offsetof.
OK.

> 
>> +    /*
>> +     * Following 3 fields are for VMStateField which needs customized handling,
>> +     * such as QTAILQ in qemu/queue.h, lists, and tree.
>> +     */
>> +    const void *meta_data;
>> +    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
>> +    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
>> +                       QJSON *vmdesc);
>>  } VMStateField;
> 
> Do not add these two function pointers to VMStateField, instead add
> QJSON* and VMStateField* arguments as needed to VMStateInfo's get and put.
> 
That is definitely the ideal way. However, VMStateInfo's get/put are
already used extensively. If I change them, I need change all the
calling sites of them. Not very sure about whether it will be welcomed.
>> +#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
>> +    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
>> +    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
>> +    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
>> +    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
>> +    .entry = offsetof(_type, _next),                                   \
>> +    .size = sizeof(_type),                                             \
>> +    .vmsd = &(_vmsd),                                                  \
>> +}
> 
> .last and .prev are unnecessary, since they come just after .first and
> .next (and their use is hidden behind QTAILQ_RAW_*).  .first and .next
> can be placed in .offset and .num_offset respectively.  So you don't
> really need an extra metadata struct.
> 
> If you prefer you could have something like
> 
>     union {
>         size_t num_offset;    /* VMS_VARRAY */
>         size_t size_offset;   /* VMS_VBUFFER */
>         size_t next_offset;   /* VMS_TAILQ */
>     } offsets;
> 
Actually I explored the approach in which I use a VMSD to encode all the
information. But a VMSD describes actual memory layout. Interpreting it
another way could be confusing.

One of the assumption about QTAILQ is that we can only use the
interfaces defined in queue.h to access it. I intend not to depend on
its actually layout. From this perspective, these 6 parameters are
needed.

> Thanks,
> 
> Paolo
> 
Thanks,
Jianjun
Paolo Bonzini May 25, 2016, 5:51 p.m. UTC | #4
> >> +    /*
> >> +     * Following 3 fields are for VMStateField which needs customized
> >> handling,
> >> +     * such as QTAILQ in qemu/queue.h, lists, and tree.
> >> +     */
> >> +    const void *meta_data;
> >> +    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
> >> +    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
> >> +                       QJSON *vmdesc);
> >>  } VMStateField;
> > 
> > Do not add these two function pointers to VMStateField, instead add
> > QJSON* and VMStateField* arguments as needed to VMStateInfo's get and put.
> 
> That is definitely the ideal way. However, VMStateInfo's get/put are
> already used extensively. If I change them, I need change all the
> calling sites of them. Not very sure about whether it will be welcomed.

Sure, don't be worried. :)  True, there are quite a few VMStateInfos (about
35) but the changes are simple.

> >> +#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
> >> +    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
> >> +    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
> >> +    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
> >> +    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
> >> +    .entry = offsetof(_type, _next),                                   \
> >> +    .size = sizeof(_type),                                             \
> >> +    .vmsd = &(_vmsd),                                                  \
> >> +}
> > 
> > .last and .prev are unnecessary, since they come just after .first and
> > .next (and their use is hidden behind QTAILQ_RAW_*).  .first and .next
                                                          ^^^^^^^^^^^^^^^^

Actually, .first and .next are always 0.  I misread your changes to qemu/queue.h.
See below.

> > can be placed in .offset and .num_offset respectively.  So you don't
> > really need an extra metadata struct.
> > 
> > If you prefer you could have something like
> > 
> >     union {
> >         size_t num_offset;    /* VMS_VARRAY */
> >         size_t size_offset;   /* VMS_VBUFFER */
> >         size_t next_offset;   /* VMS_TAILQ */
> >     } offsets;
>
> Actually I explored the approach in which I use a VMSD to encode all the
> information. But a VMSD describes actual memory layout. Interpreting it
> another way could be confusing.
> 
> One of the assumption about QTAILQ is that we can only use the
> interfaces defined in queue.h to access it. I intend not to depend on
> its actually layout. From this perspective, these 6 parameters are
> needed.

You are already adding QTAILQ_RAW_*, aren't you?  Those interfaces
would need to know about the layout, and you are passing redundant
information:

- .next_offset should always be 0
- .prev_offset should always be sizeof(void *)
- .first_offset should always be 0
- .last_offset should always be sizeof(void *)

so you only need head and entry, which you can store in .offset and
.num_offset.  The .vmsd field you have in ->metadata can be stored
in VMStateField's .vmsd, and likewise for .size (which can be stored
in VMStateField's .vmsd).

Thanks,

Paolo
Jianjun Duan May 25, 2016, 6:30 p.m. UTC | #5
I will try to explain my design rationale in details here.

1 QTAILQ should only be accessed using the interfaces defined in
queue.h. Its structs should not be directly used. So I created
interfaces in queue.h to query about its layout. If the implementation
is changed, these interfaces should be changed accordingly. Code using
these interfaces should not break.

2 Based on point 1, vmstate_load_state/vmstate_put_state in vmstate.c
doesn't exactly know the structs of QTAILAQ head and entry. So pointer
arithmetic is needed to put/get a QTAILQ. To do it, we need those 6
parameters to be passed in. So it is not redundant if we only want to
only use the interfaces.

3 At this moment, vmstate_load_state/vmstate_put_state couldn't handle a
queue, or a list, or another recursive structure. To make it
extensible, I think a metadata is needed. The idea is for any
structure which needs special handling, customized metadata/put/get
should provide enough flexibility to hack around.

There are two issues I tried to address. One is the recursive queue,
another is working around of hiding of the QTAILQ implementation
details. It seems we need to agree on how the latter should be
addressed.

I will give it a try to fix those 35 calling sites of VMStateInfo.

Thanks,
Jianjun

On 05/25/2016 10:51 AM, Paolo Bonzini wrote:
>>>> +    /*
>>>> +     * Following 3 fields are for VMStateField which needs customized
>>>> handling,
>>>> +     * such as QTAILQ in qemu/queue.h, lists, and tree.
>>>> +     */
>>>> +    const void *meta_data;
>>>> +    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
>>>> +    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
>>>> +                       QJSON *vmdesc);
>>>>  } VMStateField;
>>>
>>> Do not add these two function pointers to VMStateField, instead add
>>> QJSON* and VMStateField* arguments as needed to VMStateInfo's get and put.
>>
>> That is definitely the ideal way. However, VMStateInfo's get/put are
>> already used extensively. If I change them, I need change all the
>> calling sites of them. Not very sure about whether it will be welcomed.
> 
> Sure, don't be worried. :)  True, there are quite a few VMStateInfos (about
> 35) but the changes are simple.
> 
>>>> +#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
>>>> +    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
>>>> +    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
>>>> +    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
>>>> +    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
>>>> +    .entry = offsetof(_type, _next),                                   \
>>>> +    .size = sizeof(_type),                                             \
>>>> +    .vmsd = &(_vmsd),                                                  \
>>>> +}
>>>
>>> .last and .prev are unnecessary, since they come just after .first and
>>> .next (and their use is hidden behind QTAILQ_RAW_*).  .first and .next
>                                                           ^^^^^^^^^^^^^^^^
> 
> Actually, .first and .next are always 0.  I misread your changes to qemu/queue.h.
> See below.
> 
>>> can be placed in .offset and .num_offset respectively.  So you don't
>>> really need an extra metadata struct.
>>>
>>> If you prefer you could have something like
>>>
>>>     union {
>>>         size_t num_offset;    /* VMS_VARRAY */
>>>         size_t size_offset;   /* VMS_VBUFFER */
>>>         size_t next_offset;   /* VMS_TAILQ */
>>>     } offsets;
>>
>> Actually I explored the approach in which I use a VMSD to encode all the
>> information. But a VMSD describes actual memory layout. Interpreting it
>> another way could be confusing.
>>
>> One of the assumption about QTAILQ is that we can only use the
>> interfaces defined in queue.h to access it. I intend not to depend on
>> its actually layout. From this perspective, these 6 parameters are
>> needed.
> 
> You are already adding QTAILQ_RAW_*, aren't you?  Those interfaces
> would need to know about the layout, and you are passing redundant
> information:
> 
> - .next_offset should always be 0
> - .prev_offset should always be sizeof(void *)
> - .first_offset should always be 0
> - .last_offset should always be sizeof(void *)
> 
> so you only need head and entry, which you can store in .offset and
> .num_offset.  The .vmsd field you have in ->metadata can be stored
> in VMStateField's .vmsd, and likewise for .size (which can be stored
> in VMStateField's .vmsd).
> 
> Thanks,
> 
> Paolo
>
Paolo Bonzini May 25, 2016, 7:22 p.m. UTC | #6
> 1 QTAILQ should only be accessed using the interfaces defined in
> queue.h. Its structs should not be directly used. So I created
> interfaces in queue.h to query about its layout. If the implementation
> is changed, these interfaces should be changed accordingly. Code using
> these interfaces should not break.

You don't need to query the layout, as long as the knowledge
remains hidden in QTAILQ_RAW_* macros.  And because QTAILQ_*_OFFSET
returns constant values, you can just put the knowledge of the offsets
directly in QTAILQ_RAW_FOREACH and QTAILQ_RAW_INSERT_TAIL.

> 2 Based on point 1, vmstate_load_state/vmstate_put_state in vmstate.c
> doesn't exactly know the structs of QTAILAQ head and entry. So pointer
> arithmetic is needed to put/get a QTAILQ. To do it, we need those 6
> parameters to be passed in. So it is not redundant if we only want to
> only use the interfaces.

No, you only need two.  The other four are internal to qemu/queue.h.
Just like QTAILQ users do not know about tqh_* and tqe_*, they need not
know about their offsets, only the fields that contain them.

> 3 At this moment, vmstate_load_state/vmstate_put_state couldn't handle a
> queue, or a list, or another recursive structure. To make it
> extensible, I think a metadata is needed. The idea is for any
> structure which needs special handling, customized metadata/put/get
> should provide enough flexibility to hack around.

I think your solution is a bit overengineered.  If the metadata can
fit in the VMStateField, you can use VMStateField.

Thanks,

Paolo
Jianjun Duan May 25, 2016, 8:17 p.m. UTC | #7
On 05/25/2016 12:22 PM, Paolo Bonzini wrote:
>> 1 QTAILQ should only be accessed using the interfaces defined in
>> queue.h. Its structs should not be directly used. So I created
>> interfaces in queue.h to query about its layout. If the implementation
>> is changed, these interfaces should be changed accordingly. Code using
>> these interfaces should not break.
> 
> You don't need to query the layout, as long as the knowledge
> remains hidden in QTAILQ_RAW_* macros.  And because QTAILQ_*_OFFSET
> returns constant values, you can just put the knowledge of the offsets
> directly in QTAILQ_RAW_FOREACH and QTAILQ_RAW_INSERT_TAIL.
> 
>> 2 Based on point 1, vmstate_load_state/vmstate_put_state in vmstate.c
>> doesn't exactly know the structs of QTAILAQ head and entry. So pointer
>> arithmetic is needed to put/get a QTAILQ. To do it, we need those 6
>> parameters to be passed in. So it is not redundant if we only want to
>> only use the interfaces.
> 
> No, you only need two.  The other four are internal to qemu/queue.h.
> Just like QTAILQ users do not know about tqh_* and tqe_*, they need not
> know about their offsets, only the fields that contain them.
> 

In vmstate_load/put_state usually we use a VMSD to describe the type for
dump/load purpose. The metadata for the QTAILQ is for the same purpose.
Of course we can encode the information in a VMSD or VMStateField if
we don't have to change much.

The user may only care the position of head and entry. But to
implement QTAILQ_RAW_***, we need more offset information than that.
If we don't query the offsets using something like offset() and store
it in a metadata, we have to make the assumption that all the pointer
types have the same size. Since in vmstate_load/put_state we don't have
any type information about the QTAILQ instance, we cannot use offset()
in QTAILQ_RAW_*** macros. May have to stick the constants there for
first/last/next/prev in QTAILQ_RAW_***. Not sure if it will work for
all arches.
>> 3 At this moment, vmstate_load_state/vmstate_put_state couldn't handle a
>> queue, or a list, or another recursive structure. To make it
>> extensible, I think a metadata is needed. The idea is for any
>> structure which needs special handling, customized metadata/put/get
>> should provide enough flexibility to hack around.
> 
> I think your solution is a bit overengineered.  If the metadata can
> fit in the VMStateField, you can use VMStateField.
> 
> Thanks,
> 
> Paolo
> 

Thanks,
Jianjun
Paolo Bonzini May 26, 2016, 7:11 a.m. UTC | #8
On 25/05/2016 22:17, Jianjun Duan wrote:
> 
> 
> On 05/25/2016 12:22 PM, Paolo Bonzini wrote:
>>> 1 QTAILQ should only be accessed using the interfaces defined in
>>> queue.h. Its structs should not be directly used. So I created
>>> interfaces in queue.h to query about its layout. If the implementation
>>> is changed, these interfaces should be changed accordingly. Code using
>>> these interfaces should not break.
>>
>> You don't need to query the layout, as long as the knowledge
>> remains hidden in QTAILQ_RAW_* macros.  And because QTAILQ_*_OFFSET
>> returns constant values, you can just put the knowledge of the offsets
>> directly in QTAILQ_RAW_FOREACH and QTAILQ_RAW_INSERT_TAIL.
>>
>>> 2 Based on point 1, vmstate_load_state/vmstate_put_state in vmstate.c
>>> doesn't exactly know the structs of QTAILAQ head and entry. So pointer
>>> arithmetic is needed to put/get a QTAILQ. To do it, we need those 6
>>> parameters to be passed in. So it is not redundant if we only want to
>>> only use the interfaces.
>>
>> No, you only need two.  The other four are internal to qemu/queue.h.
>> Just like QTAILQ users do not know about tqh_* and tqe_*, they need not
>> know about their offsets, only the fields that contain them.
> 
> In vmstate_load/put_state usually we use a VMSD to describe the type for
> dump/load purpose. The metadata for the QTAILQ is for the same purpose.
> Of course we can encode the information in a VMSD or VMStateField if
> we don't have to change much.

A VMSD describes the "sub-structure" of the field.  Here, the
substructure of a QTAILQ is the structure of each entry.

> The user may only care the position of head and entry. But to
> implement QTAILQ_RAW_***, we need more offset information than that.
> If we don't query the offsets using something like offset() and store
> it in a metadata, we have to make the assumption that all the pointer
> types have the same size.

We make this assumption elsewhere anyway.  _You_ are making it by doing
loads and stores through void** in QTAILQ_RAW_*.

Thanks,

Paolo
Jianjun Duan May 26, 2016, 4:43 p.m. UTC | #9
On 05/26/2016 12:11 AM, Paolo Bonzini wrote:
> 
> 
> On 25/05/2016 22:17, Jianjun Duan wrote:
>>
>>
>> On 05/25/2016 12:22 PM, Paolo Bonzini wrote:
>>>> 1 QTAILQ should only be accessed using the interfaces defined in
>>>> queue.h. Its structs should not be directly used. So I created
>>>> interfaces in queue.h to query about its layout. If the implementation
>>>> is changed, these interfaces should be changed accordingly. Code using
>>>> these interfaces should not break.
>>>
>>> You don't need to query the layout, as long as the knowledge
>>> remains hidden in QTAILQ_RAW_* macros.  And because QTAILQ_*_OFFSET
>>> returns constant values, you can just put the knowledge of the offsets
>>> directly in QTAILQ_RAW_FOREACH and QTAILQ_RAW_INSERT_TAIL.
>>>
>>>> 2 Based on point 1, vmstate_load_state/vmstate_put_state in vmstate.c
>>>> doesn't exactly know the structs of QTAILAQ head and entry. So pointer
>>>> arithmetic is needed to put/get a QTAILQ. To do it, we need those 6
>>>> parameters to be passed in. So it is not redundant if we only want to
>>>> only use the interfaces.
>>>
>>> No, you only need two.  The other four are internal to qemu/queue.h.
>>> Just like QTAILQ users do not know about tqh_* and tqe_*, they need not
>>> know about their offsets, only the fields that contain them.
>>
>> In vmstate_load/put_state usually we use a VMSD to describe the type for
>> dump/load purpose. The metadata for the QTAILQ is for the same purpose.
>> Of course we can encode the information in a VMSD or VMStateField if
>> we don't have to change much.
> 
> A VMSD describes the "sub-structure" of the field.  Here, the
> substructure of a QTAILQ is the structure of each entry.
> 
>> The user may only care the position of head and entry. But to
>> implement QTAILQ_RAW_***, we need more offset information than that.
>> If we don't query the offsets using something like offset() and store
>> it in a metadata, we have to make the assumption that all the pointer
>> types have the same size.
> 
> We make this assumption elsewhere anyway.  _You_ are making it by doing
> loads and stores through void** in QTAILQ_RAW_*.
I thought every data pointer can be converted to and from void type of
pointer. Anyway we can make that assumption here.

How about alignment? Is it OK to assume pointers are always aligned at
4byte for 32 bit and 8byte for 64 bit? If yes then we can just add the
size of a void pointer to first to get last in QTAILQ head. We will get
something like:
first=0
last = sizeof(void *)
next=0
prev=sizeof(void *)

Thanks,
Jianjun



> 
> Thanks,
> 
> Paolo
>
Paolo Bonzini May 26, 2016, 4:52 p.m. UTC | #10
On 26/05/2016 18:43, Jianjun Duan wrote:
>>> The user may only care the position of head and entry. But to
>>> implement QTAILQ_RAW_***, we need more offset information than that.
>>> If we don't query the offsets using something like offset() and store
>>> it in a metadata, we have to make the assumption that all the pointer
>>> types have the same size.
>>
>> We make this assumption elsewhere anyway.  _You_ are making it by doing
>> loads and stores through void** in QTAILQ_RAW_*.
> 
> I thought every data pointer can be converted to and from void type of
> pointer. Anyway we can make that assumption here.

Yes, you can do that.

> How about alignment? Is it OK to assume pointers are always aligned at
> 4byte for 32 bit and 8byte for 64 bit? If yes then we can just add the
> size of a void pointer to first to get last in QTAILQ head. We will get
> something like:
> first=0
> last = sizeof(void *)
> next=0
> prev=sizeof(void *)

Yes, that's my point.  These four are constants.

Thanks,

Paolo
diff mbox

Patch

diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1622638..bf57b25 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -183,6 +183,8 @@  enum VMStateFlags {
      * to determine the number of entries in the array. Only valid in
      * combination with one of VMS_VARRAY*. */
     VMS_MULTIPLY_ELEMENTS = 0x4000,
+    /* For fields which need customized handling, such as QTAILQ in queue.h*/
+    VMS_CSTM            = 0x8000,
 };
 
 typedef struct {
@@ -198,6 +200,14 @@  typedef struct {
     const VMStateDescription *vmsd;
     int version_id;
     bool (*field_exists)(void *opaque, int version_id);
+    /*
+     * Following 3 fields are for VMStateField which needs customized handling,
+     * such as QTAILQ in qemu/queue.h, lists, and tree.
+     */
+    const void *meta_data;
+    int (*extend_get)(QEMUFile *f, const void *metadata, void *opaque);
+    void (*extend_put)(QEMUFile *f, const void *metadata, void *opaque,
+                       QJSON *vmdesc);
 } VMStateField;
 
 struct VMStateDescription {
@@ -654,6 +664,18 @@  extern const VMStateInfo vmstate_info_bitmap;
     .offset       = offsetof(_state, _field),                        \
 }
 
+/* For fields that need customized handling, such as queue, list */
+#define VMSTATE_CSTM_V(_field, _state, _version, _meta_data, _get, _put)  \
+{                                                                         \
+    .name         = (stringify(_field)),                                  \
+    .version_id   = (_version),                                           \
+    .flags        = VMS_CSTM,                                             \
+    .offset       = offsetof(_state, _field),                             \
+    .meta_data    = &(_meta_data),                                        \
+    .extend_get   = (_get),                                               \
+    .extend_put   = (_put),                                               \
+}
+
 /* _f : field name
    _f_n : num of elements field_name
    _n : num of elements
@@ -970,4 +992,41 @@  int64_t self_announce_delay(int round)
 
 void dump_vmstate_json_to_file(FILE *out_fp);
 
+
+/* Meta data for QTAILQ */
+typedef struct QTAILQMetaData {
+    /* the offset of tqh_first in QTAILQ_HEAD */
+    size_t first;
+    /* the offset of tqh_last in QTAILQ_HEAD */
+    size_t last;
+    /* the offset of tqe_next in QTAILQ_ENTRY */
+    size_t next;
+    /* the offset of tqe_prev in QTAILQ_ENTRY */
+    size_t prev;
+    /* the offset of QTAILQ_ENTRY in a QTAILQ element*/
+    size_t entry;
+    /* size of a QTAILQ element */
+    size_t size;
+    /* vmsd of a QTAILQ element */
+    const VMStateDescription *vmsd;
+} QTAILQMetaData;
+
+#define VMSTATE_QTAILQ_METADATA(_field, _state, _type, _next, _vmsd) { \
+    .first = QTAILQ_FIRST_OFFSET(typeof_field(_state, _field)),        \
+    .last =  QTAILQ_LAST_OFFSET(typeof_field(_state, _field)),         \
+    .next = QTAILQ_NEXT_OFFSET(_type, _next),                          \
+    .prev = QTAILQ_PREV_OFFSET(_type, _next),                          \
+    .entry = offsetof(_type, _next),                                   \
+    .size = sizeof(_type),                                             \
+    .vmsd = &(_vmsd),                                                  \
+}
+
+int vmstate_get_qtailq(QEMUFile *f, const void *metadata, void *opaque);
+void vmstate_put_qtailq(QEMUFile *f, const void *metadata,
+                         void *opaque, QJSON *vmdesc);
+
+/* VMStateField for QTAILQ field */
+#define VMSTATE_QTAILQ_V(_field, _state, _version, _meta_data)               \
+    VMSTATE_CSTM_V(_field, _state, _version, _meta_data, vmstate_get_qtailq, \
+                   vmstate_put_qtailq)
 #endif
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index f781aa2..46962d7 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -437,3 +437,41 @@  struct {                                                                \
         (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
 
 #endif  /* !QEMU_SYS_QUEUE_H_ */
+
+/*
+ * Offsets of layout of a tail queue head.
+ */
+#define QTAILQ_FIRST_OFFSET(head_type) \
+        ((size_t) ((char *) &((head_type *)0)->tqh_first - (char *)0))
+
+#define QTAILQ_LAST_OFFSET(head_type) \
+        ((size_t) ((char *) &((head_type *)0)->tqh_last - (char *)0))
+
+/*
+ * Offsets of layout of a tail queue element.
+ */
+#define QTAILQ_NEXT_OFFSET(ele_type, field)                            \
+        ((size_t) ((char *) &((ele_type *)0)->field.tqe_next -         \
+                   (char *) &((ele_type *)0)->field))
+
+#define QTAILQ_PREV_OFFSET(ele_type, field) \
+        ((size_t) ((char *) &((ele_type *)0)->field.tqe_prev -         \
+                   (char *) &((ele_type *)0)->field))
+/*
+ * Tail queue tranversal using pointer arithmetic.
+ */
+#define QTAILQ_RAW_FOREACH(elm, head, entry, first, next)              \
+        for ((elm) = *((void **) ((char *) (head) + (first)));         \
+             (elm);                                                    \
+             (elm) = *((void **) ((char *) (elm) + (entry) + (next))))
+/*
+ * Tail queue insertion using pointer arithmetic.
+ */
+#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry, first, last, next, prev) do { \
+            *((void **) ((char *) (elm) + (entry) + (next))) = NULL;           \
+            *((void **) ((char *) (elm) + (entry) + (prev))) =                 \
+                *((void **) ((char *) (head) + (last)));                       \
+            **((void ***)((char *) (head) + (last))) = (elm);                  \
+            *((void **) ((char *) (head) + (last))) =                          \
+                (void *) ((char *) (elm) + (entry) + (next));                  \
+} while (/*CONSTCOND*/0)
diff --git a/migration/vmstate.c b/migration/vmstate.c
index bf3d5db..47cd052 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -5,6 +5,7 @@ 
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
 #include "qemu/error-report.h"
+#include "qemu/queue.h"
 #include "trace.h"
 #include "qjson.h"
 
@@ -121,6 +122,8 @@  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
                 if (field->flags & VMS_STRUCT) {
                     ret = vmstate_load_state(f, field->vmsd, addr,
                                              field->vmsd->version_id);
+                } else if (field->flags & VMS_CSTM) {
+                    ret = field->extend_get(f, field->meta_data, addr);
                 } else {
                     ret = field->info->get(f, addr, size);
 
@@ -193,6 +196,8 @@  static const char *vmfield_get_type_name(VMStateField *field)
 
     if (field->flags & VMS_STRUCT) {
         type = "struct";
+    } else if (field->flags & VMS_CSTM) {
+        type = "customized";
     } else if (field->info->name) {
         type = field->info->name;
     }
@@ -327,6 +332,8 @@  void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
                 }
                 if (field->flags & VMS_STRUCT) {
                     vmstate_save_state(f, field->vmsd, addr, vmdesc_loop);
+                } else if  (field->flags & VMS_CSTM) {
+                    field->extend_put(f, field->meta_data, addr, vmdesc_loop);
                 } else {
                     field->info->put(f, addr, size);
                 }
@@ -916,3 +923,80 @@  const VMStateInfo vmstate_info_bitmap = {
     .get = get_bitmap,
     .put = put_bitmap,
 };
+
+/* extend_get for QTAILQ */
+int vmstate_get_qtailq(QEMUFile *f, const void *metadata, void *opaque)
+{
+    bool link;
+    int ret = 0;
+    size_t first, last, next, prev, entry, size;
+    QTAILQMetaData *data = (QTAILQMetaData *)metadata;
+    const VMStateDescription *vmsd = data->vmsd;
+    int version_id = vmsd->version_id;
+    void *elm;
+
+    first = data->first;
+    last = data->last;
+    next = data->next;
+    prev = data->prev;
+    entry = data->entry;
+    size = data->size;
+
+    trace_vmstate_load_state(vmsd->name, version_id);
+    if (version_id > vmsd->version_id) {
+        trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
+        return -EINVAL;
+    }
+    if (version_id < vmsd->minimum_version_id) {
+        trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
+        return -EINVAL;
+    }
+
+    while (true) {
+        vmstate_info_bool.get(f, &link, sizeof(bool));
+        if (!link) {
+            break;
+        }
+
+        elm =  g_malloc(size);
+        ret = vmstate_load_state(f, vmsd, elm, version_id);
+        if (ret) {
+            return ret;
+        }
+        QTAILQ_RAW_INSERT_TAIL(opaque, elm, entry, first, last, next, prev);
+    }
+
+    trace_vmstate_load_state_end(vmsd->name, "end", ret);
+    return ret;
+}
+
+/* extend_get for QTAILQ */
+void vmstate_put_qtailq(QEMUFile *f, const void *metadata, void *opaque,
+                         QJSON *vmdesc)
+{
+    bool link = true;
+    size_t first, next, entry;
+    QTAILQMetaData *data = (QTAILQMetaData *)metadata;
+    const VMStateDescription *vmsd = data->vmsd;
+    void *elm;
+
+    first = data->first;
+    next = data->next;
+    entry = data->entry;
+
+    if (vmdesc) {
+        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
+        json_prop_int(vmdesc, "version", vmsd->version_id);
+        json_start_array(vmdesc, "fields");
+    }
+
+    QTAILQ_RAW_FOREACH(elm, opaque, entry, first, next) {
+        vmstate_info_bool.put(f, &link, sizeof(bool));
+        vmstate_save_state(f, vmsd, elm, vmdesc);
+    }
+    link = false;
+    vmstate_info_bool.put(f, &link, sizeof(bool));
+    if (vmdesc) {
+        json_end_array(vmdesc);
+    }
+}