diff mbox

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

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

Commit Message

Jianjun Duan May 31, 2016, 6:02 p.m. UTC
Currently we cannot directly transfer a QTAILQ instance because of the
limitation in the migration code. Here we introduce an approach to
transfer such structures. In our approach such a structure is tagged
with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
so that when VMS_CSTM is encountered, put and get from VMStateInfo are
called respectively. This approach will be used to transfer pending_events
and ccs_list in spapr state.

We also create some macros in qemu/queue.h to access a QTAILQ using pointer
arithmetic. 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 | 22 +++++++++++++
 include/qemu/queue.h        | 32 ++++++++++++++++++
 migration/vmstate.c         | 79 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)

Comments

Paolo Bonzini May 31, 2016, 7:54 p.m. UTC | #1
----- Original Message -----
> From: "Jianjun Duan" <duanj@linux.vnet.ibm.com>
> To: qemu-devel@nongnu.org
> Cc: qemu-ppc@nongnu.org, duanj@linux.vnet.ibm.com, dmitry@daynix.com, "peter maydell" <peter.maydell@linaro.org>,
> kraxel@redhat.com, mst@redhat.com, david@gibson.dropbear.id.au, pbonzini@redhat.com, veroniabahaa@gmail.com,
> quintela@redhat.com, "amit shah" <amit.shah@redhat.com>, mreitz@redhat.com, kwolf@redhat.com, rth@twiddle.net,
> aurelien@aurel32.net, "leon alrae" <leon.alrae@imgtec.com>, blauwirbel@gmail.com, "mark cave-ayland"
> <mark.cave-ayland@ilande.co.uk>, mdroth@linux.vnet.ibm.com
> Sent: Tuesday, May 31, 2016 8:02:42 PM
> Subject: [QEMU RFC PATCH v3 4/6] Migration: migrate QTAILQ
> 
> Currently we cannot directly transfer a QTAILQ instance because of the
> limitation in the migration code. Here we introduce an approach to
> transfer such structures. In our approach such a structure is tagged
> with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
> so that when VMS_CSTM is encountered, put and get from VMStateInfo are
> called respectively. This approach will be used to transfer pending_events
> and ccs_list in spapr state.
> 
> We also create some macros in qemu/queue.h to access a QTAILQ using pointer
> arithmetic. 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 | 22 +++++++++++++
>  include/qemu/queue.h        | 32 ++++++++++++++++++
>  migration/vmstate.c         | 79
>  +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 133 insertions(+)
> 
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 56a4171..da4ef7f 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -185,6 +185,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,

Please call this VMS_LINKED.  It can be adapted to other data
structures in qemu/queue.h if there is a need later on.

>  };
>  
>  struct VMStateField {
> @@ -245,6 +247,7 @@ extern const VMStateInfo vmstate_info_timer;
>  extern const VMStateInfo vmstate_info_buffer;
>  extern const VMStateInfo vmstate_info_unused_buffer;
>  extern const VMStateInfo vmstate_info_bitmap;
> +extern const VMStateInfo vmstate_info_qtailq;
>  
>  #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
>  #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
> @@ -656,6 +659,25 @@ extern const VMStateInfo vmstate_info_bitmap;
>      .offset       = offsetof(_state, _field),                        \
>  }
>  
> +/* For QTAILQ that need customized handling
> + * _type: type of QTAILQ element
> + * _next: name of QTAILQ entry field in QTAILQ element
> + * _vmsd: VMSD for QTAILQ element
> + * size: size of QTAILQ element
> + * start: offset of QTAILQ entry in QTAILQ element
> + */
> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
> +{                                                                        \
> +    .name         = (stringify(_field)),                                 \
> +    .version_id   = (_version),                                          \
> +    .vmsd         = &(_vmsd),                                            \
> +    .size         = sizeof(_type),                                       \
> +    .info         = &vmstate_info_qtailq,                                \
> +    .flags        = VMS_CSTM,                                            \
> +    .offset       = offsetof(_state, _field),                            \
> +    .start        = offsetof(_type, _next),                              \
> +}
> +
>  /* _f : field name
>     _f_n : num of elements field_name
>     _n : num of elements
> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
> index f781aa2..003e368 100644
> --- a/include/qemu/queue.h
> +++ b/include/qemu/queue.h
> @@ -437,3 +437,35 @@ 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 0
> +#define QTAILQ_LAST_OFFSET (sizeof(void *))
> +
> +/*
> + * Offsets of layout of a tail queue element.
> + */
> +#define QTAILQ_NEXT_OFFSET 0
> +#define QTAILQ_PREV_OFFSET (sizeof(void *))
> +
> +/*
> + * Tail queue tranversal using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_FOREACH(elm, head, entry)
> \
> +        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));
> \
> +             (elm);
> \
> +             (elm) =
> \
> +                 *((void **) ((char *) (elm) + (entry) +
> QTAILQ_NEXT_OFFSET)))
> +/*
> + * Tail queue insertion using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {
> \
> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;
> \
> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =
> \
> +            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));
> \
> +        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);
> \
> +        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =
> \
> +            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);
> \
> +} while (/*CONSTCOND*/0)
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 644ba1f..ff56650 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -5,7 +5,9 @@
>  #include "migration/vmstate.h"
>  #include "qemu/bitops.h"
>  #include "qemu/error-report.h"
> +#include "qemu/queue.h"
>  #include "trace.h"
> +#include "migration/qjson.h"
>  
>  static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription
>  *vmsd,
>                                      void *opaque, QJSON *vmdesc);
> @@ -120,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->info->get(f, addr, size, field);
>                  } else {
>                      ret = field->info->get(f, addr, size, NULL);
>  
> @@ -192,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;
>      }
> @@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
>                  } else {
>                      field->info->put(f, addr, size, NULL, NULL);
>                  }
> @@ -938,3 +946,74 @@ const VMStateInfo vmstate_info_bitmap = {
>      .get = get_bitmap,
>      .put = put_bitmap,
>  };
> +
> +/*get for QTAILQ */
> +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
> +                      VMStateField *field)
> +{
> +    bool link;
> +    int ret = 0;
> +    const VMStateDescription *vmsd = field->vmsd;
> +    size_t size = field->size;
> +    size_t entry = field->start;
> +    int version_id = field->version_id;
> +    void *elm;
> +
> +    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), NULL);

You can use qemu_get_byte here, and likewise qemu_put_byte below in
put_qtailq.

