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 :|