diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index c3526439bf..2ea440e1a1 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -3284,6 +3284,13 @@ paravirtualized driver is specified via the ``disk`` element. format driver of the ``qemu`` hypervisor can be controlled via the ``max_size`` subelement (see example below). + The optional ``discard_no_unref`` attribute can be set to control the way + the ``qemu`` hypervisor handles guest discard commands inside the qcow2 + image. When enabled, a discard request from within the guest will mark the + qcow2 cluster as zero, but will keep the reference/offset of that cluster. + But it will still pass the discard further to the lower layer. + This will resolve fragmentation within the qcow2 image. :since:`Since 9.5.0` + In the majority of cases the default configuration used by the hypervisor is sufficient so modifying this setting should not be necessary. For specifics on how the metadata cache of ``qcow2`` in ``qemu`` behaves refer diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1fde53ff4c..4121b6a054 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7826,6 +7826,10 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, if (virXMLPropUInt(cur, "queue_size", 10, VIR_XML_PROP_NONE, &def->queue_size) < 0) return -1; + if (virXMLPropTristateSwitch(cur, "discard_no_unref", VIR_XML_PROP_NONE, + &def->discard_no_unref) < 0) + return -1; + return 0; } @@ -22501,6 +22505,10 @@ virDomainDiskDefFormatDriver(virBuffer *buf, virBufferAsprintf(&attrBuf, " detect_zeroes='%s'", virDomainDiskDetectZeroesTypeToString(disk->detect_zeroes)); + if (disk->discard_no_unref) + virBufferAsprintf(&attrBuf, " discard_no_unref='%s'", + virTristateSwitchTypeToString(disk->discard_no_unref)); + if (disk->queues) virBufferAsprintf(&attrBuf, " queues='%u'", disk->queues); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 629e32c39f..cddaa3824d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -606,6 +606,7 @@ struct _virDomainDiskDef { virDomainDiskDiscard discard; unsigned int iothread; /* unused = 0, > 0 specific thread # */ virDomainDiskDetectZeroes detect_zeroes; + virTristateSwitch discard_no_unref; char *domain_name; /* backend domain name */ unsigned int queues; unsigned int queue_size; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 7e3c920a38..6c4cfa16d6 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -921,6 +921,20 @@ virDomainDiskDefValidate(const virDomainDef *def, return -1; } + if (disk->discard_no_unref == VIR_TRISTATE_SWITCH_ON) { + if (disk->src->format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'discard_no_unref' only works with qcow2 disk format")); + return -1; + } + + if (disk->src->readonly) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'discard_no_unref' is not compatible with read-only disk")); + return -1; + } + } + return 0; } diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 4b595fd066..fcf9e00600 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -2516,6 +2516,11 @@ + + + + + diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 99061e4df7..dcac3a8ff6 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -809,6 +809,7 @@ virStorageSourceCopy(const virStorageSource *src, def->cachemode = src->cachemode; def->discard = src->discard; def->detect_zeroes = src->detect_zeroes; + def->discard_no_unref = src->discard_no_unref; def->sslverify = src->sslverify; def->readahead = src->readahead; def->timeout = src->timeout; diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index c6187dda59..f13e7c756a 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -399,6 +399,7 @@ struct _virStorageSource { int cachemode; /* enum virDomainDiskCache */ int discard; /* enum virDomainDiskDiscard */ int detect_zeroes; /* enum virDomainDiskDetectZeroes */ + virTristateSwitch discard_no_unref; bool floppyimg; /* set to true if the storage source is going to be used as a source for floppy drive */ diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 8b2159f845..dcdf883926 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -1117,12 +1117,11 @@ qemuBlockStorageSourceGetFormatQcow2Props(virStorageSource *src, * see: qemu.git/docs/qcow2-cache.txt * https://git.qemu.org/?p=qemu.git;a=blob;f=docs/qcow2-cache.txt */ - if (src->metadataCacheMaxSize > 0) { - if (virJSONValueObjectAdd(&props, - "U:cache-size", src->metadataCacheMaxSize, - NULL) < 0) - return -1; - } + if (virJSONValueObjectAdd(&props, + "P:cache-size", src->metadataCacheMaxSize, + "T:discard-no-unref", src->discard_no_unref, + NULL) < 0) + return -1; return 0; } diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c65c571398..94587638c3 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -11039,6 +11039,7 @@ qemuDomainPrepareDiskSourceData(virDomainDiskDef *disk, src->iomode = disk->iomode; src->cachemode = disk->cachemode; src->discard = disk->discard; + src->discard_no_unref = disk->discard_no_unref; if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) src->floppyimg = true; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c4bd766531..f20544590d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14242,8 +14242,10 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, * into the topmost virStorage source of the disk chain. * Since 'mirror' has the ambition to replace it we need to propagate * it into the mirror too. We do it directly as otherwise we'd need - * to modify all callers of 'qemuDomainPrepareStorageSourceBlockdev' */ + * to modify all callers of 'qemuDomainPrepareStorageSourceBlockdev' + * Same for discard_no_unref */ mirror->detect_zeroes = disk->detect_zeroes; + mirror->discard_no_unref = disk->discard_no_unref; /* If reusing an external image that includes a backing file but the user * did not enumerate the chain in the XML we need to detect the chain */ diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 04d0c9df73..a53729d349 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3266,6 +3266,13 @@ qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk, return -1; } + if (disk->discard_no_unref != VIR_TRISTATE_SWITCH_ABSENT && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_QCOW2_DISCARD_NO_UNREF)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'discard_no_unref' is not supported by this QEMU binary")); + return -1; + } + for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) { if (qemuDomainValidateStorageSource(n, qemuCaps) < 0) return -1; diff --git a/src/vz/vz_utils.c b/src/vz/vz_utils.c index 2db1146149..7db7dbd419 100644 --- a/src/vz/vz_utils.c +++ b/src/vz/vz_utils.c @@ -347,6 +347,12 @@ vzCheckDiskUnsupportedParams(virDomainDiskDef *disk) return -1; } + if (disk->discard_no_unref) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Disk discard_no_unref is not supported by vz driver.")); + return -1; + } + if (disk->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting up disk startup policy is not " diff --git a/tests/qemuxml2argvdata/disk-discard_no_unref.x86_64-latest.args b/tests/qemuxml2argvdata/disk-discard_no_unref.x86_64-latest.args new file mode 100644 index 0000000000..25b8211cf3 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-discard_no_unref.x86_64-latest.args @@ -0,0 +1,36 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-test \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-test/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-test/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-test/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=test,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-test/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=1048576k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":1073741824}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid 92d7a226-cfae-425b-a6d3-00bbf9ec5c9e \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot menu=on,strict=on \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \ +-blockdev '{"driver":"file","filename":"/var/lib/libvirt/images/f14.img","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-1-format","read-only":false,"discard":"unmap","driver":"qcow2","discard-no-unref":true,"file":"libvirt-1-storage"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x4","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":2}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x2"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxml2argvdata/disk-discard_no_unref.xml b/tests/qemuxml2argvdata/disk-discard_no_unref.xml new file mode 100644 index 0000000000..d5dd054a85 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-discard_no_unref.xml @@ -0,0 +1,32 @@ + + test + 92d7a226-cfae-425b-a6d3-00bbf9ec5c9e + 1048576 + 1048576 + 1 + + hvm + + + + + + destroy + restart + restart + + /usr/bin/qemu-system-x86_64 + + +
+ + + + + + + + + + diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 0b5a2475c9..c1bba779b3 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1245,6 +1245,7 @@ mymain(void) DO_TEST_CAPS_LATEST("disk-copy_on_read"); DO_TEST_CAPS_LATEST("disk-discard"); DO_TEST_CAPS_LATEST("disk-detect-zeroes"); + DO_TEST_CAPS_LATEST("disk-discard_no_unref"); DO_TEST_CAPS_LATEST("disk-snapshot"); DO_TEST_CAPS_LATEST_PARSE_ERROR("disk-same-targets"); DO_TEST_CAPS_LATEST_PARSE_ERROR("disk-missing-target-invalid"); diff --git a/tests/qemuxml2xmloutdata/disk-discard_no_unref.x86_64-latest.xml b/tests/qemuxml2xmloutdata/disk-discard_no_unref.x86_64-latest.xml new file mode 100644 index 0000000000..c57acf99dc --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-discard_no_unref.x86_64-latest.xml @@ -0,0 +1,42 @@ + + test + 92d7a226-cfae-425b-a6d3-00bbf9ec5c9e + 1048576 + 1048576 + 1 + + hvm + + + + + + qemu64 + + + destroy + restart + restart + + /usr/bin/qemu-system-x86_64 + + + + +
+ + +
+ + +
+ + + + +