> +        if (!link) {
> +            break;
> +        }
> +
> +        elm =  g_malloc(size);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        if (ret) {
> +            return ret;
> +        }
> +        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
> +    }
> +
> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
> +    return ret;
> +}
> +
> +/* put for QTAILQ */
> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
> +                       VMStateField *field, QJSON *vmdesc)
> +{
> +    bool link = true;
> +    const VMStateDescription *vmsd = field->vmsd;
> +    size_t entry = field->start;
> +    void *elm;
> +
> +    if (vmdesc) {
> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
> +        json_prop_int(vmdesc, "version", vmsd->version_id);
> +        json_start_array(vmdesc, "fields");

You need to store the fields exactly once here, even if there are
0 or >1 elements.

Otherwise looks great now!

Paolo

> +    }
> +
> +    QTAILQ_RAW_FOREACH(elm, pv, entry) {
> +        vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
> +        vmstate_save_state(f, vmsd, elm, vmdesc);
> +    }
> +    link = false;
> +    vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
> +    if (vmdesc) {
> +        json_end_array(vmdesc);
> +    }
> +}
> +const VMStateInfo vmstate_info_qtailq = {
> +    .name = "qtailq",
> +    .get  = get_qtailq,
> +    .put  = put_qtailq,
> +};
> --
> 1.9.1
> 
>
Jianjun Duan May 31, 2016, 9:53 p.m. UTC | #2
On 05/31/2016 12:54 PM, Paolo Bonzini wrote:
> 
> 
> ----- Original Message -----
>> From: "Jianjun Duan" <duanj@linux.vnet.ibm.com>
>> To: qemu-devel@nongnu.org
>> Cc: qemu-ppc@nongnu.org, duanj@linux.vnet.ibm.com, dmitry@daynix.com, "peter maydell" <peter.maydell@linaro.org>,
>> kraxel@redhat.com, mst@redhat.com, david@gibson.dropbear.id.au, pbonzini@redhat.com, veroniabahaa@gmail.com,
>> quintela@redhat.com, "amit shah" <amit.shah@redhat.com>, mreitz@redhat.com, kwolf@redhat.com, rth@twiddle.net,
>> aurelien@aurel32.net, "leon alrae" <leon.alrae@imgtec.com>, blauwirbel@gmail.com, "mark cave-ayland"
>> <mark.cave-ayland@ilande.co.uk>, mdroth@linux.vnet.ibm.com
>> Sent: Tuesday, May 31, 2016 8:02:42 PM
>> Subject: [QEMU RFC PATCH v3 4/6] Migration: migrate QTAILQ
>>
>> Currently we cannot directly transfer a QTAILQ instance because of the
>> limitation in the migration code. Here we introduce an approach to
>> transfer such structures. In our approach such a structure is tagged
>> with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
>> so that when VMS_CSTM is encountered, put and get from VMStateInfo are
>> called respectively. This approach will be used to transfer pending_events
>> and ccs_list in spapr state.
>>
>> We also create some macros in qemu/queue.h to access a QTAILQ using pointer
>> arithmetic. 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 | 22 +++++++++++++
>>  include/qemu/queue.h        | 32 ++++++++++++++++++
>>  migration/vmstate.c         | 79
>>  +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 133 insertions(+)
>>
>> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
>> index 56a4171..da4ef7f 100644
>> --- a/include/migration/vmstate.h
>> +++ b/include/migration/vmstate.h
>> @@ -185,6 +185,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,
> 
> Please call this VMS_LINKED.  It can be adapted to other data
> structures in qemu/queue.h if there is a need later on.
> 
>>  };
>>  
>>  struct VMStateField {
>> @@ -245,6 +247,7 @@ extern const VMStateInfo vmstate_info_timer;
>>  extern const VMStateInfo vmstate_info_buffer;
>>  extern const VMStateInfo vmstate_info_unused_buffer;
>>  extern const VMStateInfo vmstate_info_bitmap;
>> +extern const VMStateInfo vmstate_info_qtailq;
>>  
>>  #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
>>  #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
>> @@ -656,6 +659,25 @@ extern const VMStateInfo vmstate_info_bitmap;
>>      .offset       = offsetof(_state, _field),                        \
>>  }
>>  
>> +/* For QTAILQ that need customized handling
>> + * _type: type of QTAILQ element
>> + * _next: name of QTAILQ entry field in QTAILQ element
>> + * _vmsd: VMSD for QTAILQ element
>> + * size: size of QTAILQ element
>> + * start: offset of QTAILQ entry in QTAILQ element
>> + */
>> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
>> +{                                                                        \
>> +    .name         = (stringify(_field)),                                 \
>> +    .version_id   = (_version),                                          \
>> +    .vmsd         = &(_vmsd),                                            \
>> +    .size         = sizeof(_type),                                       \
>> +    .info         = &vmstate_info_qtailq,                                \
>> +    .flags        = VMS_CSTM,                                            \
>> +    .offset       = offsetof(_state, _field),                            \
>> +    .start        = offsetof(_type, _next),                              \
>> +}
>> +
>>  /* _f : field name
>>     _f_n : num of elements field_name
>>     _n : num of elements
>> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
>> index f781aa2..003e368 100644
>> --- a/include/qemu/queue.h
>> +++ b/include/qemu/queue.h
>> @@ -437,3 +437,35 @@ 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 0
>> +#define QTAILQ_LAST_OFFSET (sizeof(void *))
>> +
>> +/*
>> + * Offsets of layout of a tail queue element.
>> + */
>> +#define QTAILQ_NEXT_OFFSET 0
>> +#define QTAILQ_PREV_OFFSET (sizeof(void *))
>> +
>> +/*
>> + * Tail queue tranversal using pointer arithmetic.
>> + */
>> +#define QTAILQ_RAW_FOREACH(elm, head, entry)
>> \
>> +        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));
>> \
>> +             (elm);
>> \
>> +             (elm) =
>> \
>> +                 *((void **) ((char *) (elm) + (entry) +
>> QTAILQ_NEXT_OFFSET)))
>> +/*
>> + * Tail queue insertion using pointer arithmetic.
>> + */
>> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {
>> \
>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;
>> \
>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =
>> \
>> +            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));
>> \
>> +        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);
>> \
>> +        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =
>> \
>> +            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);
>> \
>> +} while (/*CONSTCOND*/0)
>> diff --git a/migration/vmstate.c b/migration/vmstate.c
>> index 644ba1f..ff56650 100644
>> --- a/migration/vmstate.c
>> +++ b/migration/vmstate.c
>> @@ -5,7 +5,9 @@
>>  #include "migration/vmstate.h"
>>  #include "qemu/bitops.h"
>>  #include "qemu/error-report.h"
>> +#include "qemu/queue.h"
>>  #include "trace.h"
>> +#include "migration/qjson.h"
>>  
>>  static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription
>>  *vmsd,
>>                                      void *opaque, QJSON *vmdesc);
>> @@ -120,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->info->get(f, addr, size, field);
>>                  } else {
>>                      ret = field->info->get(f, addr, size, NULL);
>>  
>> @@ -192,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;
>>      }
>> @@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
>>                  } else {
>>                      field->info->put(f, addr, size, NULL, NULL);
>>                  }
>> @@ -938,3 +946,74 @@ const VMStateInfo vmstate_info_bitmap = {
>>      .get = get_bitmap,
>>      .put = put_bitmap,
>>  };
>> +
>> +/*get for QTAILQ */
>> +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>> +                      VMStateField *field)
>> +{
>> +    bool link;
>> +    int ret = 0;
>> +    const VMStateDescription *vmsd = field->vmsd;
>> +    size_t size = field->size;
>> +    size_t entry = field->start;
>> +    int version_id = field->version_id;
>> +    void *elm;
>> +
>> +    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), NULL);
> 
> You can use qemu_get_byte here, and likewise qemu_put_byte below in
> put_qtailq.

qemu_get/put is indeed better choice.
> 
>> +        if (!link) {
>> +            break;
>> +        }
>> +
>> +        elm =  g_malloc(size);
>> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
>> +        if (ret) {
>> +            return ret;
>> +        }
>> +        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
>> +    }
>> +
>> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
>> +    return ret;
>> +}
>> +
>> +/* put for QTAILQ */
>> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>> +                       VMStateField *field, QJSON *vmdesc)
>> +{
>> +    bool link = true;
>> +    const VMStateDescription *vmsd = field->vmsd;
>> +    size_t entry = field->start;
>> +    void *elm;
>> +
>> +    if (vmdesc) {
>> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
>> +        json_prop_int(vmdesc, "version", vmsd->version_id);
>> +        json_start_array(vmdesc, "fields");
> 
> You need to store the fields exactly once here, even if there are
> 0 or >1 elements.
> 
Do you mean the json entries? I think it is already doing that.
> Otherwise looks great now!
> 
> Paolo
> 
>> +    }
>> +
>> +    QTAILQ_RAW_FOREACH(elm, pv, entry) {
>> +        vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
>> +        vmstate_save_state(f, vmsd, elm, vmdesc);
>> +    }
>> +    link = false;
>> +    vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
>> +    if (vmdesc) {
>> +        json_end_array(vmdesc);
>> +    }
>> +}
>> +const VMStateInfo vmstate_info_qtailq = {
>> +    .name = "qtailq",
>> +    .get  = get_qtailq,
>> +    .put  = put_qtailq,
>> +};
>> --
>> 1.9.1
>>
>>
> 

