public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH docs/manager/qemu-server v2 0/3] partially fix #1989: add QEMU qcow2 cache options
@ 2026-07-20  9:46 Erik Fastermann
  2026-07-20  9:46 ` [PATCH qemu-server v2 1/3] partially fix #1989: disk: add " Erik Fastermann
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Erik Fastermann @ 2026-07-20  9:46 UTC (permalink / raw)
  To: pve-devel; +Cc: Erik Fastermann

Add multiple options to configure the qcow2 L2/refcount cache. This
can provide significant performance gains in some cases.


qemu-server:

Erik Fastermann (1):
  partially fix #1989: disk: add qcow2 cache options

 src/PVE/QemuServer.pm          |  30 +++++
 src/PVE/QemuServer/Blockdev.pm |   8 ++
 src/PVE/QemuServer/Drive.pm    | 196 +++++++++++++++++++++++++++++++++
 3 files changed, 234 insertions(+)


pve-manager:

Erik Fastermann (1):
  partially fix #1989: ui: qemu: disk: add qcow2 cache size config

 www/manager6/Parser.js      |  2 +-
 www/manager6/qemu/HDEdit.js | 73 +++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 1 deletion(-)


pve-docs:

Erik Fastermann (1):
  partially fix #1989: qm: document qcow2 cache size config

 qm.adoc | 13 +++++++++++++
 1 file changed, 13 insertions(+)


Summary over all repositories:
  6 files changed, 321 insertions(+), 1 deletions(-)

-- 
Generated by murpp 0.11.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH qemu-server v2 1/3] partially fix #1989: disk: add qcow2 cache options
  2026-07-20  9:46 [PATCH docs/manager/qemu-server v2 0/3] partially fix #1989: add QEMU qcow2 cache options Erik Fastermann
@ 2026-07-20  9:46 ` Erik Fastermann
  2026-07-20  9:46 ` [PATCH pve-manager v2 2/3] partially fix #1989: ui: qemu: disk: add qcow2 cache size config Erik Fastermann
  2026-07-20  9:46 ` [PATCH pve-docs v2 3/3] partially fix #1989: qm: document " Erik Fastermann
  2 siblings, 0 replies; 4+ messages in thread
From: Erik Fastermann @ 2026-07-20  9:46 UTC (permalink / raw)
  To: pve-devel; +Cc: Erik Fastermann

Add multiple options to configure the qcow2 L2/refcount cache. This
can provide significant performance gains in some cases.

For a detailed explanation of the options see the QEMU docs [0].
Additionally the cache size can be configured based on the size of the
disk image automatically.

Both blockdev and the older drive commandline are supported, which makes
the feature accessible to all QEMU machine versions supported by PVE.
Options which only apply to disk creation (cluster_size, refcount_bits)
are not considered in this patch.

[0] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/qcow2-cache.txt

Signed-off-by: Erik Fastermann <e.fastermann@proxmox.com>
---

 changes since v1:
 * refactored cache option generation for the legacy drive commandline and
   blockdev to use the shared function qcow2_cache_options
 * check based on disk when generating the cache options and in
   vmconfig_update_disk
 * use safe_num_ne in vmconfig_update_disk for the cache size
 * more detailed API docs
 * reuse PVE::Storage::Common::qemu_img_info

 src/PVE/QemuServer.pm          |  30 +++++
 src/PVE/QemuServer/Blockdev.pm |   8 ++
 src/PVE/QemuServer/Drive.pm    | 196 +++++++++++++++++++++++++++++++++
 3 files changed, 234 insertions(+)

diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index 191ae549..2edd6f80 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -1295,6 +1295,13 @@ sub print_drive_commandline_full {
         $opts .= ",auto-remove=on";
     }
 
