Patchwork [v6,2/6] Introduce "save_devices"

login
register
mail settings
Submitter Stefano Stabellini
Date March 15, 2012, 6:19 p.m.
Message ID <1331835548-537-2-git-send-email-stefano.stabellini@eu.citrix.com>
Download mbox | patch
Permalink /patch/147059/
State New
Headers show

Comments

Stefano Stabellini - March 15, 2012, 6:19 p.m.
- add an "is_ram" flag to SaveStateEntry;

- register_savevm_live sets is_ram for live_savevm devices;

- introduce a "save_devices" QAPI command that can be used to save
the state of all devices, but not the RAM or the block devices of the
VM.

Changes in v6:

- remove the is_ram parameter from register_savevm_live and sets is_ram
if the device is a live_savevm device;

- introduce save_devices as a QAPI command, write a better description
for it;

- fix CODING_STYLE;

- introduce a new doc to explain the save format used by save_devices.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
CC: Anthony Liguori <anthony@codemonkey.ws>
CC: Luiz Capitulino <lcapitulino@redhat.com>
---
 docs/save_devices.txt |   33 ++++++++++++++++++++++
 qapi-schema.json      |   18 ++++++++++++
 qmp-commands.hx       |   25 +++++++++++++++++
 savevm.c              |   71 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 147 insertions(+), 0 deletions(-)
 create mode 100644 docs/save_devices.txt
Anthony Liguori - March 15, 2012, 8:16 p.m.
On 03/15/2012 01:19 PM, Stefano Stabellini wrote:
> - add an "is_ram" flag to SaveStateEntry;
>
> - register_savevm_live sets is_ram for live_savevm devices;
>
> - introduce a "save_devices" QAPI command that can be used to save
> the state of all devices, but not the RAM or the block devices of the
> VM.
>
> Changes in v6:
>
> - remove the is_ram parameter from register_savevm_live and sets is_ram
> if the device is a live_savevm device;
>
> - introduce save_devices as a QAPI command, write a better description
> for it;
>
> - fix CODING_STYLE;
>
> - introduce a new doc to explain the save format used by save_devices.
>
> Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> CC: Anthony Liguori<anthony@codemonkey.ws>
> CC: Luiz Capitulino<lcapitulino@redhat.com>
> ---
>   docs/save_devices.txt |   33 ++++++++++++++++++++++
>   qapi-schema.json      |   18 ++++++++++++
>   qmp-commands.hx       |   25 +++++++++++++++++
>   savevm.c              |   71 +++++++++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 147 insertions(+), 0 deletions(-)
>   create mode 100644 docs/save_devices.txt
>
> diff --git a/docs/save_devices.txt b/docs/save_devices.txt
> new file mode 100644
> index 0000000..79915d2
> --- /dev/null
> +++ b/docs/save_devices.txt
> @@ -0,0 +1,33 @@
> += Save Devices =
> +
> +QEMU has code to load/save the state of the guest that it is running.
> +These are two complementary operations.  Saving the state just does
> +that, saves the state for each device that the guest is running.
> +
> +These operations are normally used with migration (see migration.txt),
> +however it is also possible to save the state of all devices to file,
> +without saving the RAM or the block devices of the VM.
> +
> +This operation is called "save_devices" (see QMP/qmp-commands.txt).
> +
> +
> +The binary format used in the file is the following:
> +
> +
> +-------------------------------------------
> +
> +32 bit big endian: QEMU_VM_FILE_MAGIC
> +32 bit big endian: QEMU_VM_FILE_VERSION
> +
> +for_each_device
> +{
> +    8 bit:              QEMU_VM_SECTION_FULL
> +    32 bit big endian:  section_id
> +    8 bit:              idstr (ID string) length
> +    string:             idstr (ID string)
> +    32 bit big endian:  instance_id
> +    32 bit big endian:  version_id
> +    buffer:             device specific data
> +}
> +
> +8 bit: QEMU_VM_EOF
> diff --git a/qapi-schema.json b/qapi-schema.json
> index d0b6792..7f938ff 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1593,3 +1593,21 @@
>   { 'command': 'qom-list-types',
>     'data': { '*implements': 'str', '*abstract': 'bool' },
>     'returns': [ 'ObjectTypeInfo' ] }
> +
> +##
> +# @save_devices:
> +#
> +# Save the state of all devices to file. The RAM and the block devices
> +# of the VM are not saved by this command.
> +#
> +# @filename: the file to save the state of the devices to as binary
> +# data. See save_devices.txt for a description of the binary format.
> +#
> +# Returns: Nothing on success
> +#          If @filename cannot be opened, OpenFileFailed
> +#          If an I/O error occurs while writing the file, IOError
> +#
> +# Since: 1.0

Since: 1.1.

Otherwise Reviewed-by: Anthony Liguori <aliguori@us.ibm.com>

Regards,

Anthony Liguori


> +##
> +{ 'command': 'save_devices', 'data': {'filename': 'str'} }
> +
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 705f704..064d2a4 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -444,6 +444,31 @@ Note: inject-nmi is only supported for x86 guest currently, it will
>   EQMP
>
>       {
> +        .name       = "save_devices",
> +        .args_type  = "filename:F",
> +    .mhandler.cmd_new = qmp_marshal_input_save_devices,
> +    },
> +
> +SQMP
> +save_devices
> +-------
> +
> +Save the state of all devices to file. The RAM and the block devices
> +of the VM are not saved by this command.
> +
> +Arguments:
> +
> +- "filename": the file to save the state of the devices to as binary
> +data. See save_devices.txt for a description of the binary format.
> +
> +Example:
> +
> +->  { "execute": "save_devices", "arguments": { "filename": "/tmp/save" } }
> +<- { "return": {} }
> +
> +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..148356a 100644
> --- a/savevm.c
> +++ b/savevm.c
> @@ -84,6 +84,7 @@
>   #include "qemu-timer.h"
>   #include "cpus.h"
>   #include "memory.h"
> +#include "qmp-commands.h"
>
>   #define SELF_ANNOUNCE_ROUNDS 5
>
> @@ -1177,6 +1178,7 @@ typedef struct SaveStateEntry {
>       void *opaque;
>       CompatEntry *compat;
>       int no_migrate;
> +    int is_ram;
>   } SaveStateEntry;
>
>
> @@ -1241,6 +1243,10 @@ int register_savevm_live(DeviceState *dev,
>       se->opaque = opaque;
>       se->vmsd = NULL;
>       se->no_migrate = 0;
> +    /* if this is a live_savem then set is_ram */
> +    if (save_live_state != NULL) {
> +        se->is_ram = 1;
> +    }
>
>       if (dev&&  dev->parent_bus&&  dev->parent_bus->info->get_dev_path) {
>           char *id = dev->parent_bus->info->get_dev_path(dev);
> @@ -1728,6 +1734,45 @@ out:
>       return ret;
>   }
>
> +static int qemu_save_device_state(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 +2154,32 @@ void do_savevm(Monitor *mon, const QDict *qdict)
>           vm_start();
>   }
>
> +void qmp_save_devices(const char *filename, Error **errp)
> +{
> +    QEMUFile *f;
> +    int saved_vm_running;
> +    int ret;
> +
> +    saved_vm_running = runstate_is_running();
> +    vm_stop(RUN_STATE_SAVE_VM);
> +
> +    f = qemu_fopen(filename, "wb");
> +    if (!f) {
> +        error_set(errp, QERR_OPEN_FILE_FAILED, filename);
> +        goto the_end;
> +    }
> +    ret = qemu_save_device_state(f);
> +    qemu_fclose(f);
> +    if (ret<  0) {
> +        error_set(errp, QERR_IO_ERROR);
> +    }
> +
> + the_end:
> +    if (saved_vm_running)
> +        vm_start();
> +    return;
> +}
> +
>   int load_vmstate(const char *name)
>   {
>       BlockDriverState *bs, *bs_vm_state;
Luiz Capitulino - March 15, 2012, 8:34 p.m.
On Thu, 15 Mar 2012 15:16:15 -0500
Anthony Liguori <anthony@codemonkey.ws> wrote:

> On 03/15/2012 01:19 PM, Stefano Stabellini wrote:
> > - add an "is_ram" flag to SaveStateEntry;
> >
> > - register_savevm_live sets is_ram for live_savevm devices;
> >
> > - introduce a "save_devices" QAPI command that can be used to save
> > the state of all devices, but not the RAM or the block devices of the
> > VM.
> >
> > Changes in v6:
> >
> > - remove the is_ram parameter from register_savevm_live and sets is_ram
> > if the device is a live_savevm device;
> >
> > - introduce save_devices as a QAPI command, write a better description
> > for it;
> >
> > - fix CODING_STYLE;
> >
> > - introduce a new doc to explain the save format used by save_devices.
> >
> > Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> > CC: Anthony Liguori<anthony@codemonkey.ws>
> > CC: Luiz Capitulino<lcapitulino@redhat.com>
> > ---
> >   docs/save_devices.txt |   33 ++++++++++++++++++++++
> >   qapi-schema.json      |   18 ++++++++++++
> >   qmp-commands.hx       |   25 +++++++++++++++++
> >   savevm.c              |   71 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   4 files changed, 147 insertions(+), 0 deletions(-)
> >   create mode 100644 docs/save_devices.txt
> >
> > diff --git a/docs/save_devices.txt b/docs/save_devices.txt
> > new file mode 100644
> > index 0000000..79915d2
> > --- /dev/null
> > +++ b/docs/save_devices.txt
> > @@ -0,0 +1,33 @@
> > += Save Devices =
> > +
> > +QEMU has code to load/save the state of the guest that it is running.
> > +These are two complementary operations.  Saving the state just does
> > +that, saves the state for each device that the guest is running.
> > +
> > +These operations are normally used with migration (see migration.txt),
> > +however it is also possible to save the state of all devices to file,
> > +without saving the RAM or the block devices of the VM.
> > +
> > +This operation is called "save_devices" (see QMP/qmp-commands.txt).
> > +
> > +
> > +The binary format used in the file is the following:
> > +
> > +
> > +-------------------------------------------
> > +
> > +32 bit big endian: QEMU_VM_FILE_MAGIC
> > +32 bit big endian: QEMU_VM_FILE_VERSION
> > +
> > +for_each_device
> > +{
> > +    8 bit:              QEMU_VM_SECTION_FULL
> > +    32 bit big endian:  section_id
> > +    8 bit:              idstr (ID string) length
> > +    string:             idstr (ID string)
> > +    32 bit big endian:  instance_id
> > +    32 bit big endian:  version_id
> > +    buffer:             device specific data
> > +}
> > +
> > +8 bit: QEMU_VM_EOF
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index d0b6792..7f938ff 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -1593,3 +1593,21 @@
> >   { 'command': 'qom-list-types',
> >     'data': { '*implements': 'str', '*abstract': 'bool' },
> >     'returns': [ 'ObjectTypeInfo' ] }
> > +
> > +##
> > +# @save_devices:
> > +#
> > +# Save the state of all devices to file. The RAM and the block devices
> > +# of the VM are not saved by this command.
> > +#
> > +# @filename: the file to save the state of the devices to as binary
> > +# data. See save_devices.txt for a description of the binary format.
> > +#
> > +# Returns: Nothing on success
> > +#          If @filename cannot be opened, OpenFileFailed
> > +#          If an I/O error occurs while writing the file, IOError
> > +#
> > +# Since: 1.0
> 
> Since: 1.1.
> 
> Otherwise Reviewed-by: Anthony Liguori <aliguori@us.ibm.com>

Looks fine to me FWIW, my only nitpick is that we use an hyphen instead of the
underline in qmp command names, but I'd call this save-devices-state.

Don't you want this in HMP too, btw?

> 
> Regards,
> 
> Anthony Liguori
> 
> 
> > +##
> > +{ 'command': 'save_devices', 'data': {'filename': 'str'} }
> > +
> > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > index 705f704..064d2a4 100644
> > --- a/qmp-commands.hx
> > +++ b/qmp-commands.hx
> > @@ -444,6 +444,31 @@ Note: inject-nmi is only supported for x86 guest currently, it will
> >   EQMP
> >
> >       {
> > +        .name       = "save_devices",
> > +        .args_type  = "filename:F",
> > +    .mhandler.cmd_new = qmp_marshal_input_save_devices,
> > +    },
> > +
> > +SQMP
> > +save_devices
> > +-------
> > +
> > +Save the state of all devices to file. The RAM and the block devices
> > +of the VM are not saved by this command.
> > +
> > +Arguments:
> > +
> > +- "filename": the file to save the state of the devices to as binary
> > +data. See save_devices.txt for a description of the binary format.
> > +
> > +Example:
> > +
> > +->  { "execute": "save_devices", "arguments": { "filename": "/tmp/save" } }
> > +<- { "return": {} }
> > +
> > +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..148356a 100644
> > --- a/savevm.c
> > +++ b/savevm.c
> > @@ -84,6 +84,7 @@
> >   #include "qemu-timer.h"
> >   #include "cpus.h"
> >   #include "memory.h"
> > +#include "qmp-commands.h"
> >
> >   #define SELF_ANNOUNCE_ROUNDS 5
> >
> > @@ -1177,6 +1178,7 @@ typedef struct SaveStateEntry {
> >       void *opaque;
> >       CompatEntry *compat;
> >       int no_migrate;
> > +    int is_ram;
> >   } SaveStateEntry;
> >
> >
> > @@ -1241,6 +1243,10 @@ int register_savevm_live(DeviceState *dev,
> >       se->opaque = opaque;
> >       se->vmsd = NULL;
> >       se->no_migrate = 0;
> > +    /* if this is a live_savem then set is_ram */
> > +    if (save_live_state != NULL) {
> > +        se->is_ram = 1;
> > +    }
> >
> >       if (dev&&  dev->parent_bus&&  dev->parent_bus->info->get_dev_path) {
> >           char *id = dev->parent_bus->info->get_dev_path(dev);
> > @@ -1728,6 +1734,45 @@ out:
> >       return ret;
> >   }
> >
> > +static int qemu_save_device_state(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 +2154,32 @@ void do_savevm(Monitor *mon, const QDict *qdict)
> >           vm_start();
> >   }
> >
> > +void qmp_save_devices(const char *filename, Error **errp)
> > +{
> > +    QEMUFile *f;
> > +    int saved_vm_running;
> > +    int ret;
> > +
> > +    saved_vm_running = runstate_is_running();
> > +    vm_stop(RUN_STATE_SAVE_VM);
> > +
> > +    f = qemu_fopen(filename, "wb");
> > +    if (!f) {
> > +        error_set(errp, QERR_OPEN_FILE_FAILED, filename);
> > +        goto the_end;
> > +    }
> > +    ret = qemu_save_device_state(f);
> > +    qemu_fclose(f);
> > +    if (ret<  0) {
> > +        error_set(errp, QERR_IO_ERROR);
> > +    }
> > +
> > + the_end:
> > +    if (saved_vm_running)
> > +        vm_start();
> > +    return;
> > +}
> > +
> >   int load_vmstate(const char *name)
> >   {
> >       BlockDriverState *bs, *bs_vm_state;
>
Stefano Stabellini - March 16, 2012, 11:35 a.m.
On Thu, 15 Mar 2012, Anthony Liguori wrote:
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index d0b6792..7f938ff 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -1593,3 +1593,21 @@
> >   { 'command': 'qom-list-types',
> >     'data': { '*implements': 'str', '*abstract': 'bool' },
> >     'returns': [ 'ObjectTypeInfo' ] }
> > +
> > +##
> > +# @save_devices:
> > +#
> > +# Save the state of all devices to file. The RAM and the block devices
> > +# of the VM are not saved by this command.
> > +#
> > +# @filename: the file to save the state of the devices to as binary
> > +# data. See save_devices.txt for a description of the binary format.
> > +#
> > +# Returns: Nothing on success
> > +#          If @filename cannot be opened, OpenFileFailed
> > +#          If an I/O error occurs while writing the file, IOError
> > +#
> > +# Since: 1.0
> 
> Since: 1.1.
> 
> Otherwise Reviewed-by: Anthony Liguori <aliguori@us.ibm.com>

Thanks!
Stefano Stabellini - March 16, 2012, 11:41 a.m.
On Thu, 15 Mar 2012, Luiz Capitulino wrote:
> On Thu, 15 Mar 2012 15:16:15 -0500
> Anthony Liguori <anthony@codemonkey.ws> wrote:
> 
> > On 03/15/2012 01:19 PM, Stefano Stabellini wrote:
> > > - add an "is_ram" flag to SaveStateEntry;
> > >
> > > - register_savevm_live sets is_ram for live_savevm devices;
> > >
> > > - introduce a "save_devices" QAPI command that can be used to save
> > > the state of all devices, but not the RAM or the block devices of the
> > > VM.
> > >
> > > Changes in v6:
> > >
> > > - remove the is_ram parameter from register_savevm_live and sets is_ram
> > > if the device is a live_savevm device;
> > >
> > > - introduce save_devices as a QAPI command, write a better description
> > > for it;
> > >
> > > - fix CODING_STYLE;
> > >
> > > - introduce a new doc to explain the save format used by save_devices.
> > >
> > > Signed-off-by: Stefano Stabellini<stefano.stabellini@eu.citrix.com>
> > > CC: Anthony Liguori<anthony@codemonkey.ws>
> > > CC: Luiz Capitulino<lcapitulino@redhat.com>
> > > ---
> > >   docs/save_devices.txt |   33 ++++++++++++++++++++++
> > >   qapi-schema.json      |   18 ++++++++++++
> > >   qmp-commands.hx       |   25 +++++++++++++++++
> > >   savevm.c              |   71 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >   4 files changed, 147 insertions(+), 0 deletions(-)
> > >   create mode 100644 docs/save_devices.txt
> > >
> > > diff --git a/docs/save_devices.txt b/docs/save_devices.txt
> > > new file mode 100644
> > > index 0000000..79915d2
> > > --- /dev/null
> > > +++ b/docs/save_devices.txt
> > > @@ -0,0 +1,33 @@
> > > += Save Devices =
> > > +
> > > +QEMU has code to load/save the state of the guest that it is running.
> > > +These are two complementary operations.  Saving the state just does
> > > +that, saves the state for each device that the guest is running.
> > > +
> > > +These operations are normally used with migration (see migration.txt),
> > > +however it is also possible to save the state of all devices to file,
> > > +without saving the RAM or the block devices of the VM.
> > > +
> > > +This operation is called "save_devices" (see QMP/qmp-commands.txt).
> > > +
> > > +
> > > +The binary format used in the file is the following:
> > > +
> > > +
> > > +-------------------------------------------
> > > +
> > > +32 bit big endian: QEMU_VM_FILE_MAGIC
> > > +32 bit big endian: QEMU_VM_FILE_VERSION
> > > +
> > > +for_each_device
> > > +{
> > > +    8 bit:              QEMU_VM_SECTION_FULL
> > > +    32 bit big endian:  section_id
> > > +    8 bit:              idstr (ID string) length
> > > +    string:             idstr (ID string)
> > > +    32 bit big endian:  instance_id
> > > +    32 bit big endian:  version_id
> > > +    buffer:             device specific data
> > > +}
> > > +
> > > +8 bit: QEMU_VM_EOF
> > > diff --git a/qapi-schema.json b/qapi-schema.json
> > > index d0b6792..7f938ff 100644
> > > --- a/qapi-schema.json
> > > +++ b/qapi-schema.json
> > > @@ -1593,3 +1593,21 @@
> > >   { 'command': 'qom-list-types',
> > >     'data': { '*implements': 'str', '*abstract': 'bool' },
> > >     'returns': [ 'ObjectTypeInfo' ] }
> > > +
> > > +##
> > > +# @save_devices:
> > > +#
> > > +# Save the state of all devices to file. The RAM and the block devices
> > > +# of the VM are not saved by this command.
> > > +#
> > > +# @filename: the file to save the state of the devices to as binary
> > > +# data. See save_devices.txt for a description of the binary format.
> > > +#
> > > +# Returns: Nothing on success
> > > +#          If @filename cannot be opened, OpenFileFailed
> > > +#          If an I/O error occurs while writing the file, IOError
> > > +#
> > > +# Since: 1.0
> > 
> > Since: 1.1.
> > 
> > Otherwise Reviewed-by: Anthony Liguori <aliguori@us.ibm.com>
> 
> Looks fine to me FWIW, my only nitpick is that we use an hyphen instead of the
> underline in qmp command names, but I'd call this save-devices-state.

OK, I'll rename and resend.


> Don't you want this in HMP too, btw?

It is basically useless because the data saved by save_devices needs to
be packet together with other data before any tools can actually use it.
Eric Blake - March 16, 2012, 3:36 p.m.
On 03/15/2012 02:34 PM, Luiz Capitulino wrote:
> On Thu, 15 Mar 2012 15:16:15 -0500
> Anthony Liguori <anthony@codemonkey.ws> wrote:
> 
>> On 03/15/2012 01:19 PM, Stefano Stabellini wrote:
>>> - add an "is_ram" flag to SaveStateEntry;
>>>
>>> - register_savevm_live sets is_ram for live_savevm devices;
>>>
>>> - introduce a "save_devices" QAPI command that can be used to save
>>> the state of all devices, but not the RAM or the block devices of the
>>> VM.
>>>

>>> +SQMP
>>> +save_devices
>>> +-------
>>> +
>>> +Save the state of all devices to file. The RAM and the block devices
>>> +of the VM are not saved by this command.
>>> +
>>> +Arguments:
>>> +
>>> +- "filename": the file to save the state of the devices to as binary
>>> +data. See save_devices.txt for a description of the binary format.
>>> +
>>> +Example:
>>> +
>>> +->  { "execute": "save_devices", "arguments": { "filename": "/tmp/save" } }

Why are we adding yet another command without support for passing in an
fd instead of having qemu open the file name?  Libvirt really does want
to move to an ability to pass in fds, and the more commands we add that
open files, the harder converting them all will be.
Stefano Stabellini - March 16, 2012, 4:10 p.m.
On Fri, 16 Mar 2012, Eric Blake wrote:
> On 03/15/2012 02:34 PM, Luiz Capitulino wrote:
> > On Thu, 15 Mar 2012 15:16:15 -0500
> > Anthony Liguori <anthony@codemonkey.ws> wrote:
> > 
> >> On 03/15/2012 01:19 PM, Stefano Stabellini wrote:
> >>> - add an "is_ram" flag to SaveStateEntry;
> >>>
> >>> - register_savevm_live sets is_ram for live_savevm devices;
> >>>
> >>> - introduce a "save_devices" QAPI command that can be used to save
> >>> the state of all devices, but not the RAM or the block devices of the
> >>> VM.
> >>>
> 
> >>> +SQMP
> >>> +save_devices
> >>> +-------
> >>> +
> >>> +Save the state of all devices to file. The RAM and the block devices
> >>> +of the VM are not saved by this command.
> >>> +
> >>> +Arguments:
> >>> +
> >>> +- "filename": the file to save the state of the devices to as binary
> >>> +data. See save_devices.txt for a description of the binary format.
> >>> +
> >>> +Example:
> >>> +
> >>> +->  { "execute": "save_devices", "arguments": { "filename": "/tmp/save" } }
> 
> Why are we adding yet another command without support for passing in an
> fd instead of having qemu open the file name?  Libvirt really does want
> to move to an ability to pass in fds, and the more commands we add that
> open files, the harder converting them all will be.

This command is not meant to be used by libvirt directly: it is meant to
be used by libxenlight. Libvirt is just going to call
libxl_domain_suspend and behind the scenes libxl is going to take care
of everything.

Patch

diff --git a/docs/save_devices.txt b/docs/save_devices.txt
new file mode 100644
index 0000000..79915d2
--- /dev/null
+++ b/docs/save_devices.txt
@@ -0,0 +1,33 @@ 
+= Save Devices =
+
+QEMU has code to load/save the state of the guest that it is running.
+These are two complementary operations.  Saving the state just does
+that, saves the state for each device that the guest is running.
+
+These operations are normally used with migration (see migration.txt),
+however it is also possible to save the state of all devices to file,
+without saving the RAM or the block devices of the VM.
+
+This operation is called "save_devices" (see QMP/qmp-commands.txt).
+
+
+The binary format used in the file is the following:
+
+
+-------------------------------------------
+
+32 bit big endian: QEMU_VM_FILE_MAGIC
+32 bit big endian: QEMU_VM_FILE_VERSION
+
+for_each_device
+{
+    8 bit:              QEMU_VM_SECTION_FULL
+    32 bit big endian:  section_id
+    8 bit:              idstr (ID string) length
+    string:             idstr (ID string)
+    32 bit big endian:  instance_id
+    32 bit big endian:  version_id
+    buffer:             device specific data
+}
+
+8 bit: QEMU_VM_EOF
diff --git a/qapi-schema.json b/qapi-schema.json
index d0b6792..7f938ff 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1593,3 +1593,21 @@ 
 { 'command': 'qom-list-types',
   'data': { '*implements': 'str', '*abstract': 'bool' },
   'returns': [ 'ObjectTypeInfo' ] }
+
+##
+# @save_devices:
+#
+# Save the state of all devices to file. The RAM and the block devices
+# of the VM are not saved by this command.
+#
+# @filename: the file to save the state of the devices to as binary
+# data. See save_devices.txt for a description of the binary format.
+#
+# Returns: Nothing on success
+#          If @filename cannot be opened, OpenFileFailed
+#          If an I/O error occurs while writing the file, IOError
+#
+# Since: 1.0
+##
+{ 'command': 'save_devices', 'data': {'filename': 'str'} }
+
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 705f704..064d2a4 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -444,6 +444,31 @@  Note: inject-nmi is only supported for x86 guest currently, it will
 EQMP
 
     {
+        .name       = "save_devices",
+        .args_type  = "filename:F",
+    .mhandler.cmd_new = qmp_marshal_input_save_devices,
+    },
+
+SQMP
+save_devices
+-------
+
+Save the state of all devices to file. The RAM and the block devices
+of the VM are not saved by this command.
+
+Arguments:
+
+- "filename": the file to save the state of the devices to as binary
+data. See save_devices.txt for a description of the binary format.
+
+Example:
+
+-> { "execute": "save_devices", "arguments": { "filename": "/tmp/save" } }
+<- { "return": {} }
+
+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..148356a 100644
--- a/savevm.c
+++ b/savevm.c
@@ -84,6 +84,7 @@ 
 #include "qemu-timer.h"
 #include "cpus.h"
 #include "memory.h"
+#include "qmp-commands.h"
 
 #define SELF_ANNOUNCE_ROUNDS 5
 
@@ -1177,6 +1178,7 @@  typedef struct SaveStateEntry {
     void *opaque;
     CompatEntry *compat;
     int no_migrate;
+    int is_ram;
 } SaveStateEntry;
 
 
@@ -1241,6 +1243,10 @@  int register_savevm_live(DeviceState *dev,
     se->opaque = opaque;
     se->vmsd = NULL;
     se->no_migrate = 0;
+    /* if this is a live_savem then set is_ram */
+    if (save_live_state != NULL) {
+        se->is_ram = 1;
+    }
 
     if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
         char *id = dev->parent_bus->info->get_dev_path(dev);
@@ -1728,6 +1734,45 @@  out:
     return ret;
 }
 
+static int qemu_save_device_state(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 +2154,32 @@  void do_savevm(Monitor *mon, const QDict *qdict)
         vm_start();
 }
 
+void qmp_save_devices(const char *filename, Error **errp)
+{
+    QEMUFile *f;
+    int saved_vm_running;
+    int ret;
+
+    saved_vm_running = runstate_is_running();
+    vm_stop(RUN_STATE_SAVE_VM);
+
+    f = qemu_fopen(filename, "wb");
+    if (!f) {
+        error_set(errp, QERR_OPEN_FILE_FAILED, filename);
+        goto the_end;
+    }
+    ret = qemu_save_device_state(f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        error_set(errp, QERR_IO_ERROR);
+    }
+
+ the_end:
+    if (saved_vm_running)
+        vm_start();
+    return;
+}
+
 int load_vmstate(const char *name)
 {
     BlockDriverState *bs, *bs_vm_state;