Thanks,
Jianjun
Paolo Bonzini June 1, 2016, 3:29 p.m. UTC | #3
On 31/05/2016 23:53, Jianjun Duan wrote:
> 
> 
> On 05/31/2016 12:54 PM, Paolo Bonzini wrote:
>>
>>
>> ----- Original Message -----
>>> From: "Jianjun Duan" <duanj@linux.vnet.ibm.com>
>>> To: qemu-devel@nongnu.org
>>> Cc: qemu-ppc@nongnu.org, duanj@linux.vnet.ibm.com, dmitry@daynix.com, "peter maydell" <peter.maydell@linaro.org>,
>>> kraxel@redhat.com, mst@redhat.com, david@gibson.dropbear.id.au, pbonzini@redhat.com, veroniabahaa@gmail.com,
>>> quintela@redhat.com, "amit shah" <amit.shah@redhat.com>, mreitz@redhat.com, kwolf@redhat.com, rth@twiddle.net,
>>> aurelien@aurel32.net, "leon alrae" <leon.alrae@imgtec.com>, blauwirbel@gmail.com, "mark cave-ayland"
>>> <mark.cave-ayland@ilande.co.uk>, mdroth@linux.vnet.ibm.com
>>> Sent: Tuesday, May 31, 2016 8:02:42 PM
>>> Subject: [QEMU RFC PATCH v3 4/6] Migration: migrate QTAILQ
>>>
>>> Currently we cannot directly transfer a QTAILQ instance because of the
>>> limitation in the migration code. Here we introduce an approach to
>>> transfer such structures. In our approach such a structure is tagged
>>> with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
>>> so that when VMS_CSTM is encountered, put and get from VMStateInfo are
>>> called respectively. This approach will be used to transfer pending_events
>>> and ccs_list in spapr state.
>>>
>>> We also create some macros in qemu/queue.h to access a QTAILQ using pointer
>>> arithmetic. 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 | 22 +++++++++++++
>>>  include/qemu/queue.h        | 32 ++++++++++++++++++
>>>  migration/vmstate.c         | 79
>>>  +++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 133 insertions(+)
>>>
>>> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
>>> index 56a4171..da4ef7f 100644
>>> --- a/include/migration/vmstate.h
>>> +++ b/include/migration/vmstate.h
>>> @@ -185,6 +185,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,
>>
>> Please call this VMS_LINKED.  It can be adapted to other data
>> structures in qemu/queue.h if there is a need later on.
>>
>>>  };
>>>  
>>>  struct VMStateField {
>>> @@ -245,6 +247,7 @@ extern const VMStateInfo vmstate_info_timer;
>>>  extern const VMStateInfo vmstate_info_buffer;
>>>  extern const VMStateInfo vmstate_info_unused_buffer;
>>>  extern const VMStateInfo vmstate_info_bitmap;
>>> +extern const VMStateInfo vmstate_info_qtailq;
>>>  
>>>  #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
>>>  #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
>>> @@ -656,6 +659,25 @@ extern const VMStateInfo vmstate_info_bitmap;
>>>      .offset       = offsetof(_state, _field),                        \
>>>  }
>>>  
>>> +/* For QTAILQ that need customized handling
>>> + * _type: type of QTAILQ element
>>> + * _next: name of QTAILQ entry field in QTAILQ element
>>> + * _vmsd: VMSD for QTAILQ element
>>> + * size: size of QTAILQ element
>>> + * start: offset of QTAILQ entry in QTAILQ element
>>> + */
>>> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
>>> +{                                                                        \
>>> +    .name         = (stringify(_field)),                                 \
>>> +    .version_id   = (_version),                                          \
>>> +    .vmsd         = &(_vmsd),                                            \
>>> +    .size         = sizeof(_type),                                       \
>>> +    .info         = &vmstate_info_qtailq,                                \
>>> +    .flags        = VMS_CSTM,                                            \
>>> +    .offset       = offsetof(_state, _field),                            \
>>> +    .start        = offsetof(_type, _next),                              \
>>> +}
>>> +
>>>  /* _f : field name
>>>     _f_n : num of elements field_name
>>>     _n : num of elements
>>> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
>>> index f781aa2..003e368 100644
>>> --- a/include/qemu/queue.h
>>> +++ b/include/qemu/queue.h
>>> @@ -437,3 +437,35 @@ 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 0
>>> +#define QTAILQ_LAST_OFFSET (sizeof(void *))
>>> +
>>> +/*
>>> + * Offsets of layout of a tail queue element.
>>> + */
>>> +#define QTAILQ_NEXT_OFFSET 0
>>> +#define QTAILQ_PREV_OFFSET (sizeof(void *))
>>> +
>>> +/*
>>> + * Tail queue tranversal using pointer arithmetic.
>>> + */
>>> +#define QTAILQ_RAW_FOREACH(elm, head, entry)
>>> \
>>> +        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));
>>> \
>>> +             (elm);
>>> \
>>> +             (elm) =
>>> \
>>> +                 *((void **) ((char *) (elm) + (entry) +
>>> QTAILQ_NEXT_OFFSET)))
>>> +/*
>>> + * Tail queue insertion using pointer arithmetic.
>>> + */
>>> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {
>>> \
>>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;
>>> \
>>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =
>>> \
>>> +            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));
>>> \
>>> +        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);
>>> \
>>> +        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =
>>> \
>>> +            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);
>>> \
>>> +} while (/*CONSTCOND*/0)
>>> diff --git a/migration/vmstate.c b/migration/vmstate.c
>>> index 644ba1f..ff56650 100644
>>> --- a/migration/vmstate.c
>>> +++ b/migration/vmstate.c
>>> @@ -5,7 +5,9 @@
>>>  #include "migration/vmstate.h"
>>>  #include "qemu/bitops.h"
>>>  #include "qemu/error-report.h"
>>> +#include "qemu/queue.h"
>>>  #include "trace.h"
>>> +#include "migration/qjson.h"
>>>  
>>>  static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription
>>>  *vmsd,
>>>                                      void *opaque, QJSON *vmdesc);
>>> @@ -120,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->info->get(f, addr, size, field);
>>>                  } else {
>>>                      ret = field->info->get(f, addr, size, NULL);
>>>  
>>> @@ -192,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;
>>>      }
>>> @@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
>>>                  } else {
>>>                      field->info->put(f, addr, size, NULL, NULL);
>>>                  }
>>> @@ -938,3 +946,74 @@ const VMStateInfo vmstate_info_bitmap = {
>>>      .get = get_bitmap,
>>>      .put = put_bitmap,
>>>  };
>>> +
>>> +/*get for QTAILQ */
>>> +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>>> +                      VMStateField *field)
>>> +{
>>> +    bool link;
>>> +    int ret = 0;
>>> +    const VMStateDescription *vmsd = field->vmsd;
>>> +    size_t size = field->size;
>>> +    size_t entry = field->start;
>>> +    int version_id = field->version_id;
>>> +    void *elm;
>>> +
>>> +    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), NULL);
>>
>> You can use qemu_get_byte here, and likewise qemu_put_byte below in
>> put_qtailq.
> 
> qemu_get/put is indeed better choice.
>>
>>> +        if (!link) {
>>> +            break;
>>> +        }
>>> +
>>> +        elm =  g_malloc(size);
>>> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
>>> +        if (ret) {
>>> +            return ret;
>>> +        }
>>> +        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
>>> +    }
>>> +
>>> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
>>> +    return ret;
>>> +}
>>> +
>>> +/* put for QTAILQ */
>>> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>>> +                       VMStateField *field, QJSON *vmdesc)
>>> +{
>>> +    bool link = true;
>>> +    const VMStateDescription *vmsd = field->vmsd;
>>> +    size_t entry = field->start;
>>> +    void *elm;
>>> +
>>> +    if (vmdesc) {
>>> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
>>> +        json_prop_int(vmdesc, "version", vmsd->version_id);
>>> +        json_start_array(vmdesc, "fields");
>>
>> You need to store the fields exactly once here, even if there are
>> 0 or >1 elements.
>>
> Do you mean the json entries? I think it is already doing that.

In the case of 0 entries we don't go through the loop, so the JSON
entries are definitely missing in that case.  I'm not sure if QJSON
handles duplicates in the case of 2+ entries.

Thanks,

Paolo
Jianjun Duan June 1, 2016, 5:06 p.m. UTC | #4
On 06/01/2016 08:29 AM, Paolo Bonzini wrote:
> 
> 
> On 31/05/2016 23:53, Jianjun Duan wrote:
>>
>>
>> On 05/31/2016 12:54 PM, Paolo Bonzini wrote:
>>>
>>>
>>> ----- Original Message -----
>>>> From: "Jianjun Duan" <duanj@linux.vnet.ibm.com>
>>>> To: qemu-devel@nongnu.org
>>>> Cc: qemu-ppc@nongnu.org, duanj@linux.vnet.ibm.com, dmitry@daynix.com, "peter maydell" <peter.maydell@linaro.org>,
>>>> kraxel@redhat.com, mst@redhat.com, david@gibson.dropbear.id.au, pbonzini@redhat.com, veroniabahaa@gmail.com,
>>>> quintela@redhat.com, "amit shah" <amit.shah@redhat.com>, mreitz@redhat.com, kwolf@redhat.com, rth@twiddle.net,
>>>> aurelien@aurel32.net, "leon alrae" <leon.alrae@imgtec.com>, blauwirbel@gmail.com, "mark cave-ayland"
>>>> <mark.cave-ayland@ilande.co.uk>, mdroth@linux.vnet.ibm.com
>>>> Sent: Tuesday, May 31, 2016 8:02:42 PM
>>>> Subject: [QEMU RFC PATCH v3 4/6] Migration: migrate QTAILQ
>>>>
>>>> Currently we cannot directly transfer a QTAILQ instance because of the
>>>> limitation in the migration code. Here we introduce an approach to
>>>> transfer such structures. In our approach such a structure is tagged
>>>> with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
>>>> so that when VMS_CSTM is encountered, put and get from VMStateInfo are
>>>> called respectively. This approach will be used to transfer pending_events
>>>> and ccs_list in spapr state.
>>>>
>>>> We also create some macros in qemu/queue.h to access a QTAILQ using pointer
>>>> arithmetic. 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 | 22 +++++++++++++
>>>>  include/qemu/queue.h        | 32 ++++++++++++++++++
>>>>  migration/vmstate.c         | 79
>>>>  +++++++++++++++++++++++++++++++++++++++++++++
>>>>  3 files changed, 133 insertions(+)
>>>>
>>>> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
>>>> index 56a4171..da4ef7f 100644
>>>> --- a/include/migration/vmstate.h
>>>> +++ b/include/migration/vmstate.h
>>>> @@ -185,6 +185,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,
>>>
>>> Please call this VMS_LINKED.  It can be adapted to other data
>>> structures in qemu/queue.h if there is a need later on.
>>>
>>>>  };
>>>>  
>>>>  struct VMStateField {
>>>> @@ -245,6 +247,7 @@ extern const VMStateInfo vmstate_info_timer;
>>>>  extern const VMStateInfo vmstate_info_buffer;
>>>>  extern const VMStateInfo vmstate_info_unused_buffer;
>>>>  extern const VMStateInfo vmstate_info_bitmap;
>>>> +extern const VMStateInfo vmstate_info_qtailq;
>>>>  
>>>>  #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
>>>>  #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
>>>> @@ -656,6 +659,25 @@ extern const VMStateInfo vmstate_info_bitmap;
>>>>      .offset       = offsetof(_state, _field),                        \
>>>>  }
>>>>  
>>>> +/* For QTAILQ that need customized handling
>>>> + * _type: type of QTAILQ element
>>>> + * _next: name of QTAILQ entry field in QTAILQ element
>>>> + * _vmsd: VMSD for QTAILQ element
>>>> + * size: size of QTAILQ element
>>>> + * start: offset of QTAILQ entry in QTAILQ element
>>>> + */
>>>> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
>>>> +{                                                                        \
>>>> +    .name         = (stringify(_field)),                                 \
>>>> +    .version_id   = (_version),                                          \
>>>> +    .vmsd         = &(_vmsd),                                            \
>>>> +    .size         = sizeof(_type),                                       \
>>>> +    .info         = &vmstate_info_qtailq,                                \
>>>> +    .flags        = VMS_CSTM,                                            \
>>>> +    .offset       = offsetof(_state, _field),                            \
>>>> +    .start        = offsetof(_type, _next),                              \
>>>> +}
>>>> +
>>>>  /* _f : field name
>>>>     _f_n : num of elements field_name
>>>>     _n : num of elements
>>>> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
>>>> index f781aa2..003e368 100644
>>>> --- a/include/qemu/queue.h
>>>> +++ b/include/qemu/queue.h
>>>> @@ -437,3 +437,35 @@ 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 0
>>>> +#define QTAILQ_LAST_OFFSET (sizeof(void *))
>>>> +
>>>> +/*
>>>> + * Offsets of layout of a tail queue element.
>>>> + */
>>>> +#define QTAILQ_NEXT_OFFSET 0
>>>> +#define QTAILQ_PREV_OFFSET (sizeof(void *))
>>>> +
>>>> +/*
>>>> + * Tail queue tranversal using pointer arithmetic.
>>>> + */
>>>> +#define QTAILQ_RAW_FOREACH(elm, head, entry)
>>>> \
>>>> +        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));
>>>> \
>>>> +             (elm);
>>>> \
>>>> +             (elm) =
>>>> \
>>>> +                 *((void **) ((char *) (elm) + (entry) +
>>>> QTAILQ_NEXT_OFFSET)))
>>>> +/*
>>>> + * Tail queue insertion using pointer arithmetic.
>>>> + */
>>>> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {
>>>> \
>>>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;
>>>> \
>>>> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =
>>>> \
>>>> +            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));
>>>> \
>>>> +        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);
>>>> \
>>>> +        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =
>>>> \
>>>> +            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);
>>>> \
>>>> +} while (/*CONSTCOND*/0)
>>>> diff --git a/migration/vmstate.c b/migration/vmstate.c
>>>> index 644ba1f..ff56650 100644
>>>> --- a/migration/vmstate.c
>>>> +++ b/migration/vmstate.c
>>>> @@ -5,7 +5,9 @@
>>>>  #include "migration/vmstate.h"
>>>>  #include "qemu/bitops.h"
>>>>  #include "qemu/error-report.h"
>>>> +#include "qemu/queue.h"
>>>>  #include "trace.h"
>>>> +#include "migration/qjson.h"
>>>>  
>>>>  static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription
>>>>  *vmsd,
>>>>                                      void *opaque, QJSON *vmdesc);
>>>> @@ -120,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->info->get(f, addr, size, field);
>>>>                  } else {
>>>>                      ret = field->info->get(f, addr, size, NULL);
>>>>  
>>>> @@ -192,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;
>>>>      }
>>>> @@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
>>>>                  } else {
>>>>                      field->info->put(f, addr, size, NULL, NULL);
>>>>                  }
>>>> @@ -938,3 +946,74 @@ const VMStateInfo vmstate_info_bitmap = {
>>>>      .get = get_bitmap,
>>>>      .put = put_bitmap,
>>>>  };
>>>> +
>>>> +/*get for QTAILQ */
>>>> +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>>>> +                      VMStateField *field)
>>>> +{
>>>> +    bool link;
>>>> +    int ret = 0;
>>>> +    const VMStateDescription *vmsd = field->vmsd;
>>>> +    size_t size = field->size;
>>>> +    size_t entry = field->start;
>>>> +    int version_id = field->version_id;
>>>> +    void *elm;
>>>> +
>>>> +    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), NULL);
>>>
>>> You can use qemu_get_byte here, and likewise qemu_put_byte below in
>>> put_qtailq.
>>
>> qemu_get/put is indeed better choice.
>>>
>>>> +        if (!link) {
>>>> +            break;
>>>> +        }
>>>> +
>>>> +        elm =  g_malloc(size);
>>>> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
>>>> +        if (ret) {
>>>> +            return ret;
>>>> +        }
>>>> +        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
>>>> +    }
>>>> +
>>>> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +/* put for QTAILQ */
>>>> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>>>> +                       VMStateField *field, QJSON *vmdesc)
>>>> +{
>>>> +    bool link = true;
>>>> +    const VMStateDescription *vmsd = field->vmsd;
>>>> +    size_t entry = field->start;
>>>> +    void *elm;
>>>> +
>>>> +    if (vmdesc) {
>>>> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
>>>> +        json_prop_int(vmdesc, "version", vmsd->version_id);
>>>> +        json_start_array(vmdesc, "fields");
>>>
>>> You need to store the fields exactly once here, even if there are
>>> 0 or >1 elements.
>>>
>> Do you mean the json entries? I think it is already doing that.
> 
> In the case of 0 entries we don't go through the loop, so the JSON
> entries are definitely missing in that case.  I'm not sure if QJSON
> handles duplicates in the case of 2+ entries.
The vmsd here is the vmsd for the queue elements. Not for the queue.
Maybe the stuff written here should be information about the qtailq
instead, but we don't have a vmsd for the queue as a whole.

As it stands, it will record the name, version of the element type. If
the queue is empty, it records nothing in the fields. otherwise it will
record each element and repeat.

In vmstate_save_state, the vmsd of the array element for a uncompressed
array is recorded every time an array element is saved. The number of
written bytes is also recorded. If the array has 0 elements, it is not
recorded.

If we want to make the behavior consistent, we should not do any json
stuff in put_qtatilq function.

If we want to record the vmsd of the queue element exactly once, I can
set the vmdesc to null after the first iteration. But we may need a
recursive function just to dump out the vmsd when the queue is empty,
and record that 0 bytes are written for each field.

I would say that let's remove the json stuff here to be consistent with
array behavior.

> 
> Thanks,
> 
> Paolo
> 
Thanks,
Jianjun
Paolo Bonzini June 1, 2016, 6:32 p.m. UTC | #5
On 01/06/2016 19:06, Jianjun Duan wrote:
> On 06/01/2016 08:29 AM, Paolo Bonzini wrote:
>> On 31/05/2016 23:53, Jianjun Duan wrote:
>>> On 05/31/2016 12:54 PM, Paolo Bonzini wrote:
>>>>> +/* put for QTAILQ */
>>>>> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>>>>> +                       VMStateField *field, QJSON *vmdesc)
>>>>> +{
>>>>> +    bool link = true;
>>>>> +    const VMStateDescription *vmsd = field->vmsd;
>>>>> +    size_t entry = field->start;
>>>>> +    void *elm;
>>>>> +
>>>>> +    if (vmdesc) {
>>>>> +        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
>>>>> +        json_prop_int(vmdesc, "version", vmsd->version_id);
>>>>> +        json_start_array(vmdesc, "fields");
>>>>
>>>> You need to store the fields exactly once here, even if there are
>>>> 0 or >1 elements.
>>>>
>>> Do you mean the json entries? I think it is already doing that.
>>
>> In the case of 0 entries we don't go through the loop, so the JSON
>> entries are definitely missing in that case.  I'm not sure if QJSON
>> handles duplicates in the case of 2+ entries.
> 
> The vmsd here is the vmsd for the queue elements. Not for the queue.
> Maybe the stuff written here should be information about the qtailq
> instead, but we don't have a vmsd for the queue as a whole.

You're right, you could use vmsd_can_compress but it's not necessary to
do so.  Your code is fine.

Paolo
David Gibson June 2, 2016, 3:58 a.m. UTC | #6
On Tue, May 31, 2016 at 11:02:42AM -0700, Jianjun Duan wrote:
> Currently we cannot directly transfer a QTAILQ instance because of the
> limitation in the migration code. Here we introduce an approach to
> transfer such structures. In our approach such a structure is tagged
> with VMS_CSTM. We then modified vmstate_save_state and vmstate_load_state
> so that when VMS_CSTM is encountered, put and get from VMStateInfo are
> called respectively. This approach will be used to transfer pending_events
> and ccs_list in spapr state.
> 
> We also create some macros in qemu/queue.h to access a QTAILQ using pointer
> arithmetic. 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>

I'm comfortable with 3&4 of 6, but I'd prefer to see them merged via
the migration tree.

Juan, Dave, are you ok to merge these?

> ---
>  include/migration/vmstate.h | 22 +++++++++++++
>  include/qemu/queue.h        | 32 ++++++++++++++++++
>  migration/vmstate.c         | 79 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 133 insertions(+)
> 
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 56a4171..da4ef7f 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -185,6 +185,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,
>  };
>  
>  struct VMStateField {
> @@ -245,6 +247,7 @@ extern const VMStateInfo vmstate_info_timer;
>  extern const VMStateInfo vmstate_info_buffer;
>  extern const VMStateInfo vmstate_info_unused_buffer;
>  extern const VMStateInfo vmstate_info_bitmap;
> +extern const VMStateInfo vmstate_info_qtailq;
>  
>  #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
>  #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
> @@ -656,6 +659,25 @@ extern const VMStateInfo vmstate_info_bitmap;
>      .offset       = offsetof(_state, _field),                        \
>  }
>  
> +/* For QTAILQ that need customized handling
> + * _type: type of QTAILQ element
> + * _next: name of QTAILQ entry field in QTAILQ element
> + * _vmsd: VMSD for QTAILQ element
> + * size: size of QTAILQ element
> + * start: offset of QTAILQ entry in QTAILQ element
> + */
> +#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
> +{                                                                        \
> +    .name         = (stringify(_field)),                                 \
> +    .version_id   = (_version),                                          \
> +    .vmsd         = &(_vmsd),                                            \
> +    .size         = sizeof(_type),                                       \
> +    .info         = &vmstate_info_qtailq,                                \
> +    .flags        = VMS_CSTM,                                            \
> +    .offset       = offsetof(_state, _field),                            \
> +    .start        = offsetof(_type, _next),                              \
> +}
> +
>  /* _f : field name
>     _f_n : num of elements field_name
>     _n : num of elements
> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
> index f781aa2..003e368 100644
> --- a/include/qemu/queue.h
> +++ b/include/qemu/queue.h
> @@ -437,3 +437,35 @@ 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 0
> +#define QTAILQ_LAST_OFFSET (sizeof(void *))
> +
> +/*
> + * Offsets of layout of a tail queue element.
> + */
> +#define QTAILQ_NEXT_OFFSET 0
> +#define QTAILQ_PREV_OFFSET (sizeof(void *))
> +
> +/*
> + * Tail queue tranversal using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_FOREACH(elm, head, entry)                                   \
> +        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));     \
> +             (elm);                                                            \
> +             (elm) =                                                           \
> +                 *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)))
> +/*
> + * Tail queue insertion using pointer arithmetic.
> + */
> +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {                          \
> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;   \
> +        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =         \
> +            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));                \
> +        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);           \
> +        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =                  \
> +            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);          \
> +} while (/*CONSTCOND*/0)
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 644ba1f..ff56650 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -5,7 +5,9 @@
>  #include "migration/vmstate.h"
>  #include "qemu/bitops.h"
>  #include "qemu/error-report.h"
> +#include "qemu/queue.h"
>  #include "trace.h"
> +#include "migration/qjson.h"
>  
>  static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
>                                      void *opaque, QJSON *vmdesc);
> @@ -120,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->info->get(f, addr, size, field);
>                  } else {
>                      ret = field->info->get(f, addr, size, NULL);
>  
> @@ -192,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;
>      }
> @@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
>                  } else {
>                      field->info->put(f, addr, size, NULL, NULL);
>                  }
> @@ -938,3 +946,74 @@ const VMStateInfo vmstate_info_bitmap = {
>      .get = get_bitmap,
>      .put = put_bitmap,
>  };
> +
> +/*get for QTAILQ */
> +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
> +                      VMStateField *field)
> +{
> +    bool link;
> +    int ret = 0;
> +    const VMStateDescription *vmsd = field->vmsd;
> +    size_t size = field->size;
> +    size_t entry = field->start;
> +    int version_id = field->version_id;
> +    void *elm;
> +
> +    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), NULL);
> +        if (!link) {
> +            break;
> +        }
> +
> +        elm =  g_malloc(size);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        if (ret) {
> +            return ret;
> +        }
> +        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
> +    }
> +
> +    trace_vmstate_load_state_end(vmsd->name, "end", ret);
> +    return ret;
> +}
> +
> +/* put for QTAILQ */
> +static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
> +                       VMStateField *field, QJSON *vmdesc)
> +{
> +    bool link = true;
> +    const VMStateDescription *vmsd = field->vmsd;
> +    size_t entry = field->start;
> +    void *elm;
> +
> +    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, pv, entry) {
> +        vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
> +        vmstate_save_state(f, vmsd, elm, vmdesc);
> +    }
> +    link = false;
> +    vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
> +    if (vmdesc) {
> +        json_end_array(vmdesc);
> +    }
> +}
> +const VMStateInfo vmstate_info_qtailq = {
> +    .name = "qtailq",
> +    .get  = get_qtailq,
> +    .put  = put_qtailq,
> +};
Sascha Silbe June 2, 2016, 3:01 p.m. UTC | #7
Dear Jianjun,