+    foreach my $option_value (
+        PVE::QemuServer::Drive::qcow2_cache_options($storecfg, $drive, $drive_id, $format)
+    ) {
+        my ($option, $value) = @$option_value;
+        $opts .= ",$option=$value";
+    }
+
     # my $file_param = $live_restore_name ? "file.file.filename" : "file";
     my $file_param = "file";
     if ($live_restore_name) {
@@ -5243,6 +5250,29 @@ sub vmconfig_update_disk {
                     || safe_string_ne($drive->{ssd}, $old_drive->{ssd})
                     || safe_string_ne($drive->{vendor}, $old_drive->{vendor})
                     || safe_string_ne($drive->{ro}, $old_drive->{ro})
+                    || safe_num_ne(
+                        $drive->{'qcow2-cache-size'}, $old_drive->{'qcow2-cache-size'},
+                    )
+                    || safe_boolean_ne(
+                        $drive->{'qcow2-cache-size-based-on-disk'},
+                        $old_drive->{'qcow2-cache-size-based-on-disk'},
+                    )
+                    || safe_num_ne(
+                        $drive->{'qcow2-l2-cache-size'},
+                        $old_drive->{'qcow2-l2-cache-size'},
+                    )
+                    || safe_num_ne(
+                        $drive->{'qcow2-l2-cache-entry-size'},
+                        $old_drive->{'qcow2-l2-cache-entry-size'},
+                    )
+                    || safe_num_ne(
+                        $drive->{'qcow2-refcount-cache-size'},
+                        $old_drive->{'qcow2-refcount-cache-size'},
+                    )
+                    || safe_num_ne(
+                        $drive->{'qcow2-cache-clean-interval'},
+                        $old_drive->{'qcow2-cache-clean-interval'},
+                    )
                 ) {
                     die "skip\n";
                 }
diff --git a/src/PVE/QemuServer/Blockdev.pm b/src/PVE/QemuServer/Blockdev.pm
index 101c747c..da276bc0 100644
--- a/src/PVE/QemuServer/Blockdev.pm
+++ b/src/PVE/QemuServer/Blockdev.pm
@@ -9,6 +9,7 @@ use File::stat;
 use JSON;
 
 use PVE::JSONSchema qw(json_bool);
+use PVE::RESTEnvironment qw(log_warn);
 use PVE::Storage;
 
 use PVE::QemuServer::Drive qw(drive_is_cdrom);
@@ -403,6 +404,13 @@ sub generate_format_blockdev {
         $blockdev->{'discard-no-unref'} = JSON::true if $format eq 'qcow2';
     }
 
+    foreach my $option_value (
+        PVE::QemuServer::Drive::qcow2_cache_options($storecfg, $drive, $drive_id, $format)
+    ) {
+        my ($option, $value) = @$option_value;
+        $blockdev->{$option} = $value;
+    }
+
     return $blockdev;
 }
 
diff --git a/src/PVE/QemuServer/Drive.pm b/src/PVE/QemuServer/Drive.pm
index b80b7dbb..9c874aab 100644
--- a/src/PVE/QemuServer/Drive.pm
+++ b/src/PVE/QemuServer/Drive.pm
@@ -6,7 +6,9 @@ use warnings;
 use Storable qw(dclone);
 
 use IO::File;
+use JSON qw(decode_json);
 use List::Util qw(first);
+use POSIX qw(ceil);
 
 use PVE::RESTEnvironment qw(log_warn);
 use PVE::Storage;
@@ -256,6 +258,62 @@ my %drivedesc_base = (
         optional => 1,
         default => 0,
     },
+    'qcow2-cache-size' => {
+        type => 'integer',
+        minimum => 1,
+        maximum => 10 * 1024,
+        description =>
+            'Cache size for qcow2 disks in MiB, defaulting to 32 MiB for the L2 cache'
+            . ' and 4 clusters for the refcount cache (256 KiB with the default cluster size),'
+            . ' assuming qcow2-l2-cache-size and qcow2-refcount-cache-size are not set.'
+            . ' Only two of qcow2-cache-size, qcow2-l2-cache-size, and qcow2-refcount-cache-size'
+            . ' can be set simultaneously. This option is incompatible with'
+            . ' qcow2-cache-size-based-on-disk set to true.',
+        optional => 1,
+    },
+    'qcow2-cache-size-based-on-disk' => {
+        type => 'boolean',
+        description =>
+            'Automatically pick a qcow2 cache size based on the configured disk size.'
+            . ' With the default 64 KiB cluster size, 16 refcount bits and no extended'
+            . ' L2 entries this uses at most roughly 160 KiB of memory per GiB of disk.'
+            . ' This option is incompatible with qcow2-cache-size, qcow2-l2-cache-size,'
+            . ' or qcow2-refcount-cache-size being set.',
+        optional => 1,
+    },
+    'qcow2-l2-cache-size' => {
+        type => 'integer',
+        minimum => 1,
+        maximum => 10 * 1024,
+        description => 'L2 cache size for qcow2 disks in MiB. If qcow2-cache-size is set,'
+            . ' this value should be smaller. Only two of qcow2-cache-size, qcow2-l2-cache-size,'
+            . ' and qcow2-refcount-cache-size can be set simultaneously.',
+        optional => 1,
+    },
+    'qcow2-l2-cache-entry-size' => {
+        type => 'integer',
+        minimum => 512,
+        maximum => 2 * 1024 * 1024,
+        description => 'L2 cache entry size for qcow2 disks in bytes. Must be a power of two'
+            . ' and less than or equal to the cluster size.',
+        optional => 1,
+    },
+    'qcow2-refcount-cache-size' => {
+        type => 'integer',
+        minimum => 1,
+        maximum => 10 * 1024,
+        description =>
+            'Refcount cache size for qcow2 disks in MiB. If qcow2-cache-size is set,'
+            . ' this value should be smaller. Only two of qcow2-cache-size, qcow2-l2-cache-size,'
+            . ' and qcow2-refcount-cache-size can be set simultaneously.',
+        optional => 1,
+    },
+    'qcow2-cache-clean-interval' => {
+        type => 'integer',
+        minimum => 0,
+        description => 'Cache clean interval for qcow2 disks in seconds.',
+        optional => 1,
+    },
 );
 
 my %iothread_fmt = (
@@ -838,6 +896,52 @@ sub parse_drive {
         }
     }
 
