diff mbox

[12/13,v7] dump: make kdump-compressed format available for 'dump-guest-memory'

Message ID 1389944779-31899-13-git-send-email-qiaonuohan@cn.fujitsu.com
State New
Headers show

Commit Message

Qiao Nuohan Jan. 17, 2014, 7:46 a.m. UTC
Make monitor command 'dump-guest-memory' be able to dump in kdump-compressed
format. The command's usage:

  dump [-p] protocol [begin] [length] [format]

'format' is used to specified the format of vmcore and can be:
1. 'elf': ELF format, without compression
2. 'kdump-zlib': kdump-compressed format, with zlib-compressed
3. 'kdump-lzo': kdump-compressed format, with lzo-compressed
4. 'kdump-snappy': kdump-compressed format, with snappy-compressed
Without 'format' being set, it is same as 'elf'. And if non-elf format is
specified, paging and filter is not allowed.

Note:
  1. The kdump-compressed format is readable only with the crash utility and
     makedumpfile, and it can be smaller than the ELF format because of the
     compression support.
  2. The kdump-compressed format is the 6th edition.

Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
---
 dump.c           |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 hmp.c            |    5 ++-
 qapi-schema.json |   25 ++++++++++-
 qmp-commands.hx  |    7 ++-
 4 files changed, 156 insertions(+), 10 deletions(-)

Comments