Jianjun Duan <duanj@linux.vnet.ibm.com> writes:

[include/migration/vmstate.h]
> @@ -185,6 +185,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,

Can you describe (in the comment) how this customised handling is
performed, please? I.e. describe what exactly happens if the flag is
set, from the point of view of an API consumer.

Also, why do you need this flag at all? The only change I can see is
that you pass additional information to VMStateInfo.get() / .put(),
using NULL if it's not set. Why don't you just always pass the
additional information? If the additional information is not needed by
get() / put() the parameter will be unused anyway.

Sascha
Jianjun Duan June 2, 2016, 4:11 p.m. UTC | #8
Hi Sascha,

On 06/02/2016 08:01 AM, Sascha Silbe wrote:
> Dear Jianjun,
> 
> Jianjun Duan <duanj@linux.vnet.ibm.com> writes:
> 
> [include/migration/vmstate.h]
>> @@ -185,6 +185,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,
> 
> Can you describe (in the comment) how this customised handling is
> performed, please? I.e. describe what exactly happens if the flag is
> set, from the point of view of an API consumer.

I will add more comments. When this flag is set VMStateInfo.get/put will
be used in vmstate_load/save_state instead of recursive call. And the
user should implement VMStateInfo.get/put to handle the concerned data
structure.
> Also, why do you need this flag at all? The only change I can see is
> that you pass additional information to VMStateInfo.get() / .put(),
> using NULL if it's not set. Why don't you just always pass the
> additional information? If the additional information is not needed by
> get() / put() the parameter will be unused anyway.
You can do it without creating this flag. Instead just to check if info
is set in the field. However I think it is more readable and more robust
to check this flag in vmstate_load/get_state.
> Sascha
> 

Thanks,
Jianjun
Jianjun Duan June 3, 2016, 5:12 p.m. UTC | #9
On 06/02/2016 08:01 AM, Sascha Silbe wrote:
> Dear Jianjun,
> 
> Jianjun Duan <duanj@linux.vnet.ibm.com> writes:
> 
> [include/migration/vmstate.h]
>> @@ -185,6 +185,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,
> 
> Can you describe (in the comment) how this customised handling is
> performed, please? I.e. describe what exactly happens if the flag is
> set, from the point of view of an API consumer.
> 
> Also, why do you need this flag at all? The only change I can see is
> that you pass additional information to VMStateInfo.get() / .put(),
> using NULL if it's not set. Why don't you just always pass the
> additional information? If the additional information is not needed by
> get() / put() the parameter will be unused anyway.
> 

I now agree with you after some thought.
> Sascha
> 

Thanks,
Jianjun
Paolo Bonzini June 6, 2016, 1:47 p.m. UTC | #10
On 02/06/2016 17:01, Sascha Silbe wrote:
>> > +    /* For fields which need customized handling, such as QTAILQ in queue.h*/
>> > +    VMS_CSTM            = 0x8000,
> Can you describe (in the comment) how this customised handling is
> performed, please? I.e. describe what exactly happens if the flag is
> set, from the point of view of an API consumer.
> 
> Also, why do you need this flag at all? The only change I can see is
> that you pass additional information to VMStateInfo.get() / .put(),
> using NULL if it's not set. Why don't you just always pass the
> additional information? If the additional information is not needed by
> get() / put() the parameter will be unused anyway.

Having the flag seemed like a good idea to me, because it gives a
specific meaning to the offset fields in the VMStateField.  On the other
hand, it's true that it's unused...

Paolo
Dr. David Alan Gilbert June 7, 2016, 2:43 p.m. UTC | #11
* Jianjun Duan (duanj@linux.vnet.ibm.com) wrote:
> Hi Sascha,
> 
> On 06/02/2016 08:01 AM, Sascha Silbe wrote:
> > Dear Jianjun,
> > 
> > Jianjun Duan <duanj@linux.vnet.ibm.com> writes:
> > 
> > [include/migration/vmstate.h]
> >> @@ -185,6 +185,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,
> > 
> > Can you describe (in the comment) how this customised handling is
> > performed, please? I.e. describe what exactly happens if the flag is
> > set, from the point of view of an API consumer.
> 
> I will add more comments. When this flag is set VMStateInfo.get/put will
> be used in vmstate_load/save_state instead of recursive call. And the
> user should implement VMStateInfo.get/put to handle the concerned data
> structure.
> > Also, why do you need this flag at all? The only change I can see is
> > that you pass additional information to VMStateInfo.get() / .put(),
> > using NULL if it's not set. Why don't you just always pass the
> > additional information? If the additional information is not needed by
> > get() / put() the parameter will be unused anyway.
> You can do it without creating this flag. Instead just to check if info
> is set in the field. However I think it is more readable and more robust
> to check this flag in vmstate_load/get_state.

Apologies for not getting around to this sooner; I was on holiday when you first
sent it.

But:
  a) Is there a reason to use the 'bool' between each element; can't you count the
     length of the queue, send the length and then send the contents?  In that case
     it should look a lot more like an ARRAY case on the wire.
  b) I think you should really try and split it into two parts:
    1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
       way of allocating/counting/reading the elements
    2) A version of that which is for a QTAILQ.
       It's important that whatever ends up on the migration stream doesn't reflect
       that it happens to be implemented as a QTAILQ; so if you decide to change
       it later the migration compatibility doesn't break.
  c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
  d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
     turn the tracing on on both sides :-)
  e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
     I don't think I understand why you can't use the standard QTAILQ macros.
  f) You might need to fix up Amit's scripts/vmstate-static-checker.py

Dave

> > Sascha
> > 
> 
> Thanks,
> Jianjun
> 
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Paolo Bonzini June 7, 2016, 4:31 p.m. UTC | #12
On 07/06/2016 16:43, Dr. David Alan Gilbert wrote:
>   b) I think you should really try and split it into two parts:
>     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
>        way of allocating/counting/reading the elements
>     2) A version of that which is for a QTAILQ.
>        It's important that whatever ends up on the migration stream doesn't reflect
>        that it happens to be implemented as a QTAILQ; so if you decide to change
>        it later the migration compatibility doesn't break.

(Just to clarify before Jianjun wakes up) a VMSTATE_ARRAY is just a
sequence of elements.  The count, if needed as in a VARRAY, is stored
earlier and separately.  Currently lists (including this QTAILQ) are
usually represented in the migration stream as a sequence of elements
preceded by "1" and terminated by a "0".  Would you like to change it to
a count + sequence as well?

This would prevent using the new QTAILQ support for other existing lists
such as virtio-blk and scsi's request lists.

>   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
>   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
>      turn the tracing on on both sides :-)
>   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
>      I don't think I understand why you can't use the standard QTAILQ macros.

Because they assume a particular type. The "raw" version need to work on
void*.

Thanks,

Paolo

>   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
Michael S. Tsirkin June 7, 2016, 4:33 p.m. UTC | #13
On Tue, Jun 07, 2016 at 06:31:41PM +0200, Paolo Bonzini wrote:
> 
> 
> On 07/06/2016 16:43, Dr. David Alan Gilbert wrote:
> >   b) I think you should really try and split it into two parts:
> >     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
> >        way of allocating/counting/reading the elements
> >     2) A version of that which is for a QTAILQ.
> >        It's important that whatever ends up on the migration stream doesn't reflect
> >        that it happens to be implemented as a QTAILQ; so if you decide to change
> >        it later the migration compatibility doesn't break.
> 
> (Just to clarify before Jianjun wakes up) a VMSTATE_ARRAY is just a
> sequence of elements.  The count, if needed as in a VARRAY, is stored
> earlier and separately.

And if you migrate the count, you must validate that
it's not bigger than an actual array size before VARRAY.

>  Currently lists (including this QTAILQ) are
> usually represented in the migration stream as a sequence of elements
> preceded by "1" and terminated by a "0".  Would you like to change it to
> a count + sequence as well?
> 
> This would prevent using the new QTAILQ support for other existing lists
> such as virtio-blk and scsi's request lists.
> 
> >   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
> >   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
> >      turn the tracing on on both sides :-)
> >   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
> >      I don't think I understand why you can't use the standard QTAILQ macros.
> 
> Because they assume a particular type. The "raw" version need to work on
> void*.
> 
> Thanks,
> 
> Paolo
> 
> >   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
Dr. David Alan Gilbert June 7, 2016, 4:34 p.m. UTC | #14
* Paolo Bonzini (pbonzini@redhat.com) wrote:
> 
> 
> On 07/06/2016 16:43, Dr. David Alan Gilbert wrote:
> >   b) I think you should really try and split it into two parts:
> >     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
> >        way of allocating/counting/reading the elements
> >     2) A version of that which is for a QTAILQ.
> >        It's important that whatever ends up on the migration stream doesn't reflect
> >        that it happens to be implemented as a QTAILQ; so if you decide to change
> >        it later the migration compatibility doesn't break.
> 
> (Just to clarify before Jianjun wakes up) a VMSTATE_ARRAY is just a
> sequence of elements.  The count, if needed as in a VARRAY, is stored
> earlier and separately.  Currently lists (including this QTAILQ) are
> usually represented in the migration stream as a sequence of elements
> preceded by "1" and terminated by a "0".  Would you like to change it to
> a count + sequence as well?
> 
> This would prevent using the new QTAILQ support for other existing lists
> such as virtio-blk and scsi's request lists.

That depends if it's something you're trying to keep migration compatibility
with;  if you're trying to keep format compaibility then sure keep it as is.
If you're not trying to keep compatibility, then why can't virtio-blk or
scsi request lists also use a count rather than the markers?

> >   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
> >   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
> >      turn the tracing on on both sides :-)
> >   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
> >      I don't think I understand why you can't use the standard QTAILQ macros.
> 
> Because they assume a particular type. The "raw" version need to work on
> void*.

Ah OK.

Dave

> 
> Thanks,
> 
> Paolo
> 
> >   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Paolo Bonzini June 7, 2016, 4:35 p.m. UTC | #15
On 07/06/2016 18:34, Dr. David Alan Gilbert wrote:
> * Paolo Bonzini (pbonzini@redhat.com) wrote:
>>
>>
>> On 07/06/2016 16:43, Dr. David Alan Gilbert wrote:
>>>   b) I think you should really try and split it into two parts:
>>>     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
>>>        way of allocating/counting/reading the elements
>>>     2) A version of that which is for a QTAILQ.
>>>        It's important that whatever ends up on the migration stream doesn't reflect
>>>        that it happens to be implemented as a QTAILQ; so if you decide to change
>>>        it later the migration compatibility doesn't break.
>>
>> (Just to clarify before Jianjun wakes up) a VMSTATE_ARRAY is just a
>> sequence of elements.  The count, if needed as in a VARRAY, is stored
>> earlier and separately.  Currently lists (including this QTAILQ) are
>> usually represented in the migration stream as a sequence of elements
>> preceded by "1" and terminated by a "0".  Would you like to change it to
>> a count + sequence as well?
>>
>> This would prevent using the new QTAILQ support for other existing lists
>> such as virtio-blk and scsi's request lists.
> 
> That depends if it's something you're trying to keep migration compatibility
> with;  if you're trying to keep format compaibility then sure keep it as is.
> If you're not trying to keep compatibility, then why can't virtio-blk or
> scsi request lists also use a count rather than the markers?

We're trying to keep compatibility, and I think it's among the last bits
that are resisting conversion to vmstate.  Which explains my interest in
Jianjun's patches. :)

Paolo

>>>   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
>>>   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
>>>      turn the tracing on on both sides :-)
>>>   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
>>>      I don't think I understand why you can't use the standard QTAILQ macros.
>>
>> Because they assume a particular type. The "raw" version need to work on
>> void*.
> 
> Ah OK.
> 
> Dave
> 
>>
>> Thanks,
>>
>> Paolo
>>
>>>   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
>
Jianjun Duan June 7, 2016, 4:43 p.m. UTC | #16
On 06/07/2016 07:43 AM, Dr. David Alan Gilbert wrote:
> * Jianjun Duan (duanj@linux.vnet.ibm.com) wrote:
>> Hi Sascha,
>>
>> On 06/02/2016 08:01 AM, Sascha Silbe wrote:
>>> Dear Jianjun,
>>>
>>> Jianjun Duan <duanj@linux.vnet.ibm.com> writes:
>>>
>>> [include/migration/vmstate.h]
>>>> @@ -185,6 +185,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,
>>>
>>> Can you describe (in the comment) how this customised handling is
>>> performed, please? I.e. describe what exactly happens if the flag is
>>> set, from the point of view of an API consumer.
>>
>> I will add more comments. When this flag is set VMStateInfo.get/put will
>> be used in vmstate_load/save_state instead of recursive call. And the
>> user should implement VMStateInfo.get/put to handle the concerned data
>> structure.
>>> Also, why do you need this flag at all? The only change I can see is
>>> that you pass additional information to VMStateInfo.get() / .put(),
>>> using NULL if it's not set. Why don't you just always pass the
>>> additional information? If the additional information is not needed by
>>> get() / put() the parameter will be unused anyway.
>> You can do it without creating this flag. Instead just to check if info
>> is set in the field. However I think it is more readable and more robust
>> to check this flag in vmstate_load/get_state.
> 
> Apologies for not getting around to this sooner; I was on holiday when you first
> sent it.
> 
> But:
>   a) Is there a reason to use the 'bool' between each element; can't you count the
>      length of the queue, send the length and then send the contents?  In that case
>      it should look a lot more like an ARRAY case on the wire.
>   b) I think you should really try and split it into two parts:
>     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
>        way of allocating/counting/reading the elements
>     2) A version of that which is for a QTAILQ.
>        It's important that whatever ends up on the migration stream doesn't reflect
>        that it happens to be implemented as a QTAILQ; so if you decide to change
>        it later the migration compatibility doesn't break.

There are certainly more than one way to do this. I have thought about
the way you suggested and decided not to do it that way. We either need
to track its size, which means new element in the structure containing
the queue, or go through the queue and count it. I don't want to add an
element unless it is necessary. Counting and dumping the queue will go
through the queue twice. Current solution only goes through the queue
once. Besides, the queue is recursive and current solution is a natural
one.
>   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
>   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
>      turn the tracing on on both sides :-)
>   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
>      I don't think I understand why you can't use the standard QTAILQ macros.
I agree with this one. Need to make the trace more specific.
>   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
Will check it.
> 
> Dave
> 
>>> Sascha
>>>
>>
>> Thanks,
>> Jianjun
>>
>>
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> 

Thanks,
Jianjun
Dr. David Alan Gilbert June 7, 2016, 4:46 p.m. UTC | #17
* Paolo Bonzini (pbonzini@redhat.com) wrote:
> 
> 
> On 07/06/2016 18:34, Dr. David Alan Gilbert wrote:
> > * Paolo Bonzini (pbonzini@redhat.com) wrote:
> >>
> >>
> >> On 07/06/2016 16:43, Dr. David Alan Gilbert wrote:
> >>>   b) I think you should really try and split it into two parts:
> >>>     1) A VMSTATE_ARRAY_CUSTOM (?) - so it's an array of elements but we've got a special
> >>>        way of allocating/counting/reading the elements
> >>>     2) A version of that which is for a QTAILQ.
> >>>        It's important that whatever ends up on the migration stream doesn't reflect
> >>>        that it happens to be implemented as a QTAILQ; so if you decide to change
> >>>        it later the migration compatibility doesn't break.
> >>
> >> (Just to clarify before Jianjun wakes up) a VMSTATE_ARRAY is just a
> >> sequence of elements.  The count, if needed as in a VARRAY, is stored
> >> earlier and separately.  Currently lists (including this QTAILQ) are
> >> usually represented in the migration stream as a sequence of elements
> >> preceded by "1" and terminated by a "0".  Would you like to change it to
> >> a count + sequence as well?
> >>
> >> This would prevent using the new QTAILQ support for other existing lists
> >> such as virtio-blk and scsi's request lists.
> > 
> > That depends if it's something you're trying to keep migration compatibility
> > with;  if you're trying to keep format compaibility then sure keep it as is.
> > If you're not trying to keep compatibility, then why can't virtio-blk or
> > scsi request lists also use a count rather than the markers?
> 
> We're trying to keep compatibility, and I think it's among the last bits
> that are resisting conversion to vmstate.  Which explains my interest in
> Jianjun's patches. :)

OK, fine - if you're trying to keep format compatibility then I agree.
Although I'm not entirely sure that things like virtio-blk, scsi and everything else
are consistent in their structure.

Dave

> 
> Paolo
> 
> >>>   c) Use new trace_ names for get_qtailq rather than misusing trace_vmstate_load_state*
> >>>   d) Add a trace_ for put_qtailq as well - that way when it goes horribly wrong we can
> >>>      turn the tracing on on both sides :-)
> >>>   e) Is there anyway to make QTAILQ_RAW_INSERT_TAIL any more readable?
> >>>      I don't think I understand why you can't use the standard QTAILQ macros.
> >>
> >> Because they assume a particular type. The "raw" version need to work on
> >> void*.
> > 
> > Ah OK.
> > 
> > Dave
> > 
> >>
> >> Thanks,
> >>
> >> Paolo
> >>
> >>>   f) You might need to fix up Amit's scripts/vmstate-static-checker.py
> > --
> > Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> > 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
diff mbox

Patch

diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 56a4171..da4ef7f 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -185,6 +185,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,
 };
 
 struct VMStateField {
@@ -245,6 +247,7 @@  extern const VMStateInfo vmstate_info_timer;
 extern const VMStateInfo vmstate_info_buffer;
 extern const VMStateInfo vmstate_info_unused_buffer;
 extern const VMStateInfo vmstate_info_bitmap;
+extern const VMStateInfo vmstate_info_qtailq;
 
 #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
 #define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
@@ -656,6 +659,25 @@  extern const VMStateInfo vmstate_info_bitmap;
     .offset       = offsetof(_state, _field),                        \
 }
 
+/* For QTAILQ that need customized handling
+ * _type: type of QTAILQ element
+ * _next: name of QTAILQ entry field in QTAILQ element
+ * _vmsd: VMSD for QTAILQ element
+ * size: size of QTAILQ element
+ * start: offset of QTAILQ entry in QTAILQ element
+ */
+#define VMSTATE_QTAILQ_V(_field, _state, _version, _vmsd, _type, _next)  \
+{                                                                        \
+    .name         = (stringify(_field)),                                 \
+    .version_id   = (_version),                                          \
+    .vmsd         = &(_vmsd),                                            \
+    .size         = sizeof(_type),                                       \
+    .info         = &vmstate_info_qtailq,                                \
+    .flags        = VMS_CSTM,                                            \
+    .offset       = offsetof(_state, _field),                            \
+    .start        = offsetof(_type, _next),                              \
+}
+
 /* _f : field name
    _f_n : num of elements field_name
    _n : num of elements
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index f781aa2..003e368 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -437,3 +437,35 @@  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 0
+#define QTAILQ_LAST_OFFSET (sizeof(void *))
+
+/*
+ * Offsets of layout of a tail queue element.
+ */
+#define QTAILQ_NEXT_OFFSET 0
+#define QTAILQ_PREV_OFFSET (sizeof(void *))
+
+/*
+ * Tail queue tranversal using pointer arithmetic.
+ */
+#define QTAILQ_RAW_FOREACH(elm, head, entry)                                   \
+        for ((elm) = *((void **) ((char *) (head) + QTAILQ_FIRST_OFFSET));     \
+             (elm);                                                            \
+             (elm) =                                                           \
+                 *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)))
+/*
+ * Tail queue insertion using pointer arithmetic.
+ */
+#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do {                          \
+        *((void **) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET)) = NULL;   \
+        *((void **) ((char *) (elm) + (entry) + QTAILQ_PREV_OFFSET)) =         \
+            *((void **) ((char *) (head) +QTAILQ_LAST_OFFSET));                \
+        **((void ***)((char *) (head) +QTAILQ_LAST_OFFSET)) = (elm);           \
+        *((void **) ((char *) (head) + QTAILQ_LAST_OFFSET)) =                  \
+            (void *) ((char *) (elm) + (entry) + QTAILQ_NEXT_OFFSET);          \
+} while (/*CONSTCOND*/0)
diff --git a/migration/vmstate.c b/migration/vmstate.c
index 644ba1f..ff56650 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -5,7 +5,9 @@ 
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
 #include "qemu/error-report.h"
+#include "qemu/queue.h"
 #include "trace.h"
+#include "migration/qjson.h"
 
 static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
                                     void *opaque, QJSON *vmdesc);
@@ -120,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->info->get(f, addr, size, field);
                 } else {
                     ret = field->info->get(f, addr, size, NULL);
 
@@ -192,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;
     }
@@ -326,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->info->put(f, addr, size, field, vmdesc_loop);
                 } else {
                     field->info->put(f, addr, size, NULL, NULL);
                 }
@@ -938,3 +946,74 @@  const VMStateInfo vmstate_info_bitmap = {
     .get = get_bitmap,
     .put = put_bitmap,
 };
+
+/*get for QTAILQ */
+static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
+                      VMStateField *field)
+{
+    bool link;
+    int ret = 0;
+    const VMStateDescription *vmsd = field->vmsd;
+    size_t size = field->size;
+    size_t entry = field->start;
+    int version_id = field->version_id;
+    void *elm;
+
+    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), NULL);
+        if (!link) {
+            break;
+        }
+
+        elm =  g_malloc(size);
+        ret = vmstate_load_state(f, vmsd, elm, version_id);
+        if (ret) {
+            return ret;
+        }
+        QTAILQ_RAW_INSERT_TAIL(pv, elm, entry);
+    }
+
+    trace_vmstate_load_state_end(vmsd->name, "end", ret);
+    return ret;
+}
+
+/* put for QTAILQ */
+static void put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
+                       VMStateField *field, QJSON *vmdesc)
+{
+    bool link = true;
+    const VMStateDescription *vmsd = field->vmsd;
+    size_t entry = field->start;
+    void *elm;
+
+    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, pv, entry) {
+        vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
+        vmstate_save_state(f, vmsd, elm, vmdesc);
+    }
+    link = false;
+    vmstate_info_bool.put(f, &link, sizeof(bool), NULL, NULL);
+    if (vmdesc) {
+        json_end_array(vmdesc);
+    }
+}
+const VMStateInfo vmstate_info_qtailq = {
+    .name = "qtailq",
+    .get  = get_qtailq,
+    .put  = put_qtailq,
+};