On Tue, Aug 11, 2026 at 11:24:57AM +0200, Denis V. Lunev via Devel wrote:
> Each on_crash or watchdog-triggered dump writes a full memory dump
> into auto_dump_path. A guest that keeps crashing and restarting (or
> crashing and getting destroyed, then respawned by the mgmt app) can
> fill the disk one dump at a time, with nothing to stop it.
> 
> Add auto_dump_max_size (qemu.conf), parsed via virConfGetValueBytes()
> so it takes a plain byte count or a size with a unit suffix (e.g.
> "10GiB"). After a dump is written, the oldest dumps under
> auto_dump_path are removed until the total fits the configured quota.
> The dump that was just written is always kept by identity, not by
> sort position: mtime is only second-granularity, so two dumps written
> the same second would otherwise make the eviction order between them
> arbitrary and could delete the one just written instead of an older
> one. Defaults to 0, which keeps every dump forever, as before.
> 
> Only files named the way getAutoDumpPath() names them count toward the
> quota, and nothing is pruned when the dump itself failed.
> 
> libvirtd_qemu.aug gains a matching entry; its quoted branch requires a
> unit suffix, as one matching a bare number would overlap the integer
> branch.
> 
> Signed-off-by: Denis V. Lunev <[email protected]>
> ---
>  NEWS.rst                           |   7 ++
>  src/qemu/libvirtd_qemu.aug         |   4 +
>  src/qemu/qemu.conf.in              |  17 ++++
>  src/qemu/qemu_conf.c               |   2 +
>  src/qemu/qemu_conf.h               |   1 +
>  src/qemu/qemu_driver.c             | 130 +++++++++++++++++++++++++++++
>  src/qemu/test_libvirtd_qemu.aug.in |   1 +
>  7 files changed, 162 insertions(+)


> +static void
> +qemuPruneAutoDumpPath(virQEMUDriverConfig *cfg,
> +                      const char *keep)
> +{
> +    g_autoptr(DIR) dir = NULL;
> +    struct dirent *entry;
> +    g_autoptr(GPtrArray) files = NULL;
> +    unsigned long long total = 0;
> +    size_t i;
> +    int rc;
> +
> +    if (cfg->autoDumpMaxSize == 0)
> +        return;
> +
> +    if (virDirOpenQuiet(&dir, cfg->autoDumpPath) < 0)
> +        return;
> +
> +    files = g_ptr_array_new_with_free_func(qemuAutoDumpFileFree);
> +
> +    while ((rc = virDirRead(dir, &entry, NULL)) > 0) {
> +        g_autofree char *path = g_strdup_printf("%s/%s", cfg->autoDumpPath,
> +                                                entry->d_name);
> +        GStatBuf sb;
> +        qemuAutoDumpFile *file;
> +
> +        if (!qemuIsAutoDumpFileName(entry->d_name))
> +            continue;
> +
> +        if (g_stat(path, &sb) < 0 || !S_ISREG(sb.st_mode))
> +            continue;
> +
> +        total += sb.st_size;

'st_size' is counting logical file size.

I think we probably want to instead count allocated file
size (total += sb.st_blocks * sb.st_blksize) so that we
don't over-count if files happen to be sparse.

I was wondernig if we should be concerned about overflowing
'total', but as long as we count allocated size, not logical
size I think we'll be safe enough from overflow.

> +
> +        if (STREQ(path, keep))
> +            continue;
> +
> +        file = g_new0(qemuAutoDumpFile, 1);
> +        file->path = g_steal_pointer(&path);
> +        file->size = sb.st_size;
> +        file->mtime = sb.st_mtime;
> +
> +        g_ptr_array_add(files, file);
> +    }
> +
> +    if (rc < 0)
> +        return;
> +
> +    g_ptr_array_sort(files, qemuAutoDumpFileCompare);
> +
> +    for (i = 0; i < files->len && total > cfg->autoDumpMaxSize; i++) {
> +        qemuAutoDumpFile *file = g_ptr_array_index(files, i);
> +
> +        if (unlink(file->path) < 0 && errno != ENOENT) {
> +            VIR_WARN("Failed to prune old dump %s: %s",
> +                     file->path, g_strerror(errno));
> +            continue;
> +        }
> +
> +        VIR_DEBUG("Pruned old dump %s to satisfy auto_dump_max_size quota",
> +                  file->path);
> +        total -= file->size;
> +    }
> +}
> +
> +
>  static void
>  processWatchdogEvent(virQEMUDriver *driver,
>                       virDomainObj *vm,
> @@ -3589,6 +3715,8 @@ processWatchdogEvent(virQEMUDriver *driver,
>                                qemuDomainGetAutoDumpFormat(vm))) < 0)
>              virReportError(VIR_ERR_OPERATION_FAILED,
>                             "%s", _("Dump failed"));
> +        else
> +            qemuPruneAutoDumpPath(cfg, dumpfile);
>  
>          ret = qemuProcessStartCPUs(driver, vm,
>                                     VIR_DOMAIN_RUNNING_UNPAUSED,
> @@ -3624,6 +3752,8 @@ doCoreDumpToAutoDumpPath(virQEMUDriver *driver,
>                            qemuDomainGetAutoDumpFormat(vm))) < 0)
>          virReportError(VIR_ERR_OPERATION_FAILED,
>                         "%s", _("Dump failed"));
> +    else
> +        qemuPruneAutoDumpPath(cfg, dumpfile);
>      return ret;
>  }
>  
> diff --git a/src/qemu/test_libvirtd_qemu.aug.in 
> b/src/qemu/test_libvirtd_qemu.aug.in
> index c4cf9cf634..c97d8d7081 100644
> --- a/src/qemu/test_libvirtd_qemu.aug.in
> +++ b/src/qemu/test_libvirtd_qemu.aug.in
> @@ -83,6 +83,7 @@ module Test_libvirtd_qemu =
>  { "snapshot_image_format" = "raw" }
>  { "auto_dump_path" = "/var/lib/libvirt/qemu/dump" }
>  { "auto_dump_bypass_cache" = "0" }
> +{ "auto_dump_max_size" = "10GiB" }
>  { "auto_start_bypass_cache" = "0" }
>  { "auto_start_delay" = "0" }
>  { "auto_shutdown_try_save" = "persistent" }
> -- 
> 2.53.0
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|

Reply via email to