Ekaterina Tumanova Jan. 23, 2014, 3:17 p.m. UTC | #1
On 01/17/2014 11:46 AM, qiaonuohan wrote:
> Make monitor command 'dump-guest-memory' be able to dump in kdump-compressed
> format. The command's usage:
>
>    dump [-p] protocol [begin] [length] [format]
>
> 'format' is used to specified the format of vmcore and can be:
> 1. 'elf': ELF format, without compression
> 2. 'kdump-zlib': kdump-compressed format, with zlib-compressed
> 3. 'kdump-lzo': kdump-compressed format, with lzo-compressed
> 4. 'kdump-snappy': kdump-compressed format, with snappy-compressed
> Without 'format' being set, it is same as 'elf'. And if non-elf format is
> specified, paging and filter is not allowed.
>
> Note:
>    1. The kdump-compressed format is readable only with the crash utility and
>       makedumpfile, and it can be smaller than the ELF format because of the
>       compression support.
>    2. The kdump-compressed format is the 6th edition.
>
> Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
> ---
>   dump.c           |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++---
>   hmp.c            |    5 ++-
>   qapi-schema.json |   25 ++++++++++-
>   qmp-commands.hx  |    7 ++-
>   4 files changed, 156 insertions(+), 10 deletions(-)
>
> diff --git a/dump.c b/dump.c
> index bb03ef7..dbf4bb6 100644
> --- a/dump.c
> +++ b/dump.c
> @@ -1449,6 +1449,64 @@ out:
>       return ret;
>   }
>
> +static int create_kdump_vmcore(DumpState *s)
> +{
> +    int ret;
> +
> +    /*
> +     * the kdump-compressed format is:
> +     *                                               File offset
> +     *  +------------------------------------------+ 0x0
> +     *  |    main header (struct disk_dump_header) |
> +     *  |------------------------------------------+ block 1
> +     *  |    sub header (struct kdump_sub_header)  |
> +     *  |------------------------------------------+ block 2
> +     *  |            1st-dump_bitmap               |
> +     *  |------------------------------------------+ block 2 + X blocks
> +     *  |            2nd-dump_bitmap               | (aligned by block)
> +     *  |------------------------------------------+ block 2 + 2 * X blocks
> +     *  |  page desc for pfn 0 (struct page_desc)  | (aligned by block)
> +     *  |  page desc for pfn 1 (struct page_desc)  |
> +     *  |                    :                     |
> +     *  |------------------------------------------| (not aligned by block)
> +     *  |         page data (pfn 0)                |
> +     *  |         page data (pfn 1)                |
> +     *  |                    :                     |
> +     *  +------------------------------------------+
> +     */
> +
> +    ret = write_start_flat_header(s->fd);
> +    if (ret < 0) {
> +        dump_error(s, "dump: failed to write start flat header.\n");
> +        return -1;
> +    }
> +
> +    ret = write_dump_header(s);
> +    if (ret < 0) {
> +        return -1;
> +    }
> +
> +    ret = write_dump_bitmap(s);
> +    if (ret < 0) {
> +        return -1;
> +    }
> +
> +    ret = write_dump_pages(s);
> +    if (ret < 0) {
> +        return -1;
> +    }
> +
> +    ret = write_end_flat_header(s->fd);
> +    if (ret < 0) {
> +        dump_error(s, "dump: failed to write end flat header.\n");
> +        return -1;
> +    }
> +
> +    dump_completed(s);
> +
> +    return 0;
> +}
> +
>   static ram_addr_t get_start_block(DumpState *s)
>   {
>       GuestPhysBlock *block;
> @@ -1487,7 +1545,8 @@ static void get_max_mapnr(DumpState *s)
>       }
>   }
>
> -static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
> +static int dump_init(DumpState *s, int fd, bool has_format,
> +                     DumpGuestMemoryFormat format, bool paging, bool has_filter,
>                        int64_t begin, int64_t length, Error **errp)
>   {
>       CPUState *cpu;
> @@ -1495,6 +1554,11 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
>       Error *err = NULL;
>       int ret;
>
> +    /* kdump-compressed is conflict with paging and filter */
> +    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
> +        assert(!paging && !has_filter);
> +    }
> +
>       if (runstate_is_running()) {
>           vm_stop(RUN_STATE_SAVE_VM);
>           s->resume = true;
> @@ -1565,6 +1629,28 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
>       tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), s->page_size);
>       s->len_dump_bitmap = tmp * s->page_size;
>
> +    /* init for kdump-compressed format */
> +    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
> +        switch (format) {
> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB:
> +            s->flag_compress = DUMP_DH_COMPRESSED_ZLIB;
> +            break;
> +
> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO:
> +            s->flag_compress = DUMP_DH_COMPRESSED_LZO;
> +            break;
> +
> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY:
> +            s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY;
> +            break;
> +
> +        default:
> +            s->flag_compress = 0;
> +        }
> +
> +        return 0;
> +    }
> +
>       if (s->has_filter) {
>           memory_mapping_filter(&s->list, s->begin, s->length);
>       }
> @@ -1624,14 +1710,25 @@ cleanup:
>   }
>
>   void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
> -                           int64_t begin, bool has_length, int64_t length,
> -                           Error **errp)
> +                           int64_t begin, bool has_length,
> +                           int64_t length, bool has_format,
> +                           DumpGuestMemoryFormat format, Error **errp)
>   {
>       const char *p;
>       int fd = -1;
>       DumpState *s;
>       int ret;
>
> +    /*
> +     * kdump-compressed format need the whole memory dumped, so paging or
> +     * filter is not supported here.
> +     */
> +    if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) &&
> +        (paging || has_begin || has_length)) {
> +        error_setg(errp, "kdump-compressed format doesn't support paging or "
> +                         "filter");
> +        return;
> +    }
>       if (has_begin && !has_length) {
>           error_set(errp, QERR_MISSING_PARAMETER, "length");
>           return;
> @@ -1641,6 +1738,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
>           return;
>       }
>
> +    /* check whether lzo/snappy is supported */
> +#ifndef CONFIG_LZO
> +    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) {
> +        error_setg(errp, "kdump-lzo is not available now");
> +    }
> +#endif

When kdump-lzo is not available, command still writes a dump,
which is half smaller then uncompressed one (and is read as partial
dump), but is much bigger then the compressed one. Is it supposed to
behave like that?

Kate.

> +
> +#ifndef CONFIG_SNAPPY
> +    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) {
> +        error_setg(errp, "kdump-snappy is not available now");
> +    }
> +#endif
> +
>   #if !defined(WIN32)
>       if (strstart(file, "fd:", &p)) {
>           fd = monitor_get_fd(cur_mon, p, errp);
> @@ -1665,14 +1775,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
>
>       s = g_malloc0(sizeof(DumpState));
>
> -    ret = dump_init(s, fd, paging, has_begin, begin, length, errp);
> +    ret = dump_init(s, fd, has_format, format, paging, has_begin,
> +                    begin, length, errp);
>       if (ret < 0) {
>           g_free(s);
>           return;
>       }
>
> -    if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
> -        error_set(errp, QERR_IO_ERROR);
> +    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
> +        if (create_kdump_vmcore(s) < 0 && !error_is_set(s->errp)) {
> +            error_set(errp, QERR_IO_ERROR);
> +        }
> +    } else {
> +        if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
> +            error_set(errp, QERR_IO_ERROR);
> +        }
>       }
>
>       g_free(s);
> diff --git a/hmp.c b/hmp.c
> index 79f9c7d..5245e62 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1308,8 +1308,11 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
>       const char *file = qdict_get_str(qdict, "filename");
>       bool has_begin = qdict_haskey(qdict, "begin");
>       bool has_length = qdict_haskey(qdict, "length");
> +    /* kdump-compressed format is not supported for HMP */
> +    bool has_format = false;
>       int64_t begin = 0;
>       int64_t length = 0;
> +    enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
>       char *prot;
>
>       if (has_begin) {
> @@ -1322,7 +1325,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
>       prot = g_strconcat("file:", file, NULL);
>
>       qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
> -                          &errp);
> +                          has_format, dump_format, &errp);
>       hmp_handle_error(mon, &errp);
>       g_free(prot);
>   }
> diff --git a/qapi-schema.json b/qapi-schema.json
> index f27c48a..52df894 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2677,6 +2677,24 @@
>   { 'command': 'device_del', 'data': {'id': 'str'} }
>
>   ##
> +# @DumpGuestMemoryFormat:
> +#
> +# An enumeration of guest-memory-dump's format.
> +#
> +# @elf: elf format
> +#
> +# @kdump-zlib: kdump-compressed format with zlib-compressed
> +#
> +# @kdump-lzo: kdump-compressed format with lzo-compressed
> +#
> +# @kdump-snappy: kdump-compressed format with snappy-compressed
> +#
> +# Since: 2.0
> +##
> +{ 'enum': 'DumpGuestMemoryFormat',
> +  'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] }
> +
> +##
>   # @dump-guest-memory
>   #
>   # Dump guest's memory to vmcore. It is a synchronous operation that can take
> @@ -2712,13 +2730,18 @@
>   #          want to dump all guest's memory, please specify the start @begin
>   #          and @length
>   #
> +# @format: #optional if specified, the format of guest memory dump. But non-elf
> +#          format is conflict with paging and filter, ie. @paging, @begin and
> +#          @length is not allowed to be specified with @format at the same time
> +#          (since 2.0)
> +#
>   # Returns: nothing on success
>   #
>   # Since: 1.2
>   ##
>   { 'command': 'dump-guest-memory',
>     'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
> -            '*length': 'int' } }
> +            '*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
>
>   ##
>   # @netdev_add:
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 02cc815..9158f22 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -791,8 +791,8 @@ EQMP
>
>       {
>           .name       = "dump-guest-memory",
> -        .args_type  = "paging:b,protocol:s,begin:i?,end:i?",
> -        .params     = "-p protocol [begin] [length]",
> +        .args_type  = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
> +        .params     = "-p protocol [begin] [length] [format]",
>           .help       = "dump guest memory to file",
>           .user_print = monitor_user_noop,
>           .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
> @@ -813,6 +813,9 @@ Arguments:
>              with length together (json-int)
>   - "length": the memory size, in bytes. It's optional, and should be specified
>               with begin together (json-int)
> +- "format": the format of guest memory dump. It's optional, and can be
> +            elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will
> +            conflict with paging and filter, ie. begin and length(json-string)
>
>   Example:
>
Laszlo Ersek Jan. 24, 2014, 11:27 a.m. UTC | #2
On 01/23/14 16:17, Ekaterina Tumanova wrote:
> On 01/17/2014 11:46 AM, qiaonuohan wrote:
>> Make monitor command 'dump-guest-memory' be able to dump in
>> kdump-compressed
>> format. The command's usage:
>>
>>    dump [-p] protocol [begin] [length] [format]
>>
>> 'format' is used to specified the format of vmcore and can be:
>> 1. 'elf': ELF format, without compression
>> 2. 'kdump-zlib': kdump-compressed format, with zlib-compressed
>> 3. 'kdump-lzo': kdump-compressed format, with lzo-compressed
>> 4. 'kdump-snappy': kdump-compressed format, with snappy-compressed
>> Without 'format' being set, it is same as 'elf'. And if non-elf format is
>> specified, paging and filter is not allowed.
>>
>> Note:
>>    1. The kdump-compressed format is readable only with the crash
>> utility and
>>       makedumpfile, and it can be smaller than the ELF format because
>> of the
>>       compression support.
>>    2. The kdump-compressed format is the 6th edition.
>>
>> Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
>> ---
>>   dump.c           |  129
>> +++++++++++++++++++++++++++++++++++++++++++++++++++---
>>   hmp.c            |    5 ++-
>>   qapi-schema.json |   25 ++++++++++-
>>   qmp-commands.hx  |    7 ++-
>>   4 files changed, 156 insertions(+), 10 deletions(-)
>>
>> diff --git a/dump.c b/dump.c
>> index bb03ef7..dbf4bb6 100644
>> --- a/dump.c
>> +++ b/dump.c
>> @@ -1449,6 +1449,64 @@ out:
>>       return ret;
>>   }
>>
>> +static int create_kdump_vmcore(DumpState *s)
>> +{
>> +    int ret;
>> +
>> +    /*
>> +     * the kdump-compressed format is:
>> +     *                                               File offset
>> +     *  +------------------------------------------+ 0x0
>> +     *  |    main header (struct disk_dump_header) |
>> +     *  |------------------------------------------+ block 1
>> +     *  |    sub header (struct kdump_sub_header)  |
>> +     *  |------------------------------------------+ block 2
>> +     *  |            1st-dump_bitmap               |
>> +     *  |------------------------------------------+ block 2 + X blocks
>> +     *  |            2nd-dump_bitmap               | (aligned by block)
>> +     *  |------------------------------------------+ block 2 + 2 * X
>> blocks
>> +     *  |  page desc for pfn 0 (struct page_desc)  | (aligned by block)
>> +     *  |  page desc for pfn 1 (struct page_desc)  |
>> +     *  |                    :                     |
>> +     *  |------------------------------------------| (not aligned by
>> block)
>> +     *  |         page data (pfn 0)                |
>> +     *  |         page data (pfn 1)                |
>> +     *  |                    :                     |
>> +     *  +------------------------------------------+
>> +     */
>> +
>> +    ret = write_start_flat_header(s->fd);
>> +    if (ret < 0) {
>> +        dump_error(s, "dump: failed to write start flat header.\n");
>> +        return -1;
>> +    }
>> +
>> +    ret = write_dump_header(s);
>> +    if (ret < 0) {
>> +        return -1;
>> +    }
>> +
>> +    ret = write_dump_bitmap(s);
>> +    if (ret < 0) {
>> +        return -1;
>> +    }
>> +
>> +    ret = write_dump_pages(s);
>> +    if (ret < 0) {
>> +        return -1;
>> +    }
>> +
>> +    ret = write_end_flat_header(s->fd);
>> +    if (ret < 0) {
>> +        dump_error(s, "dump: failed to write end flat header.\n");
>> +        return -1;
>> +    }
>> +
>> +    dump_completed(s);
>> +
>> +    return 0;
>> +}
>> +
>>   static ram_addr_t get_start_block(DumpState *s)
>>   {
>>       GuestPhysBlock *block;
>> @@ -1487,7 +1545,8 @@ static void get_max_mapnr(DumpState *s)
>>       }
>>   }
>>
>> -static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
>> +static int dump_init(DumpState *s, int fd, bool has_format,
>> +                     DumpGuestMemoryFormat format, bool paging, bool
>> has_filter,
>>                        int64_t begin, int64_t length, Error **errp)
>>   {
>>       CPUState *cpu;
>> @@ -1495,6 +1554,11 @@ static int dump_init(DumpState *s, int fd, bool
>> paging, bool has_filter,
>>       Error *err = NULL;
>>       int ret;
>>
>> +    /* kdump-compressed is conflict with paging and filter */
>> +    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
>> +        assert(!paging && !has_filter);
>> +    }
>> +
>>       if (runstate_is_running()) {
>>           vm_stop(RUN_STATE_SAVE_VM);
>>           s->resume = true;
>> @@ -1565,6 +1629,28 @@ static int dump_init(DumpState *s, int fd, bool
>> paging, bool has_filter,
>>       tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT),
>> s->page_size);
>>       s->len_dump_bitmap = tmp * s->page_size;
>>
>> +    /* init for kdump-compressed format */
>> +    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
>> +        switch (format) {
>> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB:
>> +            s->flag_compress = DUMP_DH_COMPRESSED_ZLIB;
>> +            break;
>> +
>> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO:
>> +            s->flag_compress = DUMP_DH_COMPRESSED_LZO;
>> +            break;
>> +
>> +        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY:
>> +            s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY;
>> +            break;
>> +
>> +        default:
>> +            s->flag_compress = 0;
>> +        }
>> +
>> +        return 0;
>> +    }
>> +
>>       if (s->has_filter) {
>>           memory_mapping_filter(&s->list, s->begin, s->length);
>>       }
>> @@ -1624,14 +1710,25 @@ cleanup:
>>   }
>>
>>   void qmp_dump_guest_memory(bool paging, const char *file, bool
>> has_begin,
>> -                           int64_t begin, bool has_length, int64_t
>> length,
>> -                           Error **errp)
>> +                           int64_t begin, bool has_length,
>> +                           int64_t length, bool has_format,
>> +                           DumpGuestMemoryFormat format, Error **errp)
>>   {
>>       const char *p;
>>       int fd = -1;
>>       DumpState *s;
>>       int ret;
>>
>> +    /*
>> +     * kdump-compressed format need the whole memory dumped, so
>> paging or
>> +     * filter is not supported here.
>> +     */
>> +    if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) &&
>> +        (paging || has_begin || has_length)) {
>> +        error_setg(errp, "kdump-compressed format doesn't support
>> paging or "
>> +                         "filter");
>> +        return;
>> +    }
>>       if (has_begin && !has_length) {
>>           error_set(errp, QERR_MISSING_PARAMETER, "length");
>>           return;
>> @@ -1641,6 +1738,19 @@ void qmp_dump_guest_memory(bool paging, const
>> char *file, bool has_begin,
>>           return;
>>       }
>>
>> +    /* check whether lzo/snappy is supported */
>> +#ifndef CONFIG_LZO
>> +    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) {
>> +        error_setg(errp, "kdump-lzo is not available now");
>> +    }
>> +#endif
> 
> When kdump-lzo is not available, command still writes a dump,
> which is half smaller then uncompressed one (and is read as partial
> dump), but is much bigger then the compressed one. Is it supposed to
> behave like that?

No, it's not.

The format availability checks set the error like they should, but the
return statements are missing. Compare these lzo and snappy fmt avail
tests to the paging/filtering test just above -- after setting the
error, qmp_dump_guest_memory() has no business continuing, it must bail out.

Good catch. I missed it in the v6 review (perhaps because I already
found other warts to clean up in these parts -- at least that's what I'm
telling myself :)).

Thanks,
Laszlo
Laszlo Ersek Jan. 24, 2014, 12:06 p.m. UTC | #3
Thanks for addressing my earlier comments. Some new ones below:

On 01/17/14 08:46, qiaonuohan wrote:

> +    /* check whether lzo/snappy is supported */
> +#ifndef CONFIG_LZO
> +    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) {
> +        error_setg(errp, "kdump-lzo is not available now");
> +    }
> +#endif
> +
> +#ifndef CONFIG_SNAPPY
> +    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) {
> +        error_setg(errp, "kdump-snappy is not available now");
> +    }
> +#endif

Ekaterina caught in testing that these two blocks must be complemented
with a return statement each -- when you detect these errors,
qmp_dump_guest_memory() must not proceed. Apologies for not noticing
them in v6.

> +##
>  # @dump-guest-memory
>  #
>  # Dump guest's memory to vmcore. It is a synchronous operation that can take
> @@ -2712,13 +2730,18 @@
>  #          want to dump all guest's memory, please specify the start @begin
>  #          and @length
>  #
> +# @format: #optional if specified, the format of guest memory dump. But non-elf
> +#          format is conflict with paging and filter, ie. @paging, @begin and
> +#          @length is not allowed to be specified with @format at the same time

Please make it more precise with "non-elf", as in:

  ... are not allowed to be specified with *non-elf* @format at the same
  time

Not really relevant but since you'll respin anyway... :)

> +#          (since 2.0)
> +#
>  # Returns: nothing on success
>  #
>  # Since: 1.2
>  ##
>  { 'command': 'dump-guest-memory',
>    'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
> -            '*length': 'int' } }
> +            '*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
>  
>  ##
>  # @netdev_add:
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 02cc815..9158f22 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -791,8 +791,8 @@ EQMP
>  
>      {
>          .name       = "dump-guest-memory",
> -        .args_type  = "paging:b,protocol:s,begin:i?,end:i?",
> -        .params     = "-p protocol [begin] [length]",
> +        .args_type  = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
> +        .params     = "-p protocol [begin] [length] [format]",
>          .help       = "dump guest memory to file",
>          .user_print = monitor_user_noop,
>          .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
> @@ -813,6 +813,9 @@ Arguments:
>             with length together (json-int)
>  - "length": the memory size, in bytes. It's optional, and should be specified
>              with begin together (json-int)
> +- "format": the format of guest memory dump. It's optional, and can be
> +            elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will
> +            conflict with paging and filter, ie. begin and length(json-string)

Please add a space between "length" and "(json-string)".

Thank you!
Laszlo
diff mbox

Patch

diff --git a/dump.c b/dump.c
index bb03ef7..dbf4bb6 100644
--- a/dump.c
+++ b/dump.c
@@ -1449,6 +1449,64 @@  out:
     return ret;
 }
 
+static int create_kdump_vmcore(DumpState *s)
+{
+    int ret;
+
+    /*
+     * the kdump-compressed format is:
+     *                                               File offset
+     *  +------------------------------------------+ 0x0
+     *  |    main header (struct disk_dump_header) |
+     *  |------------------------------------------+ block 1
+     *  |    sub header (struct kdump_sub_header)  |
+     *  |------------------------------------------+ block 2
+     *  |            1st-dump_bitmap               |
+     *  |------------------------------------------+ block 2 + X blocks
+     *  |            2nd-dump_bitmap               | (aligned by block)
+     *  |------------------------------------------+ block 2 + 2 * X blocks
+     *  |  page desc for pfn 0 (struct page_desc)  | (aligned by block)
+     *  |  page desc for pfn 1 (struct page_desc)  |
+     *  |                    :                     |
+     *  |------------------------------------------| (not aligned by block)
+     *  |         page data (pfn 0)                |
+     *  |         page data (pfn 1)                |
+     *  |                    :                     |
+     *  +------------------------------------------+
+     */
+
+    ret = write_start_flat_header(s->fd);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write start flat header.\n");
+        return -1;
+    }
+
+    ret = write_dump_header(s);
+    if (ret < 0) {
+        return -1;
+    }
+
+    ret = write_dump_bitmap(s);
+    if (ret < 0) {
+        return -1;
+    }
+
+    ret = write_dump_pages(s);
+    if (ret < 0) {
+        return -1;
+    }
+
+    ret = write_end_flat_header(s->fd);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write end flat header.\n");
+        return -1;
+    }
+
+    dump_completed(s);
+
+    return 0;
+}
+
 static ram_addr_t get_start_block(DumpState *s)
 {
     GuestPhysBlock *block;
@@ -1487,7 +1545,8 @@  static void get_max_mapnr(DumpState *s)
     }
 }
 
-static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
+static int dump_init(DumpState *s, int fd, bool has_format,
+                     DumpGuestMemoryFormat format, bool paging, bool has_filter,
                      int64_t begin, int64_t length, Error **errp)
 {
     CPUState *cpu;
@@ -1495,6 +1554,11 @@  static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
     Error *err = NULL;
     int ret;
 
+    /* kdump-compressed is conflict with paging and filter */
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
+        assert(!paging && !has_filter);
+    }
+
     if (runstate_is_running()) {
         vm_stop(RUN_STATE_SAVE_VM);
         s->resume = true;
@@ -1565,6 +1629,28 @@  static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
     tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), s->page_size);
     s->len_dump_bitmap = tmp * s->page_size;
 
+    /* init for kdump-compressed format */
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
+        switch (format) {
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB:
+            s->flag_compress = DUMP_DH_COMPRESSED_ZLIB;
+            break;
+
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO:
+            s->flag_compress = DUMP_DH_COMPRESSED_LZO;
+            break;
+
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY:
+            s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY;
+            break;
+
+        default:
+            s->flag_compress = 0;
+        }
+
+        return 0;
+    }
+
     if (s->has_filter) {
         memory_mapping_filter(&s->list, s->begin, s->length);
     }
@@ -1624,14 +1710,25 @@  cleanup:
 }
 
 void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
-                           int64_t begin, bool has_length, int64_t length,
-                           Error **errp)
+                           int64_t begin, bool has_length,
+                           int64_t length, bool has_format,
+                           DumpGuestMemoryFormat format, Error **errp)
 {
     const char *p;
     int fd = -1;
     DumpState *s;
     int ret;
 
+    /*
+     * kdump-compressed format need the whole memory dumped, so paging or
+     * filter is not supported here.
+     */
+    if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) &&
+        (paging || has_begin || has_length)) {
+        error_setg(errp, "kdump-compressed format doesn't support paging or "
+                         "filter");
+        return;
+    }
     if (has_begin && !has_length) {
         error_set(errp, QERR_MISSING_PARAMETER, "length");
         return;
@@ -1641,6 +1738,19 @@  void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
         return;
     }
 
+    /* check whether lzo/snappy is supported */
+#ifndef CONFIG_LZO
+    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) {
+        error_setg(errp, "kdump-lzo is not available now");
+    }
+#endif
+
+#ifndef CONFIG_SNAPPY
+    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) {
+        error_setg(errp, "kdump-snappy is not available now");
+    }
+#endif
+
 #if !defined(WIN32)
     if (strstart(file, "fd:", &p)) {
         fd = monitor_get_fd(cur_mon, p, errp);
@@ -1665,14 +1775,21 @@  void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
 
     s = g_malloc0(sizeof(DumpState));
 
-    ret = dump_init(s, fd, paging, has_begin, begin, length, errp);
+    ret = dump_init(s, fd, has_format, format, paging, has_begin,
+                    begin, length, errp);
     if (ret < 0) {
         g_free(s);
         return;
     }
 
-    if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
-        error_set(errp, QERR_IO_ERROR);
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
+        if (create_kdump_vmcore(s) < 0 && !error_is_set(s->errp)) {
+            error_set(errp, QERR_IO_ERROR);
+        }
+    } else {
+        if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
+            error_set(errp, QERR_IO_ERROR);
+        }
     }
 
     g_free(s);
diff --git a/hmp.c b/hmp.c
index 79f9c7d..5245e62 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1308,8 +1308,11 @@  void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
     const char *file = qdict_get_str(qdict, "filename");
     bool has_begin = qdict_haskey(qdict, "begin");
     bool has_length = qdict_haskey(qdict, "length");
+    /* kdump-compressed format is not supported for HMP */
+    bool has_format = false;
     int64_t begin = 0;
     int64_t length = 0;
+    enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
     char *prot;
 
     if (has_begin) {
@@ -1322,7 +1325,7 @@  void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
     prot = g_strconcat("file:", file, NULL);
 
     qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
-                          &errp);
+                          has_format, dump_format, &errp);
     hmp_handle_error(mon, &errp);
     g_free(prot);
 }
diff --git a/qapi-schema.json b/qapi-schema.json
index f27c48a..52df894 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2677,6 +2677,24 @@ 
 { 'command': 'device_del', 'data': {'id': 'str'} }
 
 ##
+# @DumpGuestMemoryFormat:
+#
+# An enumeration of guest-memory-dump's format.
+#
+# @elf: elf format
+#
+# @kdump-zlib: kdump-compressed format with zlib-compressed
+#
+# @kdump-lzo: kdump-compressed format with lzo-compressed
+#
+# @kdump-snappy: kdump-compressed format with snappy-compressed
+#
+# Since: 2.0
+##
+{ 'enum': 'DumpGuestMemoryFormat',
+  'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] }
+
+##
 # @dump-guest-memory
 #
 # Dump guest's memory to vmcore. It is a synchronous operation that can take
@@ -2712,13 +2730,18 @@ 
 #          want to dump all guest's memory, please specify the start @begin
 #          and @length
 #
+# @format: #optional if specified, the format of guest memory dump. But non-elf
+#          format is conflict with paging and filter, ie. @paging, @begin and
+#          @length is not allowed to be specified with @format at the same time
+#          (since 2.0)
+#
 # Returns: nothing on success
 #
 # Since: 1.2
 ##
 { 'command': 'dump-guest-memory',
   'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
-            '*length': 'int' } }
+            '*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
 
 ##
 # @netdev_add:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 02cc815..9158f22 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -791,8 +791,8 @@  EQMP
 
     {
         .name       = "dump-guest-memory",
-        .args_type  = "paging:b,protocol:s,begin:i?,end:i?",
-        .params     = "-p protocol [begin] [length]",
+        .args_type  = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
+        .params     = "-p protocol [begin] [length] [format]",
         .help       = "dump guest memory to file",
         .user_print = monitor_user_noop,
         .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
@@ -813,6 +813,9 @@  Arguments:
            with length together (json-int)
 - "length": the memory size, in bytes. It's optional, and should be specified
             with begin together (json-int)
+- "format": the format of guest memory dump. It's optional, and can be
+            elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will
+            conflict with paging and filter, ie. begin and length(json-string)
 
 Example: