Patchwork [RESEND,v5,2/6] Introduce "save_devices"

login
register
mail settings
Submitter Stefano Stabellini
Date Feb. 28, 2012, 3:51 p.m.
Message ID <1330444295-8859-2-git-send-email-stefano.stabellini@eu.citrix.com>
Download mbox | patch
Permalink /patch/143483/
State New
Headers show

Comments

Stefano Stabellini - Feb. 28, 2012, 3:51 p.m.
- add an "is_ram" flag to SaveStateEntry;

- add an "is_ram" parameter to register_savevm_live;

- introduce a "save_devices" monitor command that can be used to save
the state of non-ram devices.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
---
 block-migration.c |    2 +-
 hmp-commands.hx   |   14 ++++++++++
 qmp-commands.hx   |   17 ++++++++++++
 savevm.c          |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 sysemu.h          |    1 +
 vl.c              |    2 +-
 vmstate.h         |    1 +
 7 files changed, 106 insertions(+), 3 deletions(-)
Anthony PERARD - Feb. 28, 2012, 6:58 p.m.
On 28/02/12 15:51, Stefano Stabellini wrote:
> - add an "is_ram" flag to SaveStateEntry;
>
> - add an "is_ram" parameter to register_savevm_live;
>
> - introduce a "save_devices" monitor command that can be used to save
> the state of non-ram devices.
>
> Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> ---
>   block-migration.c |    2 +-
>   hmp-commands.hx   |   14 ++++++++++
>   qmp-commands.hx   |   17 ++++++++++++
>   savevm.c          |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   sysemu.h          |    1 +
>   vl.c              |    2 +-
>   vmstate.h         |    1 +
>   7 files changed, 106 insertions(+), 3 deletions(-)
>
> diff --git a/block-migration.c b/block-migration.c
> index 4467468..d283fd0 100644
> --- a/block-migration.c
> +++ b/block-migration.c
> @@ -722,6 +722,6 @@ void blk_mig_init(void)
>       QSIMPLEQ_INIT(&block_mig_state.bmds_list);
>       QSIMPLEQ_INIT(&block_mig_state.blk_list);
>
> -    register_savevm_live(NULL, "block", 0, 1, block_set_params,
> +    register_savevm_live(NULL, "block", 0, 1, 0, block_set_params,
>                            block_save_live, NULL, block_load,&block_mig_state);
>   }
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 64b3656..873abc9 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -280,6 +280,20 @@ a snapshot with the same tag or ID, it is replaced. More info at
>   ETEXI
>
>       {
> +        .name       = "save_devices",
> +        .args_type  = "filename:F",
> +        .params     = "filename",
> +        .help       = "save the state of non-ram devices.",
> +        .mhandler.cmd_new = do_save_device_state,
> +    },
> +
> +STEXI
> +@item save_devices @var{filename}
> +@findex save_devices
> +Save the state of non-ram devices.
> +ETEXI
> +
> +    {
>           .name       = "loadvm",
>           .args_type  = "name:s",
>           .params     = "tag|id",
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 705f704..619d9de 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -444,6 +444,23 @@ Note: inject-nmi is only supported for x86 guest currently, it will
>   EQMP
>
>       {
> +        .name       = "save_devices",
> +        .args_type  = "filename:F",
> +        .params     = "filename",
> +        .help       = "save the state of non-ram devices.",
> +        .user_print = monitor_user_noop,	
> +    .mhandler.cmd_new = do_save_device_state,
> +    },
> +
> +SQMP
> +save_devices
> +-------
> +
> +Save the state of non-ram devices.
> +
> +EQMP
> +
> +    {
>           .name       = "migrate",
>           .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
>           .params     = "[-d] [-b] [-i] uri",
> diff --git a/savevm.c b/savevm.c
> index 80be1ff..d0e62bb 100644
> --- a/savevm.c
> +++ b/savevm.c
> @@ -1177,6 +1177,7 @@ typedef struct SaveStateEntry {
>       void *opaque;
>       CompatEntry *compat;
>       int no_migrate;
> +    int is_ram;
>   } SaveStateEntry;
>
>
> @@ -1223,6 +1224,7 @@ int register_savevm_live(DeviceState *dev,
>                            const char *idstr,
>                            int instance_id,
>                            int version_id,
> +                         int is_ram,
>                            SaveSetParamsHandler *set_params,
>                            SaveLiveStateHandler *save_live_state,
>                            SaveStateHandler *save_state,
> @@ -1241,6 +1243,7 @@ int register_savevm_live(DeviceState *dev,
>       se->opaque = opaque;
>       se->vmsd = NULL;
>       se->no_migrate = 0;
> +    se->is_ram = is_ram;
>
>       if (dev&&  dev->parent_bus&&  dev->parent_bus->info->get_dev_path) {
>           char *id = dev->parent_bus->info->get_dev_path(dev);
> @@ -1277,7 +1280,7 @@ int register_savevm(DeviceState *dev,
>                       LoadStateHandler *load_state,
>                       void *opaque)
>   {
> -    return register_savevm_live(dev, idstr, instance_id, version_id,
> +    return register_savevm_live(dev, idstr, instance_id, version_id, 0,
>                                   NULL, NULL, save_state, load_state, opaque);
>   }
>
> @@ -1728,6 +1731,43 @@ out:
>       return ret;
>   }
>
> +static int qemu_save_device_state(Monitor *mon, QEMUFile *f)
> +{
> +    SaveStateEntry *se;
> +
> +    qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
> +    qemu_put_be32(f, QEMU_VM_FILE_VERSION);
> +
> +    cpu_synchronize_all_states();
> +
> +    QTAILQ_FOREACH(se,&savevm_handlers, entry) {
> +        int len;
> +
> +        if (se->is_ram)
> +            continue;
> +        if (se->save_state == NULL&&  se->vmsd == NULL)
> +            continue;
> +
> +        /* Section type */
> +        qemu_put_byte(f, QEMU_VM_SECTION_FULL);
> +        qemu_put_be32(f, se->section_id);
> +
> +        /* ID string */
> +        len = strlen(se->idstr);
> +        qemu_put_byte(f, len);
> +        qemu_put_buffer(f, (uint8_t *)se->idstr, len);
> +
> +        qemu_put_be32(f, se->instance_id);
> +        qemu_put_be32(f, se->version_id);
> +
> +        vmstate_save(f, se);
> +    }
> +
> +    qemu_put_byte(f, QEMU_VM_EOF);
> +
> +    return qemu_file_get_error(f);
> +}
> +
>   static SaveStateEntry *find_se(const char *idstr, int instance_id)
>   {
>       SaveStateEntry *se;
> @@ -2109,6 +2149,36 @@ void do_savevm(Monitor *mon, const QDict *qdict)
>           vm_start();
>   }
>
> +int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data)
> +{
> +    int ret;
> +    QEMUFile *f;
> +    int saved_vm_running;
> +    const char *filename = qdict_get_try_str(qdict, "filename");
> +
> +    saved_vm_running = runstate_is_running();
> +    vm_stop(RUN_STATE_SAVE_VM);
> +
> +    f = qemu_fopen(filename, "wb");
> +    if (!f) {
> +        monitor_printf(mon, "Could not open VM state file\n");
> +        ret = -1;
> +        goto the_end;
> +    }
> +    ret = qemu_save_device_state(mon, f);
> +    qemu_fclose(f);
> +    if (ret<  0) {
> +        monitor_printf(mon, "Error %d while writing VM\n", ret);
> +        goto the_end;
> +    }
> +    ret = 0;
> +
> + the_end:
> +    if (saved_vm_running)
> +        vm_start();
> +    return ret;
> +}
> +
>   int load_vmstate(const char *name)
>   {
>       BlockDriverState *bs, *bs_vm_state;
> diff --git a/sysemu.h b/sysemu.h
> index 98118cc..f4d5bf4 100644
> --- a/sysemu.h
> +++ b/sysemu.h
> @@ -70,6 +70,7 @@ void qemu_remove_exit_notifier(Notifier *notify);
>   void qemu_add_machine_init_done_notifier(Notifier *notify);
>
>   void do_savevm(Monitor *mon, const QDict *qdict);
> +int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data);
>   int load_vmstate(const char *name);
>   void do_delvm(Monitor *mon, const QDict *qdict);
>   void do_info_snapshots(Monitor *mon);
> diff --git a/vl.c b/vl.c
> index 1d4c350..5b2b84c 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3393,7 +3393,7 @@ int main(int argc, char **argv, char **envp)
>       default_drive(default_sdcard, snapshot, machine->use_scsi,
>                     IF_SD, 0, SD_OPTS);
>
> -    register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
> +    register_savevm_live(NULL, "ram", 0, 4, 1, NULL, ram_save_live, NULL,
>                            ram_load, NULL);
>
>       if (nb_numa_nodes>  0) {
> diff --git a/vmstate.h b/vmstate.h
> index 9d3c49c..3cef117 100644
> --- a/vmstate.h
> +++ b/vmstate.h
> @@ -44,6 +44,7 @@ int register_savevm_live(DeviceState *dev,
>                            const char *idstr,
>                            int instance_id,
>                            int version_id,
> +                         int is_ram,
>                            SaveSetParamsHandler *set_params,
>                            SaveLiveStateHandler *save_live_state,
>                            SaveStateHandler *save_state,

Acked-by: Anthony PERARD <anthony.perard@citrix.com>
Anthony Liguori - March 12, 2012, 6:33 p.m.
On 02/28/2012 09:51 AM, Stefano Stabellini wrote:
> - add an "is_ram" flag to SaveStateEntry;
>
> - add an "is_ram" parameter to register_savevm_live;
>
> - introduce a "save_devices" monitor command that can be used to save
> the state of non-ram devices.
>
> Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> ---
>   block-migration.c |    2 +-
>   hmp-commands.hx   |   14 ++++++++++
>   qmp-commands.hx   |   17 ++++++++++++
>   savevm.c          |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   sysemu.h          |    1 +
>   vl.c              |    2 +-
>   vmstate.h         |    1 +
>   7 files changed, 106 insertions(+), 3 deletions(-)
>
> diff --git a/block-migration.c b/block-migration.c
> index 4467468..d283fd0 100644
> --- a/block-migration.c
> +++ b/block-migration.c
> @@ -722,6 +722,6 @@ void blk_mig_init(void)
>       QSIMPLEQ_INIT(&block_mig_state.bmds_list);
>       QSIMPLEQ_INIT(&block_mig_state.blk_list);
>
> -    register_savevm_live(NULL, "block", 0, 1, block_set_params,
> +    register_savevm_live(NULL, "block", 0, 1, 0, block_set_params,
>                            block_save_live, NULL, block_load,&block_mig_state);

Do you really want the block live migration to be part of Xen?

If not, then you can simplify by just making register_savevm_live always imply 
!device and adjust register_savevm() accordingly.  Otherwise, the likelihood of 
a casual observer knowing what '0, 1, 0' means is pretty bad...

>   }
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 64b3656..873abc9 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -280,6 +280,20 @@ a snapshot with the same tag or ID, it is replaced. More info at
>   ETEXI
>
>       {
> +        .name       = "save_devices",
> +        .args_type  = "filename:F",
> +        .params     = "filename",
> +        .help       = "save the state of non-ram devices.",
> +        .mhandler.cmd_new = do_save_device_state,
> +    },
> +
> +STEXI
> +@item save_devices @var{filename}
> +@findex save_devices
> +Save the state of non-ram devices.
> +ETEXI
> +
> +    {
>           .name       = "loadvm",
>           .args_type  = "name:s",
>           .params     = "tag|id",
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 705f704..619d9de 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -444,6 +444,23 @@ Note: inject-nmi is only supported for x86 guest currently, it will
>   EQMP
>
>       {
> +        .name       = "save_devices",
> +        .args_type  = "filename:F",
> +        .params     = "filename",
> +        .help       = "save the state of non-ram devices.",
> +        .user_print = monitor_user_noop,	
> +    .mhandler.cmd_new = do_save_device_state,
> +    },
> +
> +SQMP
> +save_devices
> +-------
> +
> +Save the state of non-ram devices.
> +
> +EQMP
> +
> +    {
>           .name       = "migrate",
>           .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
>           .params     = "[-d] [-b] [-i] uri",


Should be QAPI commands and documented a great deal more (see other examples in 
qapi-schema.json).  Please CC Luiz too when adding new QMP commands.

> diff --git a/savevm.c b/savevm.c
> index 80be1ff..d0e62bb 100644
> --- a/savevm.c
> +++ b/savevm.c
> @@ -1177,6 +1177,7 @@ typedef struct SaveStateEntry {
>       void *opaque;
>       CompatEntry *compat;
>       int no_migrate;
> +    int is_ram;
>   } SaveStateEntry;
>
>
> @@ -1223,6 +1224,7 @@ int register_savevm_live(DeviceState *dev,
>                            const char *idstr,
>                            int instance_id,
>                            int version_id,
> +                         int is_ram,
>                            SaveSetParamsHandler *set_params,
>                            SaveLiveStateHandler *save_live_state,
>                            SaveStateHandler *save_state,
> @@ -1241,6 +1243,7 @@ int register_savevm_live(DeviceState *dev,
>       se->opaque = opaque;
>       se->vmsd = NULL;
>       se->no_migrate = 0;
> +    se->is_ram = is_ram;
>
>       if (dev&&  dev->parent_bus&&  dev->parent_bus->info->get_dev_path) {
>           char *id = dev->parent_bus->info->get_dev_path(dev);
> @@ -1277,7 +1280,7 @@ int register_savevm(DeviceState *dev,
>                       LoadStateHandler *load_state,
>                       void *opaque)
>   {
> -    return register_savevm_live(dev, idstr, instance_id, version_id,
> +    return register_savevm_live(dev, idstr, instance_id, version_id, 0,
>                                   NULL, NULL, save_state, load_state, opaque);
>   }
>
> @@ -1728,6 +1731,43 @@ out:
>       return ret;
>   }
>
> +static int qemu_save_device_state(Monitor *mon, QEMUFile *f)
> +{
> +    SaveStateEntry *se;
> +
> +    qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
> +    qemu_put_be32(f, QEMU_VM_FILE_VERSION);
> +
> +    cpu_synchronize_all_states();
> +
> +    QTAILQ_FOREACH(se,&savevm_handlers, entry) {
> +        int len;
> +
> +        if (se->is_ram)
> +            continue;

Violating CODING_STYLE.

> +        if (se->save_state == NULL&&  se->vmsd == NULL)
> +            continue;
> +
> +        /* Section type */
> +        qemu_put_byte(f, QEMU_VM_SECTION_FULL);
> +        qemu_put_be32(f, se->section_id);
> +
> +        /* ID string */
> +        len = strlen(se->idstr);
> +        qemu_put_byte(f, len);
> +        qemu_put_buffer(f, (uint8_t *)se->idstr, len);
> +
> +        qemu_put_be32(f, se->instance_id);
> +        qemu_put_be32(f, se->version_id);
> +
> +        vmstate_save(f, se);
> +    }
> +
> +    qemu_put_byte(f, QEMU_VM_EOF);
> +
> +    return qemu_file_get_error(f);
> +}

Please add something to docs/ documenting this format.

> +
>   static SaveStateEntry *find_se(const char *idstr, int instance_id)
>   {
>       SaveStateEntry *se;
> @@ -2109,6 +2149,36 @@ void do_savevm(Monitor *mon, const QDict *qdict)
>           vm_start();
>   }
>
> +int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data)
> +{
> +    int ret;
> +    QEMUFile *f;
> +    int saved_vm_running;
> +    const char *filename = qdict_get_try_str(qdict, "filename");
> +
> +    saved_vm_running = runstate_is_running();
> +    vm_stop(RUN_STATE_SAVE_VM);
> +
> +    f = qemu_fopen(filename, "wb");
> +    if (!f) {
> +        monitor_printf(mon, "Could not open VM state file\n");
> +        ret = -1;
> +        goto the_end;
> +    }
> +    ret = qemu_save_device_state(mon, f);
> +    qemu_fclose(f);
> +    if (ret<  0) {
> +        monitor_printf(mon, "Error %d while writing VM\n", ret);
> +        goto the_end;
> +    }
> +    ret = 0;
> +
> + the_end:
> +    if (saved_vm_running)
> +        vm_start();
> +    return ret;
> +}
> +

Would it be useful/interesting to return a binary blob via QMP instead of 
writing to a file?

Regards,

Anthony Liguori

>   int load_vmstate(const char *name)
>   {
>       BlockDriverState *bs, *bs_vm_state;
> diff --git a/sysemu.h b/sysemu.h
> index 98118cc..f4d5bf4 100644
> --- a/sysemu.h
> +++ b/sysemu.h
> @@ -70,6 +70,7 @@ void qemu_remove_exit_notifier(Notifier *notify);
>   void qemu_add_machine_init_done_notifier(Notifier *notify);
>
>   void do_savevm(Monitor *mon, const QDict *qdict);
> +int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data);
>   int load_vmstate(const char *name);
>   void do_delvm(Monitor *mon, const QDict *qdict);
>   void do_info_snapshots(Monitor *mon);
> diff --git a/vl.c b/vl.c
> index 1d4c350..5b2b84c 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3393,7 +3393,7 @@ int main(int argc, char **argv, char **envp)
>       default_drive(default_sdcard, snapshot, machine->use_scsi,
>                     IF_SD, 0, SD_OPTS);
>
> -    register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
> +    register_savevm_live(NULL, "ram", 0, 4, 1, NULL, ram_save_live, NULL,
>                            ram_load, NULL);
>
>       if (nb_numa_nodes>  0) {
> diff --git a/vmstate.h b/vmstate.h
> index 9d3c49c..3cef117 100644
> --- a/vmstate.h
> +++ b/vmstate.h
> @@ -44,6 +44,7 @@ int register_savevm_live(DeviceState *dev,
>                            const char *idstr,
>                            int instance_id,
>                            int version_id,
> +                         int is_ram,
>                            SaveSetParamsHandler *set_params,
>                            SaveLiveStateHandler *save_live_state,
>                            SaveStateHandler *save_state,
Stefano Stabellini - March 13, 2012, 11:51 a.m.
On Mon, 12 Mar 2012, Anthony Liguori wrote:
> On 02/28/2012 09:51 AM, Stefano Stabellini wrote:
> > - add an "is_ram" flag to SaveStateEntry;
> >
> > - add an "is_ram" parameter to register_savevm_live;
> >
> > - introduce a "save_devices" monitor command that can be used to save
> > the state of non-ram devices.
> >
> > Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> > ---
> >   block-migration.c |    2 +-
> >   hmp-commands.hx   |   14 ++++++++++
> >   qmp-commands.hx   |   17 ++++++++++++
> >   savevm.c          |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
> >   sysemu.h          |    1 +
> >   vl.c              |    2 +-
> >   vmstate.h         |    1 +
> >   7 files changed, 106 insertions(+), 3 deletions(-)
> >
> > diff --git a/block-migration.c b/block-migration.c
> > index 4467468..d283fd0 100644
> > --- a/block-migration.c
> > +++ b/block-migration.c
> > @@ -722,6 +722,6 @@ void blk_mig_init(void)
> >       QSIMPLEQ_INIT(&block_mig_state.bmds_list);
> >       QSIMPLEQ_INIT(&block_mig_state.blk_list);
> >
> > -    register_savevm_live(NULL, "block", 0, 1, block_set_params,
> > +    register_savevm_live(NULL, "block", 0, 1, 0, block_set_params,
> >                            block_save_live, NULL, block_load,&block_mig_state);
> 
> Do you really want the block live migration to be part of Xen?
> 
> If not, then you can simplify by just making register_savevm_live always imply 
> !device and adjust register_savevm() accordingly.  Otherwise, the likelihood of 
> a casual observer knowing what '0, 1, 0' means is pretty bad...

That's a good point, I'll do that.


> >   }
> > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > index 64b3656..873abc9 100644
> > --- a/hmp-commands.hx
> > +++ b/hmp-commands.hx
> > @@ -280,6 +280,20 @@ a snapshot with the same tag or ID, it is replaced. More info at
> >   ETEXI
> >
> >       {
> > +        .name       = "save_devices",
> > +        .args_type  = "filename:F",
> > +        .params     = "filename",
> > +        .help       = "save the state of non-ram devices.",
> > +        .mhandler.cmd_new = do_save_device_state,
> > +    },
> > +
> > +STEXI
> > +@item save_devices @var{filename}
> > +@findex save_devices
> > +Save the state of non-ram devices.
> > +ETEXI
> > +
> > +    {
> >           .name       = "loadvm",
> >           .args_type  = "name:s",
> >           .params     = "tag|id",
> > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > index 705f704..619d9de 100644
> > --- a/qmp-commands.hx
> > +++ b/qmp-commands.hx
> > @@ -444,6 +444,23 @@ Note: inject-nmi is only supported for x86 guest currently, it will
> >   EQMP
> >
> >       {
> > +        .name       = "save_devices",
> > +        .args_type  = "filename:F",
> > +        .params     = "filename",
> > +        .help       = "save the state of non-ram devices.",
> > +        .user_print = monitor_user_noop,	
> > +    .mhandler.cmd_new = do_save_device_state,
> > +    },
> > +
> > +SQMP
> > +save_devices
> > +-------
> > +
> > +Save the state of non-ram devices.
> > +
> > +EQMP
> > +
> > +    {
> >           .name       = "migrate",
> >           .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
> >           .params     = "[-d] [-b] [-i] uri",
> 
> 
> Should be QAPI commands and documented a great deal more (see other examples in 
> qapi-schema.json).  Please CC Luiz too when adding new QMP commands.

OK, I'll do.


> > diff --git a/savevm.c b/savevm.c
> > index 80be1ff..d0e62bb 100644
> > --- a/savevm.c
> > +++ b/savevm.c
> > @@ -1177,6 +1177,7 @@ typedef struct SaveStateEntry {
> >       void *opaque;
> >       CompatEntry *compat;
> >       int no_migrate;
> > +    int is_ram;
> >   } SaveStateEntry;
> >
> >
> > @@ -1223,6 +1224,7 @@ int register_savevm_live(DeviceState *dev,
> >                            const char *idstr,
> >                            int instance_id,
> >                            int version_id,
> > +                         int is_ram,
> >                            SaveSetParamsHandler *set_params,
> >                            SaveLiveStateHandler *save_live_state,
> >                            SaveStateHandler *save_state,
> > @@ -1241,6 +1243,7 @@ int register_savevm_live(DeviceState *dev,
> >       se->opaque = opaque;
> >       se->vmsd = NULL;
> >       se->no_migrate = 0;
> > +    se->is_ram = is_ram;
> >
> >       if (dev&&  dev->parent_bus&&  dev->parent_bus->info->get_dev_path) {
> >           char *id = dev->parent_bus->info->get_dev_path(dev);
> > @@ -1277,7 +1280,7 @@ int register_savevm(DeviceState *dev,
> >                       LoadStateHandler *load_state,
> >                       void *opaque)
> >   {
> > -    return register_savevm_live(dev, idstr, instance_id, version_id,
> > +    return register_savevm_live(dev, idstr, instance_id, version_id, 0,
> >                                   NULL, NULL, save_state, load_state, opaque);
> >   }
> >
> > @@ -1728,6 +1731,43 @@ out:
> >       return ret;
> >   }
> >
> > +static int qemu_save_device_state(Monitor *mon, QEMUFile *f)
> > +{
> > +    SaveStateEntry *se;
> > +
> > +    qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
> > +    qemu_put_be32(f, QEMU_VM_FILE_VERSION);
> > +
> > +    cpu_synchronize_all_states();
> > +
> > +    QTAILQ_FOREACH(se,&savevm_handlers, entry) {
> > +        int len;
> > +
> > +        if (se->is_ram)
> > +            continue;
> 
> Violating CODING_STYLE.

I'll fix that.


> > +        if (se->save_state == NULL&&  se->vmsd == NULL)
> > +            continue;
> > +
> > +        /* Section type */
> > +        qemu_put_byte(f, QEMU_VM_SECTION_FULL);
> > +        qemu_put_be32(f, se->section_id);
> > +
> > +        /* ID string */
> > +        len = strlen(se->idstr);
> > +        qemu_put_byte(f, len);
> > +        qemu_put_buffer(f, (uint8_t *)se->idstr, len);
> > +
> > +        qemu_put_be32(f, se->instance_id);
> > +        qemu_put_be32(f, se->version_id);
> > +
> > +        vmstate_save(f, se);
> > +    }
> > +
> > +    qemu_put_byte(f, QEMU_VM_EOF);
> > +
> > +    return qemu_file_get_error(f);
> > +}
> 
> Please add something to docs/ documenting this format.

OK


> > +
> >   static SaveStateEntry *find_se(const char *idstr, int instance_id)
> >   {
> >       SaveStateEntry *se;
> > @@ -2109,6 +2149,36 @@ void do_savevm(Monitor *mon, const QDict *qdict)
> >           vm_start();
> >   }
> >
> > +int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data)
> > +{
> > +    int ret;
> > +    QEMUFile *f;
> > +    int saved_vm_running;
> > +    const char *filename = qdict_get_try_str(qdict, "filename");
> > +
> > +    saved_vm_running = runstate_is_running();
> > +    vm_stop(RUN_STATE_SAVE_VM);
> > +
> > +    f = qemu_fopen(filename, "wb");
> > +    if (!f) {
> > +        monitor_printf(mon, "Could not open VM state file\n");
> > +        ret = -1;
> > +        goto the_end;
> > +    }
> > +    ret = qemu_save_device_state(mon, f);
> > +    qemu_fclose(f);
> > +    if (ret<  0) {
> > +        monitor_printf(mon, "Error %d while writing VM\n", ret);
> > +        goto the_end;
> > +    }
> > +    ret = 0;
> > +
> > + the_end:
> > +    if (saved_vm_running)
> > +        vm_start();
> > +    return ret;
> > +}
> > +
> 
> Would it be useful/interesting to return a binary blob via QMP instead of 
> writing to a file?

Yes, it is potentially useful. I have just gone with the file interface
because it is easier to handle but I could change it or maybe have QMP
return the binary as an alternative option.
Luiz Capitulino - March 13, 2012, 7:26 p.m.
On Tue, 13 Mar 2012 11:51:56 +0000
Stefano Stabellini <stefano.stabellini@eu.citrix.com> wrote:

> > Should be QAPI commands and documented a great deal more (see other examples in 
> > qapi-schema.json).  Please CC Luiz too when adding new QMP commands.
> 
> OK, I'll do.

I reviewed the RunState patch but missed that one... Btw Stefano, there's a
tutorial for new QMP commands: docs/writing-qmp-commands.txt.

Patch

diff --git a/block-migration.c b/block-migration.c
index 4467468..d283fd0 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -722,6 +722,6 @@  void blk_mig_init(void)
     QSIMPLEQ_INIT(&block_mig_state.bmds_list);
     QSIMPLEQ_INIT(&block_mig_state.blk_list);
 
-    register_savevm_live(NULL, "block", 0, 1, block_set_params,
+    register_savevm_live(NULL, "block", 0, 1, 0, block_set_params,
                          block_save_live, NULL, block_load, &block_mig_state);
 }
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 64b3656..873abc9 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -280,6 +280,20 @@  a snapshot with the same tag or ID, it is replaced. More info at
 ETEXI
 
     {
+        .name       = "save_devices",
+        .args_type  = "filename:F",
+        .params     = "filename",
+        .help       = "save the state of non-ram devices.",
+        .mhandler.cmd_new = do_save_device_state,
+    },
+
+STEXI
+@item save_devices @var{filename}
+@findex save_devices
+Save the state of non-ram devices.
+ETEXI
+
+    {
         .name       = "loadvm",
         .args_type  = "name:s",
         .params     = "tag|id",
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 705f704..619d9de 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -444,6 +444,23 @@  Note: inject-nmi is only supported for x86 guest currently, it will
 EQMP
 
     {
+        .name       = "save_devices",
+        .args_type  = "filename:F",
+        .params     = "filename",
+        .help       = "save the state of non-ram devices.",
+        .user_print = monitor_user_noop,	
+    .mhandler.cmd_new = do_save_device_state,
+    },
+
+SQMP
+save_devices
+-------
+
+Save the state of non-ram devices.
+
+EQMP
+
+    {
         .name       = "migrate",
         .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
         .params     = "[-d] [-b] [-i] uri",
diff --git a/savevm.c b/savevm.c
index 80be1ff..d0e62bb 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1177,6 +1177,7 @@  typedef struct SaveStateEntry {
     void *opaque;
     CompatEntry *compat;
     int no_migrate;
+    int is_ram;
 } SaveStateEntry;
 
 
@@ -1223,6 +1224,7 @@  int register_savevm_live(DeviceState *dev,
                          const char *idstr,
                          int instance_id,
                          int version_id,
+                         int is_ram,
                          SaveSetParamsHandler *set_params,
                          SaveLiveStateHandler *save_live_state,
                          SaveStateHandler *save_state,
@@ -1241,6 +1243,7 @@  int register_savevm_live(DeviceState *dev,
     se->opaque = opaque;
     se->vmsd = NULL;
     se->no_migrate = 0;
+    se->is_ram = is_ram;
 
     if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
         char *id = dev->parent_bus->info->get_dev_path(dev);
@@ -1277,7 +1280,7 @@  int register_savevm(DeviceState *dev,
                     LoadStateHandler *load_state,
                     void *opaque)
 {
-    return register_savevm_live(dev, idstr, instance_id, version_id,
+    return register_savevm_live(dev, idstr, instance_id, version_id, 0,
                                 NULL, NULL, save_state, load_state, opaque);
 }
 
@@ -1728,6 +1731,43 @@  out:
     return ret;
 }
 
+static int qemu_save_device_state(Monitor *mon, QEMUFile *f)
+{
+    SaveStateEntry *se;
+
+    qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+    qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+    cpu_synchronize_all_states();
+
+    QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+        int len;
+
+        if (se->is_ram)
+            continue;
+        if (se->save_state == NULL && se->vmsd == NULL)
+            continue;
+
+        /* Section type */
+        qemu_put_byte(f, QEMU_VM_SECTION_FULL);
+        qemu_put_be32(f, se->section_id);
+
+        /* ID string */
+        len = strlen(se->idstr);
+        qemu_put_byte(f, len);
+        qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+        qemu_put_be32(f, se->instance_id);
+        qemu_put_be32(f, se->version_id);
+
+        vmstate_save(f, se);
+    }
+
+    qemu_put_byte(f, QEMU_VM_EOF);
+
+    return qemu_file_get_error(f);
+}
+
 static SaveStateEntry *find_se(const char *idstr, int instance_id)
 {
     SaveStateEntry *se;
@@ -2109,6 +2149,36 @@  void do_savevm(Monitor *mon, const QDict *qdict)
         vm_start();
 }
 
+int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    int ret;
+    QEMUFile *f;
+    int saved_vm_running;
+    const char *filename = qdict_get_try_str(qdict, "filename");
+
+    saved_vm_running = runstate_is_running();
+    vm_stop(RUN_STATE_SAVE_VM);
+
+    f = qemu_fopen(filename, "wb");
+    if (!f) {
+        monitor_printf(mon, "Could not open VM state file\n");
+        ret = -1;
+        goto the_end;
+    }
+    ret = qemu_save_device_state(mon, f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        monitor_printf(mon, "Error %d while writing VM\n", ret);
+        goto the_end;
+    }
+    ret = 0;
+
+ the_end:
+    if (saved_vm_running)
+        vm_start();
+    return ret;
+}
+
 int load_vmstate(const char *name)
 {
     BlockDriverState *bs, *bs_vm_state;
diff --git a/sysemu.h b/sysemu.h
index 98118cc..f4d5bf4 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -70,6 +70,7 @@  void qemu_remove_exit_notifier(Notifier *notify);
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 
 void do_savevm(Monitor *mon, const QDict *qdict);
+int do_save_device_state(Monitor *mon, const QDict *qdict, QObject **ret_data);
 int load_vmstate(const char *name);
 void do_delvm(Monitor *mon, const QDict *qdict);
 void do_info_snapshots(Monitor *mon);
diff --git a/vl.c b/vl.c
index 1d4c350..5b2b84c 100644
--- a/vl.c
+++ b/vl.c
@@ -3393,7 +3393,7 @@  int main(int argc, char **argv, char **envp)
     default_drive(default_sdcard, snapshot, machine->use_scsi,
                   IF_SD, 0, SD_OPTS);
 
-    register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
+    register_savevm_live(NULL, "ram", 0, 4, 1, NULL, ram_save_live, NULL,
                          ram_load, NULL);
 
     if (nb_numa_nodes > 0) {
diff --git a/vmstate.h b/vmstate.h
index 9d3c49c..3cef117 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -44,6 +44,7 @@  int register_savevm_live(DeviceState *dev,
                          const char *idstr,
                          int instance_id,
                          int version_id,
+                         int is_ram,
                          SaveSetParamsHandler *set_params,
                          SaveLiveStateHandler *save_live_state,
                          SaveStateHandler *save_state,