+    my $cache_option_count =
+        defined($res->{'qcow2-cache-size'}) +
+        defined($res->{'qcow2-l2-cache-size'}) +
+        defined($res->{'qcow2-refcount-cache-size'});
+
+    if ($cache_option_count > 2) {
+        warn "at most two of qcow2-cache-size, qcow2-l2-cache-size, qcow2-refcount-cache-size"
+            . " can be set simultaneously\n";
+        ++$error;
+    }
+
+    if (
+        defined($res->{'qcow2-l2-cache-entry-size'})
+        && !is_power_of_two($res->{'qcow2-l2-cache-entry-size'})
+    ) {
+        warn "qcow2-l2-cache-entry-size must be a power of two\n";
+        ++$error;
+    }
+
+    if (
+        defined($res->{'qcow2-cache-size-based-on-disk'})
+        && $res->{'qcow2-cache-size-based-on-disk'}
+    ) {
+        if (
+            defined($res->{'qcow2-cache-size'})
+            || defined($res->{'qcow2-l2-cache-size'})
+            || defined($res->{'qcow2-refcount-cache-size'})
+        ) {
+            warn "qcow2-cache-size-based-on-disk is not compatible with cache-size,"
+                . " l2-cache-size or refcount-cache-size being set\n";
+            ++$error;
+        }
+    }
+
+    if (defined($res->{'qcow2-cache-size'})) {
+        if (($res->{'qcow2-l2-cache-size'} // 0) >= $res->{'qcow2-cache-size'}) {
+            warn "qcow2-l2-cache-size is larger than or equal to qcow2-cache-size\n";
+            ++$error;
+        }
+
+        if (($res->{'qcow2-refcount-cache-size'} // 0) >= $res->{'qcow2-cache-size'}) {
+            warn "qcow2-refcount-cache-size is larger than or equal to qcow2-cache-size\n";
+            ++$error;
+        }
+    }
+
     return if $error;
 
     return if $res->{mbps_rd} && $res->{mbps};
@@ -857,6 +961,11 @@ sub parse_drive {
     return $res;
 }
 
+sub is_power_of_two {
+    my ($n) = @_;
+    return $n > 0 && (($n & ($n - 1)) == 0);
+}
+
 sub print_drive {
     my ($drive, $with_alloc) = @_;
     my $skip = ['index', 'interface'];
@@ -1179,4 +1288,91 @@ sub drive_qmp_peer {
     return drive_uses_qsd_fuse($storecfg, $drive) ? qsd_qmp_peer($vmid) : vm_qmp_peer($vmid);
 }
 
+my sub qcow2_cache_size_by_disk_size {
+    # The calculation is adapted from here:
+    # https://gitlab.com/qemu-project/qemu/-/blob/master/docs/qcow2-cache.txt
+    # As this combines the refcount and L2 cache sizes, QEMU is free to
+    # choose a different value for each of them, which means the L2 cache
+    # is not necessarily 4 times bigger than the refcount cache with the
+    # default cluster_size, refcount_bits and no extended L2 entries.
+
+    my ($storecfg, $drive) = @_;
+
+    my $disk_path = PVE::Storage::abs_filesystem_path($storecfg, $drive->{file}, 1);
+    my $img_json = PVE::Storage::Common::qemu_img_info($disk_path);
+    die "failed to query file information with qemu-img\n" if !$img_json;
+    my $img_info = eval { decode_json($img_json) };
+    die "could not parse qemu-img info command output for '$disk_path' - $@\n" if $@;
+
+    my $cluster_size = $img_info->{'cluster-size'} // 65536;
+    my $refcount_bits = $img_info->{'format-specific'}->{data}->{'refcount-bits'} // 16;
+    my $has_extended_l2 = $img_info->{'format-specific'}->{data}->{'extended-l2'} // 0;
+
+    my $l2_multiplier = $has_extended_l2 ? 16 : 8;
+    my $l2_cache_size = ($drive->{size} * $l2_multiplier) / $cluster_size;
+
+    $l2_cache_size = 2 * $cluster_size if $l2_cache_size < (2 * $cluster_size);
+    $l2_cache_size = ceil($l2_cache_size / $cluster_size) * $cluster_size;
+
+    my $refcount_cache_size = ($drive->{size} * $refcount_bits) / (8 * $cluster_size);
+
+    $refcount_cache_size = 4 * $cluster_size if $refcount_cache_size < (4 * $cluster_size);
+    $refcount_cache_size = ceil($refcount_cache_size / $cluster_size) * $cluster_size;
+
+    return $l2_cache_size + $refcount_cache_size;
+}
+
+sub qcow2_cache_options {
+    my ($storecfg, $drive, $drive_id, $format) = @_;
+
+    if ($format ne 'qcow2') {
+        for my $qcow2_cache_option (
+            'qcow2-cache-size',
+            'qcow2-cache-size-based-on-disk',
+            'qcow2-l2-cache-size',
+            'qcow2-l2-cache-entry-size',
+            'qcow2-refcount-cache-size',
+            'qcow2-cache-clean-interval',
+        ) {
+            if (defined($drive->{$qcow2_cache_option})) {
+                log_warn("$drive_id: $qcow2_cache_option requires disk format qcow2");
+            }
+        }
+
+        return ();
+    }
+
+    my $options = [];
+
+    if (
+        defined($drive->{'qcow2-cache-size-based-on-disk'})
+        && $drive->{'qcow2-cache-size-based-on-disk'}
+    ) {
+        my $cache_size = qcow2_cache_size_by_disk_size($storecfg, $drive);
+        push @$options, ['cache-size', int($cache_size)];
+    } elsif (defined($drive->{'qcow2-cache-size'})) {
+        push @$options, ['cache-size', int($drive->{'qcow2-cache-size'}) * 1024 * 1024];
+    }
+
+    if (defined($drive->{'qcow2-l2-cache-size'})) {
+        push @$options, ['l2-cache-size', int($drive->{'qcow2-l2-cache-size'}) * 1024 * 1024];
+    }
+
+    if (defined($drive->{'qcow2-l2-cache-entry-size'})) {
+        push @$options, ['l2-cache-entry-size', int($drive->{'qcow2-l2-cache-entry-size'})];
+    }
+
+    if (defined($drive->{'qcow2-refcount-cache-size'})) {
+        push @$options, [
+            'refcount-cache-size', int($drive->{'qcow2-refcount-cache-size'}) * 1024 * 1024,
+        ];
+    }
+
+    if (defined($drive->{'qcow2-cache-clean-interval'})) {
+        push @$options, ['cache-clean-interval', int($drive->{'qcow2-cache-clean-interval'})];
+    }
+
+    return @$options;
+}
+
 1;
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH pve-manager v2 2/3] partially fix #1989: ui: qemu: disk: add qcow2 cache size config
  2026-07-20  9:46 [PATCH docs/manager/qemu-server v2 0/3] partially fix #1989: add QEMU qcow2 cache options Erik Fastermann
  2026-07-20  9:46 ` [PATCH qemu-server v2 1/3] partially fix #1989: disk: add " Erik Fastermann
@ 2026-07-20  9:46 ` Erik Fastermann
  2026-07-20  9:46 ` [PATCH pve-docs v2 3/3] partially fix #1989: qm: document " Erik Fastermann
  2 siblings, 0 replies; 4+ messages in thread
From: Erik Fastermann @ 2026-07-20  9:46 UTC (permalink / raw)
  To: pve-devel; +Cc: Erik Fastermann

Add the option to control the size of the qcow2 cache in the vm disk
create and edit dialogs. This can provide significant performance gains
in some vm configurations.

For a detailed explanation see the QEMU docs [0]. Currently only the
cache-size is configurable in the frontend, either directly or based on
the size of the disk.

[0] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/qcow2-cache.txt

Signed-off-by: Erik Fastermann <e.fastermann@proxmox.com>
---

 changes since v1:
 * simplified qcow2CacheSizeBasedOnDisk usage by binding the value
   (sadly the workaround for isQcow2 is still required)
 * added tooltips for the qcow2 cache options

 www/manager6/Parser.js      |  2 +-
 www/manager6/qemu/HDEdit.js | 73 +++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js
index 36df8e9d..f8d2bdad 100644
--- a/www/manager6/Parser.js
+++ b/www/manager6/Parser.js
@@ -207,7 +207,7 @@ Ext.define('PVE.Parser', {
                 if (!p || p.match(/^\s*$/)) {
                     return undefined; // continue
                 }
-                let match = p.match(/^([a-z_]+)=(\S+)$/);
+                let match = p.match(/^([0-9a-z-]+)=(\S+)$/);
                 if (!match) {
                     if (!p.match(/[=]/)) {
                         res.file = p;
diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js
index 1bb2bfda..fafa0f55 100644
--- a/www/manager6/qemu/HDEdit.js
+++ b/www/manager6/qemu/HDEdit.js
@@ -18,6 +18,8 @@ Ext.define('PVE.qemu.HDInputPanel', {
             isSCSI: false,
             isVirtIO: false,
             isSCSISingle: false,
+            isQcow2: false,
+            qcow2CacheSizeBasedOnDisk: false,
         },
     },
 
@@ -54,6 +56,15 @@ Ext.define('PVE.qemu.HDInputPanel', {
                     vm.set('isSCSISingle', value === 'virtio-scsi-single');
                 },
             },
+            'field[name=diskformat]': {
+                change: 'onDiskFormatChange',
+                // afterrender is needed for setting the initial value
+                afterrender: 'onDiskFormatChange',
+            },
+        },
+
+        onDiskFormatChange: function (field) {
+            this.getViewModel().set('isQcow2', field.getValue() === 'qcow2');
         },
 
         init: function (view) {
@@ -88,6 +99,20 @@ Ext.define('PVE.qemu.HDInputPanel', {
             me.drive.format = values.diskformat;
         }
 
+        if (me.drive?.format === 'qcow2' || me.drive?.file?.endsWith('.qcow2')) {
+            if (values.qcow2CacheSize) {
+                me.drive['qcow2-cache-size'] = values.qcow2CacheSize;
+            } else {
+                delete me.drive['qcow2-cache-size'];
+            }
+        }
+
+        PVE.Utils.propertyStringSet(
+            me.drive,
+            values.qcow2CacheSizeBasedOnDisk,
+            'qcow2-cache-size-based-on-disk',
+        );
+
         PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0');
         PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no');
         PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on');
@@ -156,6 +181,8 @@ Ext.define('PVE.qemu.HDInputPanel', {
         values.iothread = PVE.Parser.parseBoolean(drive.iothread);
         values.readOnly = PVE.Parser.parseBoolean(drive.ro);
         values.aio = drive.aio || '__default__';
+        values.qcow2CacheSizeBasedOnDisk = drive['qcow2-cache-size-based-on-disk'];
+        values.qcow2CacheSize = drive['qcow2-cache-size'];
 
         values.mbps_rd = drive.mbps_rd;
         values.mbps_wr = drive.mbps_wr;
@@ -167,6 +194,11 @@ Ext.define('PVE.qemu.HDInputPanel', {
         values.iops_wr_max = drive.iops_wr_max;
 
         me.setValues(values);
+
+        me.getViewModel().set(
+            'isQcow2',
+            values.diskformat === 'qcow2' || values.hdimage?.endsWith('.qcow2'),
+        );
     },
 
     setNodename: function (nodename) {
@@ -355,6 +387,47 @@ Ext.define('PVE.qemu.HDInputPanel', {
                     disabled: '{!isVirtIO && !isSCSI}',
                 },
             },
+            {
+                xtype: 'numberfield',
+                name: 'qcow2CacheSize',
+                minValue: 1,
+                fieldLabel: gettext('qcow2 cache') + ' (MiB)',
+                emptyText: gettext('Default cache size'),
+                autoEl: {
+                    tag: 'div',
+                    'data-qtip': gettext(
+                        'Total size of the qcow2 cache in MiB. A larger cache can' +
+                            ' improve I/O performance on large images. Leave empty to' +
+                            ' use the QEMU default (32 MiB L2 cache and 256 KiB' +
+                            ' refcount cache with the default disk settings).',
+                    ),
+                },
+                bind: {
+                    disabled: '{!isQcow2 || qcow2CacheSizeBasedOnDisk}',
+                },
+                listeners: {
+                    disable: (field) => field.setValue(null),
+                },
+            },
+            {
+                xtype: 'proxmoxcheckbox',
+                name: 'qcow2CacheSizeBasedOnDisk',
+                defaultValue: 0,
+                fieldLabel: gettext('qcow2 cache based on disk'),
+                clearOnDisable: true,
+                autoEl: {
+                    tag: 'div',
+                    'data-qtip': gettext(
+                        'Automatically size the qcow2 cache to cover the whole disk.' +
+                            ' Uses roughly 160 KiB of memory per GiB of disk with' +
+                            ' the default disk settings.',
+                    ),
+                },
+                bind: {
+                    value: '{qcow2CacheSizeBasedOnDisk}',
+                    disabled: '{!isQcow2}',
+                },
+            },
         );
 
         advancedColumn2.push(
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH pve-docs v2 3/3] partially fix #1989: qm: document qcow2 cache size config
  2026-07-20  9:46 [PATCH docs/manager/qemu-server v2 0/3] partially fix #1989: add QEMU qcow2 cache options Erik Fastermann
  2026-07-20  9:46 ` [PATCH qemu-server v2 1/3] partially fix #1989: disk: add " Erik Fastermann
  2026-07-20  9:46 ` [PATCH pve-manager v2 2/3] partially fix #1989: ui: qemu: disk: add qcow2 cache size config Erik Fastermann
@ 2026-07-20  9:46 ` Erik Fastermann
  2 siblings, 0 replies; 4+ messages in thread
From: Erik Fastermann @ 2026-07-20  9:46 UTC (permalink / raw)
  To: pve-devel; +Cc: Erik Fastermann

Add a new subsection to document the new qcow2 cache config options.

Signed-off-by: Erik Fastermann <e.fastermann@proxmox.com>
---

 changes since v1:
 * documented the based on disk calculation for the default case
 * also some small rewording

 qm.adoc | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/qm.adoc b/qm.adoc
index 93372c5..da5122c 100644
--- a/qm.adoc
+++ b/qm.adoc
@@ -341,6 +341,19 @@ underlying storage. Another benefit is reduced latency (hangs) in the guest for
 very I/O-intensive host workloads, since neither the main thread nor a vCPU
 thread can be blocked by disk I/O.
 
+[[qm_hard_disk_qcow2_cache]]
+Cache Size (qcow2)
+^^^^^^^^^^^^^^^^^^
+Fine-tuning the qcow2 L2/refcount cache behavior can provide significant
+performance gains. See the
+https://gitlab.com/qemu-project/qemu/-/blob/master/docs/qcow2-cache.txt[QEMU documentation]
+for a detailed explanation of these options. You can either set the cache size
+directly or let {pve} choose it automatically based on the disk image size. When
+sizing the cache automatically, with the default 64 KiB cluster size, 16
+refcount bits and no extended L2 entries, the cache uses at most roughly 160 KiB
+of memory per GiB of disk. The remaining cache options can only be configured
+through the API.
+
 [[qm_cpu]]
 CPU
 ~~~
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-20  9:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20  9:46 [PATCH docs/manager/qemu-server v2 0/3] partially fix #1989: add QEMU qcow2 cache options Erik Fastermann
2026-07-20  9:46 ` [PATCH qemu-server v2 1/3] partially fix #1989: disk: add " Erik Fastermann
2026-07-20  9:46 ` [PATCH pve-manager v2 2/3] partially fix #1989: ui: qemu: disk: add qcow2 cache size config Erik Fastermann
2026-07-20  9:46 ` [PATCH pve-docs v2 3/3] partially fix #1989: qm: document " Erik Fastermann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal