diff --git a/.gitignore b/.gitignore index 60a5670..fc71b27 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -libvirt-10.5.0.tar.xz +libvirt-11.10.0.tar.xz diff --git a/libvirt-Expose-latency-histograms-via-virConnectGetAllDomainStats.patch b/libvirt-Expose-latency-histograms-via-virConnectGetAllDomainStats.patch new file mode 100644 index 0000000..fd28497 --- /dev/null +++ b/libvirt-Expose-latency-histograms-via-virConnectGetAllDomainStats.patch @@ -0,0 +1,248 @@ +From 48f5933f6cd6f53997823cfe2a277b822b00264f Mon Sep 17 00:00:00 2001 +Message-ID: <48f5933f6cd6f53997823cfe2a277b822b00264f.1771336681.git.jdenemar@redhat.com> +From: Peter Krempa +Date: Thu, 29 Jan 2026 18:10:26 +0100 +Subject: [PATCH] Expose latency histograms via 'virConnectGetAllDomainStats' + +Add documentation and constants for constructing the stats field names +for latency histograms and expose them in the qemu driver: + +Example: + + block.1.latency_histogram.read.bin.count=9 + block.1.latency_histogram.read.bin.0.start=0 + block.1.latency_histogram.read.bin.0.value=0 + block.1.latency_histogram.read.bin.1.start=10 + block.1.latency_histogram.read.bin.1.value=0 + block.1.latency_histogram.read.bin.2.start=100 + block.1.latency_histogram.read.bin.2.value=0 + block.1.latency_histogram.read.bin.3.start=1000 + block.1.latency_histogram.read.bin.3.value=1047 + block.1.latency_histogram.read.bin.4.start=10000 + block.1.latency_histogram.read.bin.4.value=2131 + block.1.latency_histogram.read.bin.5.start=100000 + block.1.latency_histogram.read.bin.5.value=0 + block.1.latency_histogram.read.bin.6.start=1000000 + block.1.latency_histogram.read.bin.6.value=0 + block.1.latency_histogram.read.bin.7.start=10000000 + block.1.latency_histogram.read.bin.7.value=0 + block.1.latency_histogram.read.bin.8.start=100000000 + block.1.latency_histogram.read.bin.8.value=0 + +Signed-off-by: Peter Krempa +Reviewed-by: Michal Privoznik +(cherry picked from commit 237e49127a9390f054e33e689ba9db1587cdc9f1) + +https://issues.redhat.com/browse/RHEL-147866 [rhel-9.8] +https://issues.redhat.com/browse/RHEL-131335 [rhel-10.2] +--- + docs/manpages/virsh.rst | 7 ++ + include/libvirt/libvirt-domain.h | 113 +++++++++++++++++++++++++++++++ + src/qemu/qemu_driver.c | 43 ++++++++++++ + 3 files changed, 163 insertions(+) + +diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst +index a9d691824e..ff0cf1a715 100644 +--- a/docs/manpages/virsh.rst ++++ b/docs/manpages/virsh.rst +@@ -2811,6 +2811,13 @@ Information listed includes: + pending write operations in the defined interval + * ``block..timed_group..zone_append_queue_depth_avg`` - average number + of pending zone append operations in the defined interval ++* ``block..latency_histogram..bin.count`` - number of bins in ++ latency histogram. is one of ``read``, ``write``, ``zone_append``, or ++ ``flush`` ++* ``block..latency_histogram..bin..start`` start boundary of ++ a latency histogram bin in nanoseconds of given operation duration ++* ``block..latency_histogram..bin..value`` current number of ++ events corresponding to the given bin and type + + + *--iothread* returns information about IOThreads on the running guest +diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h +index 16fac6b085..8e62bd23d4 100644 +--- a/include/libvirt/libvirt-domain.h ++++ b/include/libvirt/libvirt-domain.h +@@ -3815,6 +3815,119 @@ struct _virDomainStatsRecord { + */ + # define VIR_DOMAIN_STATS_BLOCK_SUFFIX_TIMED_GROUP_SUFFIX_ZONE_APPEND_QUEUE_DEPTH_AVG ".zone_append_queue_depth_avg" + ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_READ_PREFIX: ++ * ++ * The parameter name prefix to access 'read' latency histograms. Concatenate ++ * the prefix with either: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT ++ * to get the number of bins in given histogram ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX and ++ * entry number formatted as an unsigned integer and one of the latency ++ * histogram suffix parameters to compelte a full bin parameter name ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_READ_PREFIX ".latency_histogram.read." ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_WRITE_PREFIX: ++ * ++ * The parameter name prefix to access 'write' latency histograms. Concatenate ++ * the prefix with either: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT ++ * to get the number of bins in given histogram ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX and ++ * entry number formatted as an unsigned integer and one of the latency ++ * histogram suffix parameters to compelte a full bin parameter name ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_WRITE_PREFIX ".latency_histogram.write." ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_ZONE_APPEND_PREFIX: ++ * ++ * The parameter name prefix to access 'zone_append' latency histograms. Concatenate ++ * the prefix with either: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT ++ * to get the number of bins in given histogram ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX and ++ * entry number formatted as an unsigned integer and one of the latency ++ * histogram suffix parameters to compelte a full bin parameter name ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_ZONE_APPEND_PREFIX ".latency_histogram.zone_append." ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_FLUSH_PREFIX: ++ * ++ * The parameter name prefix to access 'flush' latency histograms. Concatenate ++ * the prefix with either: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT ++ * to get the number of bins in given histogram ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX and ++ * entry number formatted as an unsigned integer and one of the latency ++ * histogram suffix parameters to compelte a full bin parameter name ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_FLUSH_PREFIX ".latency_histogram.flush." ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT: ++ * ++ * The parameter name suffix to access number of bins in one of the following ++ * latency histogram types: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_READ_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_WRITE_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_ZONE_APPEND_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_FLUSH_PREFIX ++ * ++ * Number of bins in latency histogram as unsigned long long. ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT "bin.count" ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX: ++ * ++ * The parameter name suffix to access a latency histogram bin in one of the ++ * following latency histogram types: ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_READ_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_WRITE_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_ZONE_APPEND_PREFIX ++ * - VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_FLUSH_PREFIX ++ * ++ * Concatenate with a bin number as unsigned int and one of the other field ++ * suffixes to access bin parameters. ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX "bin." ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_START: ++ * ++ * Start of the current latency histogram bin in nanoseconds as unsigned long long. ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_START ".start" ++ ++/** ++ * VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_VALUE: ++ * ++ * Current value of the number of occurences of the latency within this bin ++ * as unsigned long long. ++ * ++ * Since: 12.1.0 ++ */ ++# define VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_VALUE ".value" ++ ++ + /** + * VIR_DOMAIN_STATS_PERF_CMT: + * +diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c +index 08a547c546..f3e7410f9e 100644 +--- a/src/qemu/qemu_driver.c ++++ b/src/qemu/qemu_driver.c +@@ -17597,6 +17597,36 @@ qemuDomainGetStatsBlockExportBackendStorage(const char *entryname, + } + + ++static void ++qemuDomainGetStatsBlockExportFrontendLatencyHistogram(struct qemuBlockStatsLatencyHistogram *h, ++ size_t disk_idx, ++ const char *prefix_hist, ++ virTypedParamList *par) ++{ ++ size_t i; ++ ++ if (!h) ++ return; ++ ++ virTypedParamListAddULLong(par, h->nbins, ++ VIR_DOMAIN_STATS_BLOCK_PREFIX "%zu%s" VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_COUNT, ++ disk_idx, prefix_hist); ++ ++ for (i = 0; i < h->nbins; i++) { ++ virTypedParamListAddULLong(par, h->bins[i].start, ++ VIR_DOMAIN_STATS_BLOCK_PREFIX "%zu%s" ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX "%zu" ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_START, ++ disk_idx, prefix_hist, i); ++ virTypedParamListAddULLong(par, h->bins[i].value, ++ VIR_DOMAIN_STATS_BLOCK_PREFIX "%zu%s" ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_PREFIX "%zu" ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_SUFFIX_BIN_SUFFIX_VALUE, ++ disk_idx, prefix_hist, i); ++ } ++} ++ ++ + static void + qemuDomainGetStatsBlockExportFrontend(const char *frontendname, + GHashTable *stats, +@@ -17721,6 +17751,19 @@ qemuDomainGetStatsBlockExportFrontend(const char *frontendname, + idx, i); + } + } ++ ++ qemuDomainGetStatsBlockExportFrontendLatencyHistogram(en->histogram_read, idx, ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_READ_PREFIX, ++ par); ++ qemuDomainGetStatsBlockExportFrontendLatencyHistogram(en->histogram_write, idx, ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_WRITE_PREFIX, ++ par); ++ qemuDomainGetStatsBlockExportFrontendLatencyHistogram(en->histogram_zone, idx, ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_ZONE_APPEND_PREFIX, ++ par); ++ qemuDomainGetStatsBlockExportFrontendLatencyHistogram(en->histogram_flush, idx, ++ VIR_DOMAIN_STATS_BLOCK_SUFFIX_LATENCY_HISTOGRAM_FLUSH_PREFIX, ++ par); + } + + +-- +2.53.0 diff --git a/libvirt-Introduce-support-for-disk-operation-latency-histogram-collection.patch b/libvirt-Introduce-support-for-disk-operation-latency-histogram-collection.patch new file mode 100644 index 0000000..c417f58 --- /dev/null +++ b/libvirt-Introduce-support-for-disk-operation-latency-histogram-collection.patch @@ -0,0 +1,422 @@ +From 8fbea435edb1635ec98c9419e9249223e5c3b2b6 Mon Sep 17 00:00:00 2001 +Message-ID: <8fbea435edb1635ec98c9419e9249223e5c3b2b6.1771336682.git.jdenemar@redhat.com> +From: Peter Krempa +Date: Fri, 23 Jan 2026 17:09:27 +0100 +Subject: [PATCH] Introduce support for disk operation latency histogram + collection + +Add config and docs allowing enabling latency histogram collection for +block device operations. + +This patch sets up the docs, schema and XML infrastructure. + +Signed-off-by: Peter Krempa +Reviewed-by: Michal Privoznik +(cherry picked from commit b874c944bd8c4ffa6c51394557587c8c203f1656) + +https://issues.redhat.com/browse/RHEL-147866 [rhel-9.8] +https://issues.redhat.com/browse/RHEL-131335 [rhel-10.2] +--- + docs/formatdomain.rst | 41 ++++++ + src/conf/domain_conf.c | 133 +++++++++++++++++- + src/conf/domain_conf.h | 7 + + src/conf/schemas/domaincommon.rng | 37 ++++- + ...isk-statistics-intervals.x86_64-latest.xml | 29 ++++ + .../disk-statistics-intervals.xml | 25 ++++ + 6 files changed, 262 insertions(+), 10 deletions(-) + +diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst +index 70882c6820..31232deb3c 100644 +--- a/docs/formatdomain.rst ++++ b/docs/formatdomain.rst +@@ -3628,6 +3628,47 @@ paravirtualized driver is specified via the ``disk`` element. + + :since:`Since 11.9.0 (QEMU 10.2, virtio, ide, scsi disks only)`. + ++ Block operation latency histogram collection can be configured using ++ ```` sub-element. The histogram is collected for ++ the whole runtime of the VM, but can be re-started or reconfigured using ++ the `virDomainUpdateDeviceFlags `__ ++ API. Using the same config re-starts histogram collection. ++ ++ The optional ``type`` attribute configures specific operation to collect ++ the histogram for. Supported types are ``read``, ``write``, ``zone``, and ++ ``flush``. If the ``type`` attribute is omitted the histogram collection ++ bins bins apply to all of the aforementioned types, which can be overriden ++ with specific config. ++ ++ The ```` has multiple mandatory ```` sub-elements ++ with mandatory ``start`` attribute configuring the starting boundary of ++ the histogram bin configured in nanosecods of the operation duration and ++ the intervals must be properly ordered and non-duplicate. ++ ++ Example:: ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ [or for specific operation types] ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ :since:`Since 12.1.0`. ++ + - The optional ``queues`` attribute specifies the number of virt queues for + virtio-blk ( :since:`Since 3.9.0` ) or vhost-user-blk + ( :since:`Since 7.1.0` ) +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index f5c4d135a9..83c58ab5ff 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -2445,6 +2445,11 @@ virDomainDiskDefFree(virDomainDiskDef *def) + virObjectUnref(def->privateData); + g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainIothreadMappingDefFree); + g_free(def->statistics); ++ g_free(def->histogram_boundaries); ++ g_free(def->histogram_boundaries_read); ++ g_free(def->histogram_boundaries_write); ++ g_free(def->histogram_boundaries_zone); ++ g_free(def->histogram_boundaries_flush); + + if (def->throttlefilters) { + size_t i; +@@ -8307,6 +8312,91 @@ virDomainIothreadMappingDefParse(xmlNodePtr driverNode, + } + + ++static int ++virDomainDiskDefDriverParseXMLHistogramOne(virDomainDiskDef *def, ++ xmlNodePtr cur) ++{ ++ g_autofree char *histogram_type = NULL; ++ unsigned int **histogram_config = NULL; ++ g_autoptr(GPtrArray) binNodes = virXMLNodeGetSubelementList(cur, "bin"); ++ size_t nbins = 0; ++ size_t i; ++ ++ if ((histogram_type = virXMLPropString(cur, "type"))) { ++ if (STREQ(histogram_type, "read")) { ++ histogram_config = &def->histogram_boundaries_read; ++ } else if (STREQ(histogram_type, "write")) { ++ histogram_config = &def->histogram_boundaries_write; ++ } else if (STREQ(histogram_type, "zone")) { ++ histogram_config = &def->histogram_boundaries_zone; ++ } else if (STREQ(histogram_type, "flush")) { ++ histogram_config = &def->histogram_boundaries_flush; ++ } else { ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, ++ _("unknown latency_histogram type '%1$s'"), ++ histogram_type); ++ return -1; ++ } ++ } else { ++ histogram_config = &def->histogram_boundaries; ++ } ++ ++ if (*histogram_config) { ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", ++ _("only one latency-histogram of a given type is supported")); ++ return -1; ++ } ++ ++ if (binNodes->len == 0) { ++ virReportError(VIR_ERR_XML_ERROR, "%s", ++ _("missing 'bin' elements for 'latency-histogram'")); ++ return -1; ++ } ++ ++ *histogram_config = g_new0(unsigned int, binNodes->len + 1); ++ ++ for (i = 0; i < binNodes->len; i++) { ++ unsigned int val; ++ ++ if (virXMLPropUInt(g_ptr_array_index(binNodes, i), ++ "start", 10, ++ VIR_XML_PROP_REQUIRED, ++ &val) < 0) ++ return -1; ++ ++ if (nbins > 0 && ++ (val == 0 || ++ val <= (*histogram_config)[nbins-1])) { ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", ++ _("the values of 'start' attribute of a 'latency-histogram' 'bin' configuration must be sorted and non-overlapping")); ++ return -1; ++ } ++ ++ if (val > 0) ++ (*histogram_config)[nbins++] = val; ++ } ++ ++ return 0; ++} ++ ++ ++static int ++virDomainDiskDefDriverParseXMLHistograms(virDomainDiskDef *def, ++ xmlNodePtr cur) ++{ ++ g_autoptr(GPtrArray) histogramNodes = virXMLNodeGetSubelementList(cur, "latency-histogram"); ++ size_t i; ++ ++ for (i = 0; i < histogramNodes->len; i++) { ++ if (virDomainDiskDefDriverParseXMLHistogramOne(def, ++ g_ptr_array_index(histogramNodes, i)) < 0) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ + static int + virDomainDiskDefDriverParseXML(virDomainDiskDef *def, + xmlNodePtr cur) +@@ -8380,6 +8470,9 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, + return -1; + } + } ++ ++ if (virDomainDiskDefDriverParseXMLHistograms(def, statisticsNode) < 0) ++ return -1; + } + + if (virXMLPropEnum(cur, "detect_zeroes", +@@ -23961,12 +24054,37 @@ virDomainDiskDefFormatThrottleFilters(virBuffer *buf, + } + + ++static void ++virDomainDiskDefFormatDriverHistogram(virBuffer *buf, ++ const char *type, ++ unsigned int *bins) ++{ ++ g_auto(virBuffer) histogramAttrBuf = VIR_BUFFER_INITIALIZER; ++ g_auto(virBuffer) histogramChildBuf = VIR_BUFFER_INIT_CHILD(buf); ++ ++ if (!bins || bins[0] == 0) ++ return; ++ ++ if (type) ++ virBufferAsprintf(&histogramAttrBuf, " type='%s'", type); ++ ++ /* we dont store the start boundary of the first bin but it's always there */ ++ virBufferAddLit(&histogramChildBuf, "\n"); ++ ++ for (; *bins > 0; bins++) ++ virBufferAsprintf(&histogramChildBuf, "\n", *bins); ++ ++ virXMLFormatElement(buf, "latency-histogram", &histogramAttrBuf, &histogramChildBuf); ++} ++ ++ + static void + virDomainDiskDefFormatDriver(virBuffer *buf, + virDomainDiskDef *disk) + { + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); ++ g_auto(virBuffer) statisticsChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf); + + virBufferEscapeString(&attrBuf, " name='%s'", virDomainDiskGetDriver(disk)); + +@@ -24038,16 +24156,25 @@ virDomainDiskDefFormatDriver(virBuffer *buf, + virDomainIothreadMappingDefFormat(&childBuf, disk->iothreads); + + if (disk->statistics) { +- g_auto(virBuffer) statisticsChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf); + size_t i; + + for (i = 0; disk->statistics[i] > 0; i++) + virBufferAsprintf(&statisticsChildBuf, "\n", + disk->statistics[i]); +- +- virXMLFormatElement(&childBuf, "statistics", NULL, &statisticsChildBuf); + } + ++ virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, NULL, ++ disk->histogram_boundaries); ++ virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "read", ++ disk->histogram_boundaries_read); ++ virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "write", ++ disk->histogram_boundaries_write); ++ virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "zone", ++ disk->histogram_boundaries_zone); ++ virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "flush", ++ disk->histogram_boundaries_flush); ++ ++ virXMLFormatElement(&childBuf, "statistics", NULL, &statisticsChildBuf); + + virXMLFormatElement(buf, "driver", &attrBuf, &childBuf); + } +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index 8f53ed96c0..b120d4a68e 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -596,6 +596,13 @@ struct _virDomainDiskDef { + GSList *iothreads; /* List of virDomainIothreadMappingDef */ + unsigned int *statistics; /* Optional, zero terminated list of intervals to + collect statistics for */ ++ /* optional zero terminated lists of bin boundaries for latency histograms */ ++ unsigned int *histogram_boundaries; ++ unsigned int *histogram_boundaries_read; ++ unsigned int *histogram_boundaries_write; ++ unsigned int *histogram_boundaries_zone; ++ unsigned int *histogram_boundaries_flush; ++ + virDomainDiskDetectZeroes detect_zeroes; + virTristateSwitch discard_no_unref; + char *domain_name; /* backend domain name */ +diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng +index 1f9ac102a0..441328a08e 100644 +--- a/src/conf/schemas/domaincommon.rng ++++ b/src/conf/schemas/domaincommon.rng +@@ -2728,13 +2728,36 @@ + + + +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ read ++ write ++ zone ++ flush ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml b/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml +index 4c55c50ef5..d02f954073 100644 +--- a/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml ++++ b/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml +@@ -22,6 +22,11 @@ + + + ++ ++ ++ ++ ++ + + + +@@ -33,6 +38,30 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/tests/qemuxmlconfdata/disk-statistics-intervals.xml b/tests/qemuxmlconfdata/disk-statistics-intervals.xml +index f5e801f5a8..5f9e9470d7 100644 +--- a/tests/qemuxmlconfdata/disk-statistics-intervals.xml ++++ b/tests/qemuxmlconfdata/disk-statistics-intervals.xml +@@ -19,6 +19,11 @@ + + + ++ ++ ++ ++ ++ + + + +@@ -29,6 +34,26 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +-- +2.53.0 diff --git a/libvirt-RHEL-ONLY-backport-test-data-for-migrate-pr-capability-of-scsi-block.patch b/libvirt-RHEL-ONLY-backport-test-data-for-migrate-pr-capability-of-scsi-block.patch new file mode 100644 index 0000000..de4bb90 --- /dev/null +++ b/libvirt-RHEL-ONLY-backport-test-data-for-migrate-pr-capability-of-scsi-block.patch @@ -0,0 +1,125 @@ +From c2eb6d70891d9be5ce13f07946841239a81c3ed9 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Peter Krempa +Date: Mon, 16 Feb 2026 15:08:54 +0100 +Subject: [PATCH] RHEL-ONLY: backport test data for 'migrate-pr' capability of + 'scsi-block' + +In upstream qemu the capability is present starting with qemu-11.0. We +don't have the test data downstream and backporting them would be too +invasive. Backport the relevant capability detection as a +downstream-only fix. + +https://issues.redhat.com/browse/RHEL-140614 [rhel-9.8] +https://issues.redhat.com/browse/RHEL-135115 [rhel-10.2] + +Signed-off-by: Peter Krempa +--- + .../caps_10.2.0_x86_64.replies | 79 ++++++++++++++++++- + .../caps_10.2.0_x86_64.xml | 1 + + 2 files changed, 76 insertions(+), 4 deletions(-) + +diff --git a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.replies b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.replies +index cb4abb4533..10db9baca1 100644 +--- a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.replies ++++ b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.replies +@@ -33081,10 +33081,81 @@ + } + + { +- "error": { +- "class": "DeviceNotFound", +- "desc": "The libvirt device dump was not collected for this version+device tuple" +- }, ++ "return": [ ++ { ++ "default-value": 4294967295, ++ "name": "scsi-id", ++ "type": "uint32" ++ }, ++ { ++ "default-value": 4294967295, ++ "name": "lun", ++ "type": "uint32" ++ }, ++ { ++ "default-value": 0, ++ "name": "channel", ++ "type": "uint32" ++ }, ++ { ++ "default-value": "auto", ++ "name": "rerror", ++ "description": "Error handling policy (report/ignore/enospc/stop/auto)", ++ "type": "BlockdevOnError" ++ }, ++ { ++ "default-value": 2147483647, ++ "name": "max_io_size", ++ "type": "uint64" ++ }, ++ { ++ "default-value": false, ++ "name": "share-rw", ++ "description": "on/off", ++ "type": "bool" ++ }, ++ { ++ "default-value": true, ++ "name": "migrate-pr", ++ "description": "on/off", ++ "type": "bool" ++ }, ++ { ++ "default-value": "auto", ++ "name": "werror", ++ "description": "Error handling policy (report/ignore/enospc/stop/auto)", ++ "type": "BlockdevOnError" ++ }, ++ { ++ "default-value": 1073741824, ++ "name": "max_unmap_size", ++ "type": "uint64" ++ }, ++ { ++ "default-value": -1, ++ "name": "scsi_version", ++ "type": "int32" ++ }, ++ { ++ "default-value": 0, ++ "name": "rotation_rate", ++ "type": "uint16" ++ }, ++ { ++ "name": "drive", ++ "description": "Node name or ID of a block device to use as a backend", ++ "type": "str" ++ }, ++ { ++ "default-value": 30, ++ "name": "io_timeout", ++ "type": "uint32" ++ }, ++ { ++ "name": "bootindex", ++ "type": "int32" ++ } ++ ], + "id": "libvirt-37" + } + +diff --git a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml +index 7cff2c2291..7d5a75ce88 100644 +--- a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml ++++ b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml +@@ -215,6 +215,7 @@ + + + ++ + 10001091 + 43100287 + v10.2.0-rc1-38-gfb241d0a1f +-- +2.53.0 diff --git a/libvirt-conf-Add-firmwareFeatures-element-for-domaincaps.patch b/libvirt-conf-Add-firmwareFeatures-element-for-domaincaps.patch new file mode 100644 index 0000000..8896e29 --- /dev/null +++ b/libvirt-conf-Add-firmwareFeatures-element-for-domaincaps.patch @@ -0,0 +1,85 @@ +From 271cfe0d7954d5398af307b24fc5b601977975b8 Mon Sep 17 00:00:00 2001 +Message-ID: <271cfe0d7954d5398af307b24fc5b601977975b8.1772815313.git.jdenemar@redhat.com> +From: Andrea Bolognani +Date: Mon, 9 Feb 2026 21:28:50 +0100 +Subject: [PATCH] conf: Add firmwareFeatures element for domaincaps + +Signed-off-by: Andrea Bolognani +Reviewed-by: Michal Privoznik +(cherry picked from commit 928bdc3e67b29ff2314ff538905703e299b1e47e) + +https://issues.redhat.com/browse/RHEL-82645 + +Signed-off-by: Andrea Bolognani +--- + src/conf/domain_capabilities.c | 15 +++++++++++++++ + src/conf/domain_capabilities.h | 8 ++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c +index 49179b97ab..9b3577cd08 100644 +--- a/src/conf/domain_capabilities.c ++++ b/src/conf/domain_capabilities.c +@@ -422,6 +422,19 @@ virDomainCapsFeatureFormatSimple(virBuffer *buf, + } + + ++static void ++virDomainCapsFirmwareFeaturesFormat(virBuffer *buf, ++ const virDomainCapsFirmwareFeatures *firmwareFeatures) ++{ ++ FORMAT_PROLOGUE(firmwareFeatures); ++ ++ ENUM_PROCESS(firmwareFeatures, secureBoot, virTristateBoolTypeToString); ++ ENUM_PROCESS(firmwareFeatures, enrolledKeys, virTristateBoolTypeToString); ++ ++ FORMAT_EPILOGUE(firmwareFeatures); ++} ++ ++ + static void + virDomainCapsLoaderFormat(virBuffer *buf, + const virDomainCapsLoader *loader) +@@ -440,12 +453,14 @@ static void + virDomainCapsOSFormat(virBuffer *buf, + const virDomainCapsOS *os) + { ++ const virDomainCapsFirmwareFeatures *firmwareFeatures = &os->firmwareFeatures; + const virDomainCapsLoader *loader = &os->loader; + + FORMAT_PROLOGUE(os); + + ENUM_PROCESS(os, firmware, virDomainOsDefFirmwareTypeToString); + ++ virDomainCapsFirmwareFeaturesFormat(&childBuf, firmwareFeatures); + virDomainCapsLoaderFormat(&childBuf, loader); + + FORMAT_EPILOGUE(os); +diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h +index b10370db8f..a68fafe235 100644 +--- a/src/conf/domain_capabilities.h ++++ b/src/conf/domain_capabilities.h +@@ -43,6 +43,13 @@ struct _virDomainCapsStringValues { + size_t nvalues; /* number of strings */ + }; + ++typedef struct _virDomainCapsFirmwareFeatures virDomainCapsFirmwareFeatures; ++struct _virDomainCapsFirmwareFeatures { ++ virTristateBool supported; ++ virDomainCapsEnum secureBoot; ++ virDomainCapsEnum enrolledKeys; ++}; ++ + STATIC_ASSERT_ENUM(VIR_DOMAIN_LOADER_TYPE_LAST); + STATIC_ASSERT_ENUM(VIR_TRISTATE_BOOL_LAST); + typedef struct _virDomainCapsLoader virDomainCapsLoader; +@@ -59,6 +66,7 @@ typedef struct _virDomainCapsOS virDomainCapsOS; + struct _virDomainCapsOS { + virTristateBool supported; + virDomainCapsEnum firmware; /* Info about virDomainOsDefFirmware */ ++ virDomainCapsFirmwareFeatures firmwareFeatures; + virDomainCapsLoader loader; /* Info about virDomainLoaderDef */ + }; + +-- +2.53.0 diff --git a/libvirt-conf-Include-varstore-element-in-domcaps.patch b/libvirt-conf-Include-varstore-element-in-domcaps.patch new file mode 100644 index 0000000..f254bed --- /dev/null +++ b/libvirt-conf-Include-varstore-element-in-domcaps.patch @@ -0,0 +1,140 @@ +From af94300604718604a70a5d587e56187ffe5e6557 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Andrea Bolognani +Date: Fri, 30 Jan 2026 17:46:30 +0100 +Subject: [PATCH] conf: Include varstore element in domcaps +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We want to advertise whether the element is usable when +defining new domains. + +Signed-off-by: Andrea Bolognani +Reviewed-by: Michal Privoznik +Acked-by: Gerd Hoffmann +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 3d6987914bb10beb11b9eb5e83ec2194dfab1659) + +https://issues.redhat.com/browse/RHEL-82645 + +Signed-off-by: Andrea Bolognani +--- + docs/formatdomaincaps.rst | 7 +++++++ + src/conf/domain_capabilities.c | 10 ++++++++++ + src/conf/domain_capabilities.h | 6 ++++++ + src/conf/schemas/domaincaps.rng | 9 +++++++++ + 4 files changed, 32 insertions(+) + +diff --git a/docs/formatdomaincaps.rst b/docs/formatdomaincaps.rst +index 3426b7c9cd..5a1d3f2670 100644 +--- a/docs/formatdomaincaps.rst ++++ b/docs/formatdomaincaps.rst +@@ -141,6 +141,7 @@ domains. + no + + ++ + + ... + +@@ -227,6 +228,12 @@ are the following: + possible to enforce Secure Boot, look at the ``enrolledKeys`` enum inside + the ```` element instead. + ++The ```` element :since:`(since 12.1.0)` indicates whether UEFI ++variable storage backed by the ``uefi-vars`` QEMU device can be used as an ++alternative to pflash-based NVRAM storage. This is the only type of variable ++storage compatible with Secure Boot on non-x86 architectures, but it can be ++used on x86 too. ++ + CPU configuration + ~~~~~~~~~~~~~~~~~ + +diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c +index 9b3577cd08..78b8e6e6e1 100644 +--- a/src/conf/domain_capabilities.c ++++ b/src/conf/domain_capabilities.c +@@ -449,12 +449,21 @@ virDomainCapsLoaderFormat(virBuffer *buf, + FORMAT_EPILOGUE(loader); + } + ++static void ++virDomainCapsVarstoreFormat(virBuffer *buf, ++ const virDomainCapsVarstore *varstore) ++{ ++ FORMAT_PROLOGUE(varstore); ++ FORMAT_EPILOGUE(varstore); ++} ++ + static void + virDomainCapsOSFormat(virBuffer *buf, + const virDomainCapsOS *os) + { + const virDomainCapsFirmwareFeatures *firmwareFeatures = &os->firmwareFeatures; + const virDomainCapsLoader *loader = &os->loader; ++ const virDomainCapsVarstore *varstore = &os->varstore; + + FORMAT_PROLOGUE(os); + +@@ -462,6 +471,7 @@ virDomainCapsOSFormat(virBuffer *buf, + + virDomainCapsFirmwareFeaturesFormat(&childBuf, firmwareFeatures); + virDomainCapsLoaderFormat(&childBuf, loader); ++ virDomainCapsVarstoreFormat(&childBuf, varstore); + + FORMAT_EPILOGUE(os); + } +diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h +index a68fafe235..02344fd9b6 100644 +--- a/src/conf/domain_capabilities.h ++++ b/src/conf/domain_capabilities.h +@@ -61,6 +61,11 @@ struct _virDomainCapsLoader { + virDomainCapsEnum secure; /* Info about secure:virTristateBool */ + }; + ++typedef struct _virDomainCapsVarstore virDomainCapsVarstore; ++struct _virDomainCapsVarstore { ++ virTristateBool supported; ++}; ++ + STATIC_ASSERT_ENUM(VIR_DOMAIN_OS_DEF_FIRMWARE_LAST); + typedef struct _virDomainCapsOS virDomainCapsOS; + struct _virDomainCapsOS { +@@ -68,6 +73,7 @@ struct _virDomainCapsOS { + virDomainCapsEnum firmware; /* Info about virDomainOsDefFirmware */ + virDomainCapsFirmwareFeatures firmwareFeatures; + virDomainCapsLoader loader; /* Info about virDomainLoaderDef */ ++ virDomainCapsVarstore varstore; + }; + + STATIC_ASSERT_ENUM(VIR_DOMAIN_MEMORY_SOURCE_LAST); +diff --git a/src/conf/schemas/domaincaps.rng b/src/conf/schemas/domaincaps.rng +index 3b24caeca6..4682abbf41 100644 +--- a/src/conf/schemas/domaincaps.rng ++++ b/src/conf/schemas/domaincaps.rng +@@ -87,6 +87,12 @@ + + + ++ ++ ++ ++ ++ ++ + + + +@@ -98,6 +104,9 @@ + + + ++ ++ ++ + + + +-- +2.53.0 diff --git a/libvirt-conf-Introduce-iommufd-enum-for-domaincaps.patch b/libvirt-conf-Introduce-iommufd-enum-for-domaincaps.patch new file mode 100644 index 0000000..3570634 --- /dev/null +++ b/libvirt-conf-Introduce-iommufd-enum-for-domaincaps.patch @@ -0,0 +1,69 @@ +From c0fbd0d516a2c4457789d158bfdea839255d0854 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Pavel Hrdina +Date: Sat, 14 Feb 2026 06:14:20 +0100 +Subject: [PATCH] conf: Introduce iommufd enum for domaincaps + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 855f8fe9e2454555ba84696750e0e1501dd5ba80) + +Resolves: https://issues.redhat.com/browse/RHEL-148135 +Signed-off-by: Pavel Hrdina +--- + docs/formatdomaincaps.rst | 7 +++++++ + src/conf/domain_capabilities.c | 1 + + src/conf/domain_capabilities.h | 1 + + 3 files changed, 9 insertions(+) + +diff --git a/docs/formatdomaincaps.rst b/docs/formatdomaincaps.rst +index 8b4f0ecff3..6ba7f84f96 100644 +--- a/docs/formatdomaincaps.rst ++++ b/docs/formatdomaincaps.rst +@@ -461,6 +461,10 @@ Well, only if the following is enabled: + vfio + xen + ++ ++ yes ++ no ++ + + + +@@ -477,6 +481,9 @@ Well, only if the following is enabled: + ``mode="capabilities"``. + ``pciBackend`` + Options for the ``name`` attribute of the element. ++``iommufd`` ++ Options for the ``iommufd`` attribute of the element. ++ :since:`Since 12.1.0` + + RNG device + ^^^^^^^^^^ +diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c +index f843124695..49179b97ab 100644 +--- a/src/conf/domain_capabilities.c ++++ b/src/conf/domain_capabilities.c +@@ -620,6 +620,7 @@ virDomainCapsDeviceHostdevFormat(virBuffer *buf, + ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString); + ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString); + ENUM_PROCESS(hostdev, pciBackend, virDeviceHostdevPCIDriverNameTypeToString); ++ ENUM_PROCESS(hostdev, iommufd, virTristateBoolTypeToString); + + FORMAT_EPILOGUE(hostdev); + } +diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h +index 437981c711..b10370db8f 100644 +--- a/src/conf/domain_capabilities.h ++++ b/src/conf/domain_capabilities.h +@@ -108,6 +108,7 @@ struct _virDomainCapsDeviceHostdev { + virDomainCapsEnum subsysType; /* Info about virDomainHostdevSubsysType */ + virDomainCapsEnum capsType; /* Info about virDomainHostdevCapsType */ + virDomainCapsEnum pciBackend; /* Info about virDomainHostdevSubsysPCIBackendType */ ++ virDomainCapsEnum iommufd; /* Info about iommufd:virTristateBool */ + /* add new fields here */ + }; + +-- +2.53.0 diff --git a/libvirt-conf-Introduce-virDomainDefHasPCIHostdevWithIOMMUFD.patch b/libvirt-conf-Introduce-virDomainDefHasPCIHostdevWithIOMMUFD.patch new file mode 100644 index 0000000..945db18 --- /dev/null +++ b/libvirt-conf-Introduce-virDomainDefHasPCIHostdevWithIOMMUFD.patch @@ -0,0 +1,132 @@ +From 4e8fe2eb42b47a55e491a63e2600a24e0501fd1f Mon Sep 17 00:00:00 2001 +Message-ID: <4e8fe2eb42b47a55e491a63e2600a24e0501fd1f.1771423658.git.jdenemar@redhat.com> +From: Pavel Hrdina +Date: Sun, 15 Feb 2026 18:19:56 +0100 +Subject: [PATCH] conf: Introduce virDomainDefHasPCIHostdevWithIOMMUFD + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 4b176cfc3877cca882d63ab4ed446794d7a05722) + +Resolves: https://issues.redhat.com/browse/RHEL-150351 +Signed-off-by: Pavel Hrdina +--- + src/conf/domain_conf.c | 14 ++++++++++++++ + src/conf/domain_conf.h | 3 +++ + src/libvirt_private.syms | 1 + + src/qemu/qemu_command.c | 42 ++++++++++++---------------------------- + 4 files changed, 30 insertions(+), 30 deletions(-) + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 9ae48e9abc..cb047e5a3e 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -32482,6 +32482,20 @@ virDomainDefHasPCIHostdev(const virDomainDef *def) + } + + ++bool ++virDomainDefHasPCIHostdevWithIOMMUFD(const virDomainDef *def) ++{ ++ size_t i; ++ ++ for (i = 0; i < def->nhostdevs; i++) { ++ if (virHostdevIsPCIDeviceWithIOMMUFD(def->hostdevs[i])) ++ return true; ++ } ++ ++ return false; ++} ++ ++ + bool + virDomainDefHasMdevHostdev(const virDomainDef *def) + { +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index d958ed04f9..69a8e79c6d 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -4655,6 +4655,9 @@ virDomainDefHasNVMeDisk(const virDomainDef *def); + bool + virDomainDefHasPCIHostdev(const virDomainDef *def); + ++bool ++virDomainDefHasPCIHostdevWithIOMMUFD(const virDomainDef *def); ++ + bool + virDomainDefHasMdevHostdev(const virDomainDef *def); + +diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms +index 863e50ec4f..effe44fe57 100644 +--- a/src/libvirt_private.syms ++++ b/src/libvirt_private.syms +@@ -348,6 +348,7 @@ virDomainDefHasNVMeDisk; + virDomainDefHasOldStyleROUEFI; + virDomainDefHasOldStyleUEFI; + virDomainDefHasPCIHostdev; ++virDomainDefHasPCIHostdevWithIOMMUFD; + virDomainDefHasTimer; + virDomainDefHasUSB; + virDomainDefHasVcpusOffline; +diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c +index c8626e6d49..3119191413 100644 +--- a/src/qemu/qemu_command.c ++++ b/src/qemu/qemu_command.c +@@ -5349,43 +5349,25 @@ qemuBuildIOMMUFDCommandLine(virCommand *cmd, + const virDomainDef *def, + virDomainObj *vm) + { +- size_t i; + qemuDomainObjPrivate *priv = vm->privateData; + g_autofree char *fdstr = g_strdup_printf("%d", priv->iommufd); ++ g_autoptr(virJSONValue) props = NULL; + ++ if (!virDomainDefHasPCIHostdevWithIOMMUFD(def)) ++ return 0; + +- for (i = 0; i < def->nhostdevs; i++) { +- virDomainHostdevDef *hostdev = def->hostdevs[i]; +- virDomainHostdevSubsys *subsys = &hostdev->source.subsys; +- g_autoptr(virJSONValue) props = NULL; ++ virCommandPassFD(cmd, priv->iommufd, VIR_COMMAND_PASS_FD_CLOSE_PARENT); + +- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) +- continue; ++ priv->iommufd = -1; + +- if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) +- continue; ++ if (qemuMonitorCreateObjectProps(&props, "iommufd", ++ "iommufd0", ++ "S:fd", fdstr, ++ NULL) < 0) ++ return -1; + +- if (hostdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) +- continue; +- +- if (subsys->u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) +- continue; +- +- virCommandPassFD(cmd, priv->iommufd, VIR_COMMAND_PASS_FD_CLOSE_PARENT); +- +- priv->iommufd = -1; +- +- if (qemuMonitorCreateObjectProps(&props, "iommufd", +- "iommufd0", +- "S:fd", fdstr, +- NULL) < 0) +- return -1; +- +- if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) +- return -1; +- +- break; +- } ++ if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) ++ return -1; + + return 0; + } +-- +2.53.0 diff --git a/libvirt-conf-Introduce-virHostdevIsPCIDeviceWithIOMMUFD.patch b/libvirt-conf-Introduce-virHostdevIsPCIDeviceWithIOMMUFD.patch new file mode 100644 index 0000000..7f8e036 --- /dev/null +++ b/libvirt-conf-Introduce-virHostdevIsPCIDeviceWithIOMMUFD.patch @@ -0,0 +1,89 @@ +From 615f11792c8988cfd6a30717dcc8d5d9174ea508 Mon Sep 17 00:00:00 2001 +Message-ID: <615f11792c8988cfd6a30717dcc8d5d9174ea508.1771423658.git.jdenemar@redhat.com> +From: Pavel Hrdina +Date: Sun, 15 Feb 2026 18:19:23 +0100 +Subject: [PATCH] conf: Introduce virHostdevIsPCIDeviceWithIOMMUFD + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 97eed30948e980be8b7552fff637e828768854e4) + +Resolves: https://issues.redhat.com/browse/RHEL-150351 +Signed-off-by: Pavel Hrdina +--- + src/conf/domain_conf.c | 15 +++++++++++++++ + src/conf/domain_conf.h | 3 +++ + src/libvirt_private.syms | 1 + + src/qemu/qemu_process.c | 5 +---- + 4 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 83c58ab5ff..9ae48e9abc 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -32758,6 +32758,21 @@ virHostdevIsPCIDevice(const virDomainHostdevDef *hostdev) + } + + ++/** ++ * virHostdevIsPCIDeviceWithIOMMUFD: ++ * @hostdev: host device to check ++ * ++ * Returns true if @hostdev is a PCI device with IOMMUFD enabled, false otherwise. ++ */ ++bool ++virHostdevIsPCIDeviceWithIOMMUFD(const virDomainHostdevDef *hostdev) ++{ ++ return virHostdevIsPCIDevice(hostdev) && ++ hostdev->source.subsys.u.pci.driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO && ++ hostdev->source.subsys.u.pci.driver.iommufd == VIR_TRISTATE_BOOL_YES; ++} ++ ++ + static void + virDomainObjGetMessagesIOErrorsSrc(virStorageSource *src, + const char *diskdst, +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index b120d4a68e..d958ed04f9 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -4713,6 +4713,9 @@ virHostdevIsMdevDevice(const virDomainHostdevDef *hostdev) + bool + virHostdevIsPCIDevice(const virDomainHostdevDef *hostdev) + ATTRIBUTE_NONNULL(1); ++bool ++virHostdevIsPCIDeviceWithIOMMUFD(const virDomainHostdevDef *hostdev) ++ ATTRIBUTE_NONNULL(1); + + void + virDomainObjGetMessagesIOErrorsChain(virStorageSource *src, +diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms +index 9ae44e31b8..863e50ec4f 100644 +--- a/src/libvirt_private.syms ++++ b/src/libvirt_private.syms +@@ -812,6 +812,7 @@ virDomainQemuMonitorEventNew; + virDomainQemuMonitorEventStateRegisterID; + virHostdevIsMdevDevice; + virHostdevIsPCIDevice; ++virHostdevIsPCIDeviceWithIOMMUFD; + virHostdevIsSCSIDevice; + + +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index 7e32325fa0..3bd81c55b3 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -7732,10 +7732,7 @@ qemuProcessOpenVfioFds(virDomainObj *vm) + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevDef *hostdev = vm->def->hostdevs[i]; + +- if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && +- hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && +- hostdev->source.subsys.u.pci.driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO && +- hostdev->source.subsys.u.pci.driver.iommufd == VIR_TRISTATE_BOOL_YES) { ++ if (virHostdevIsPCIDeviceWithIOMMUFD(hostdev)) { + /* Open VFIO device FD */ + if (qemuProcessOpenVfioDeviceFd(hostdev) < 0) + return -1; +-- +2.53.0 diff --git a/libvirt-conf-Move-type-rom-default-for-loader-to-drivers.patch b/libvirt-conf-Move-type-rom-default-for-loader-to-drivers.patch new file mode 100644 index 0000000..96b68cb --- /dev/null +++ b/libvirt-conf-Move-type-rom-default-for-loader-to-drivers.patch @@ -0,0 +1,104 @@ +From 08ff36546b810ae14135c19c99fb1dc1aa5fcbb2 Mon Sep 17 00:00:00 2001 +Message-ID: <08ff36546b810ae14135c19c99fb1dc1aa5fcbb2.1772815313.git.jdenemar@redhat.com> +From: Andrea Bolognani +Date: Tue, 3 Feb 2026 15:18:39 +0100 +Subject: [PATCH] conf: Move type=rom default for loader to drivers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Right now we set this default in the common parsing code, which +is not a big problem per se but would get in the way of some +upcoming changes. + +Leave this choice to individual drivers instead. Only the QEMU +and Xen drivers use the value for anything, so we can limit the +amount of code duplication this change causes. + +Signed-off-by: Andrea Bolognani +Reviewed-by: Michal Privoznik +Acked-by: Gerd Hoffmann +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 1504b7f687bdfc679377e605d076776b18533468) + +https://issues.redhat.com/browse/RHEL-82645 + +Signed-off-by: Andrea Bolognani +--- + src/conf/domain_postparse.c | 19 ------------------- + src/libxl/libxl_domain.c | 6 ++++++ + src/qemu/qemu_firmware.c | 5 +++++ + 3 files changed, 11 insertions(+), 19 deletions(-) + +diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c +index 38e731348d..cbaae75c02 100644 +--- a/src/conf/domain_postparse.c ++++ b/src/conf/domain_postparse.c +@@ -89,22 +89,6 @@ virDomainDefPostParseMemory(virDomainDef *def, + } + + +-static int +-virDomainDefPostParseOs(virDomainDef *def) +-{ +- if (!def->os.loader) +- return 0; +- +- if (def->os.loader->path && +- def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_NONE) { +- /* By default, loader is type of 'rom' */ +- def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM; +- } +- +- return 0; +-} +- +- + static void + virDomainDefPostParseMemtune(virDomainDef *def) + { +@@ -1251,9 +1235,6 @@ virDomainDefPostParseCommon(virDomainDef *def, + if (virDomainDefPostParseMemory(def, data->parseFlags) < 0) + return -1; + +- if (virDomainDefPostParseOs(def) < 0) +- return -1; +- + virDomainDefPostParseMemtune(def); + + if (virDomainDefRejectDuplicateControllers(def) < 0) +diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c +index 9842d6fece..c6717e31cf 100644 +--- a/src/libxl/libxl_domain.c ++++ b/src/libxl/libxl_domain.c +@@ -279,6 +279,12 @@ libxlDomainDefPostParse(virDomainDef *def, + def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON; + } + ++ if (def->os.loader && ++ def->os.loader->path && ++ !def->os.loader->type) { ++ def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM; ++ } ++ + /* add implicit balloon device */ + if (def->memballoon == NULL) { + virDomainMemballoonDef *memballoon; +diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c +index 519828f6f9..6a074055ca 100644 +--- a/src/qemu/qemu_firmware.c ++++ b/src/qemu/qemu_firmware.c +@@ -1662,6 +1662,11 @@ qemuFirmwareFillDomainCustom(virDomainDef *def) + if (!loader) + return; + ++ if (loader->path && ++ !loader->type) { ++ loader->type = VIR_DOMAIN_LOADER_TYPE_ROM; ++ } ++ + if (loader->path && + !loader->format) { + loader->format = VIR_STORAGE_FILE_RAW; +-- +2.53.0 diff --git a/libvirt-conf-Parse-and-format-varstore-element.patch b/libvirt-conf-Parse-and-format-varstore-element.patch new file mode 100644 index 0000000..477d66e --- /dev/null +++ b/libvirt-conf-Parse-and-format-varstore-element.patch @@ -0,0 +1,385 @@ +From 50a7a37ea4d6c8ffab8110a58db1b16b9d1d7b84 Mon Sep 17 00:00:00 2001 +Message-ID: <50a7a37ea4d6c8ffab8110a58db1b16b9d1d7b84.1772815313.git.jdenemar@redhat.com> +From: Andrea Bolognani +Date: Mon, 19 Jan 2026 14:20:06 +0100 +Subject: [PATCH] conf: Parse and format varstore element +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This will be used to configure the backing storage used by the +uefi-vars QEMU device. + +Dealing with the element itself is trivial, however we have to +refactor the existing code which deals with the loader and nvram +elements slightly: in particular, we can no longer perform an +early exit if those elements are absent. + +Signed-off-by: Andrea Bolognani +Reviewed-by: Michal Privoznik +Acked-by: Gerd Hoffmann +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 3feee6d0aba5abf5e69d69b0022c08ea6bd5af3e) + +https://issues.redhat.com/browse/RHEL-82645 + +Signed-off-by: Andrea Bolognani +--- + docs/formatdomain.rst | 23 +++++++-- + docs/kbase/secureboot.rst | 46 ++++++++++++------ + src/conf/domain_conf.c | 81 ++++++++++++++++++++++++++++--- + src/conf/domain_conf.h | 9 ++++ + src/conf/schemas/domaincommon.rng | 22 ++++++++- + src/conf/virconftypes.h | 2 + + src/libvirt_private.syms | 2 + + 7 files changed, 157 insertions(+), 28 deletions(-) + +diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst +index 152fd7f530..7d6cc45efd 100644 +--- a/docs/formatdomain.rst ++++ b/docs/formatdomain.rst +@@ -196,9 +196,9 @@ harddisk, cdrom, network) determining where to obtain/find the boot image. + + ``firmware`` + The ``firmware`` attribute allows management applications to automatically +- fill ```` and ```` elements and possibly enable some +- features required by selected firmware. Accepted values are ``bios`` and +- ``efi``. ++ fill ```` and ```` or ```` elements and possibly ++ enable some features required by selected firmware. Accepted values are ++ ``bios`` and ``efi``. + The selection process scans for files describing installed firmware images in + specified location and uses the most specific one which fulfills domain + requirements. The locations in order of preference (from generic to most +@@ -311,6 +311,23 @@ harddisk, cdrom, network) determining where to obtain/find the boot image. + It is not valid to provide this element if the loader is marked as + stateless. + ++``varstore`` ++ This works much the same way as the ```` element described above, ++ except that variable storage is handled by the ``uefi-vars`` QEMU device ++ instead of being backed by a pflash device. :since:`Since 12.1.0 (QEMU only)` ++ ++ The ``path`` attribute contains the path of the domain-specific file where ++ variables are stored, while the ``template`` attribute points to a template ++ that the domain-specific file can be (re)generated from. Assuming that the ++ necessary JSON firmware descriptor files are present, both attributes will ++ be filled in automatically by libvirt. ++ ++ Using ```` instead of ```` is particularly useful on ++ non-x86 architectures such as aarch64, where it represents the only way to ++ get Secure Boot working. It can be used on x86 too, and doing so will make ++ it possible to keep UEFI authenticated variables safe from tampering without ++ requiring the use of SMM emulation. ++ + ``boot`` + The ``dev`` attribute takes one of the values "fd", "hd", "cdrom" or + "network" and is used to specify the next boot device to consider. The +diff --git a/docs/kbase/secureboot.rst b/docs/kbase/secureboot.rst +index 6c22b08d22..b411b65f00 100644 +--- a/docs/kbase/secureboot.rst ++++ b/docs/kbase/secureboot.rst +@@ -74,8 +74,8 @@ Changing an existing VM + + When a VM is defined, libvirt will pick the firmware that best + satisfies the provided criteria and record this information for use +-on subsequent boots. The resulting XML configuration will look like +-this: ++on subsequent boots. The resulting XML configuration will look either ++like this: + + :: + +@@ -88,14 +88,28 @@ this: + /var/lib/libvirt/qemu/nvram/vm_VARS.fd + + ++or like this: ++ ++:: ++ ++ ++ ++ ++ ++ ++ /usr/share/edk2/aarch64/QEMU_EFI.qemuvars.fd ++ ++ ++ + In order to force libvirt to repeat the firmware autoselection +-process, it's necessary to remove the ```` and ```` +-elements. Failure to do so will likely result in an error. ++process, it's necessary to remove the ```` as well as the ++```` or ```` elements, depending on what's ++applicable. Failure to do so will likely result in an error. + + Note that updating the XML configuration as described above is +-**not** enough to change the Secure Boot status: the NVRAM file +-associated with the VM has to be regenerated from its template as +-well. ++**not** enough to change the Secure Boot status: the NVRAM/varstore ++file associated with the VM has to be regenerated from its template ++as well. + + In order to do that, update the XML and then start the VM with + +@@ -107,9 +121,9 @@ This option is only available starting with libvirt 8.1.0, so if your + version of libvirt is older than that you will have to delete the + NVRAM file manually before starting the VM. + +-Most guest operating systems will be able to cope with the NVRAM file +-being reinitialized, but in some cases the VM will be unable to boot +-after the change. ++Most guest operating systems will be able to cope with the ++NVRAM/varstore file being reinitialized, but in some cases the VM ++will be unable to boot after the change. + + + Additional information +@@ -126,15 +140,15 @@ can be used to validate the operating system signature need to be + provided as well. + + Asking for the ``enrolled-keys`` firmware feature to be enabled will +-cause libvirt to initialize the NVRAM file associated with the VM +-from a template that contains a suitable set of keys. These keys +-being present will cause the firmware to enforce the Secure Boot ++cause libvirt to initialize the NVRAM/varstore file associated with ++the VM from a template that contains a suitable set of keys. These ++keys being present will cause the firmware to enforce the Secure Boot + signing requirements. + + The opposite configuration, where the feature is explicitly disabled, +-will result in no keys being present in the NVRAM file. Unable to +-verify signatures, the firmware will allow even unsigned operating +-systems to run. ++will result in no keys being present in the NVRAM/varstore file. ++Unable to verify signatures, the firmware will allow even unsigned ++operating systems to run. + + If running unsigned code is desired, it's also possible to ask for + the ``secure-boot`` feature to be disabled, which will cause libvirt +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index e72cda0048..16ea9f0b2e 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -3932,6 +3932,27 @@ virDomainLoaderDefFree(virDomainLoaderDef *loader) + g_free(loader); + } + ++virDomainVarstoreDef * ++virDomainVarstoreDefNew(void) ++{ ++ virDomainVarstoreDef *def = NULL; ++ ++ def = g_new0(virDomainVarstoreDef, 1); ++ ++ return def; ++} ++ ++void ++virDomainVarstoreDefFree(virDomainVarstoreDef *varstore) ++{ ++ if (!varstore) ++ return; ++ ++ g_free(varstore->path); ++ g_free(varstore->template); ++ g_free(varstore); ++} ++ + + static void + virDomainResctrlMonDefFree(virDomainResctrlMonDef *domresmon) +@@ -4034,6 +4055,7 @@ virDomainOSDefClear(virDomainOSDef *os) + virDomainOSACPITableDefFree(os->acpiTables[i]); + g_free(os->acpiTables); + virDomainLoaderDefFree(os->loader); ++ virDomainVarstoreDefFree(os->varstore); + g_free(os->bootloader); + g_free(os->bootloaderArgs); + } +@@ -17983,6 +18005,17 @@ virDomainLoaderDefParseXMLLoader(virDomainLoaderDef *loader, + } + + ++static int ++virDomainVarstoreDefParseXML(virDomainVarstoreDef *varstore, ++ xmlNodePtr varstoreNode) ++{ ++ varstore->path = virXMLPropString(varstoreNode, "path"); ++ varstore->template = virXMLPropString(varstoreNode, "template"); ++ ++ return 0; ++} ++ ++ + static int + virDomainLoaderDefParseXML(virDomainLoaderDef *loader, + xmlNodePtr loaderNode, +@@ -18430,16 +18463,29 @@ virDomainDefParseBootLoaderOptions(virDomainDef *def, + xmlNodePtr loaderNode = virXPathNode("./os/loader[1]", ctxt); + xmlNodePtr nvramNode = virXPathNode("./os/nvram[1]", ctxt); + xmlNodePtr nvramSourceNode = virXPathNode("./os/nvram/source[1]", ctxt); ++ xmlNodePtr varstoreNode = virXPathNode("./os/varstore[1]", ctxt); + +- if (!loaderNode && !nvramNode) +- return 0; +- +- def->os.loader = virDomainLoaderDefNew(); +- +- if (virDomainLoaderDefParseXML(def->os.loader, +- loaderNode, nvramNode, nvramSourceNode, +- ctxt, xmlopt, flags) < 0) ++ if (nvramNode && varstoreNode) { ++ virReportError(VIR_ERR_XML_ERROR, "%s", ++ _("Cannot have both and ")); + return -1; ++ } ++ ++ if (loaderNode || nvramNode) { ++ def->os.loader = virDomainLoaderDefNew(); ++ ++ if (virDomainLoaderDefParseXML(def->os.loader, ++ loaderNode, nvramNode, nvramSourceNode, ++ ctxt, xmlopt, flags) < 0) ++ return -1; ++ } ++ ++ if (varstoreNode) { ++ def->os.varstore = virDomainVarstoreDefNew(); ++ ++ if (virDomainVarstoreDefParseXML(def->os.varstore, varstoreNode) < 0) ++ return -1; ++ } + + return 0; + } +@@ -28062,6 +28108,20 @@ virDomainLoaderDefFormat(virBuffer *buf, + return 0; + } + ++static int ++virDomainVarstoreDefFormat(virBuffer *buf, ++ virDomainVarstoreDef *varstore) ++{ ++ g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; ++ ++ virBufferEscapeString(&attrBuf, " template='%s'", varstore->template); ++ virBufferEscapeString(&attrBuf, " path='%s'", varstore->path); ++ ++ virXMLFormatElementEmpty(buf, "varstore", &attrBuf, NULL); ++ ++ return 0; ++} ++ + static void + virDomainKeyWrapDefFormat(virBuffer *buf, virDomainKeyWrapDef *keywrap) + { +@@ -29523,6 +29583,11 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def, + if (def->os.loader && + virDomainLoaderDefFormat(buf, def->os.loader, xmlopt, flags) < 0) + return -1; ++ ++ if (def->os.varstore && ++ virDomainVarstoreDefFormat(buf, def->os.varstore) < 0) ++ return -1; ++ + virBufferEscapeString(buf, "%s\n", + def->os.kernel); + virBufferEscapeString(buf, "%s\n", +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index 69a8e79c6d..ead3b07475 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -2420,6 +2420,14 @@ struct _virDomainLoaderDef { + virDomainLoaderDef *virDomainLoaderDefNew(void); + void virDomainLoaderDefFree(virDomainLoaderDef *loader); + ++struct _virDomainVarstoreDef { ++ char *path; ++ char *template; ++}; ++ ++virDomainVarstoreDef *virDomainVarstoreDefNew(void); ++void virDomainVarstoreDefFree(virDomainVarstoreDef *varstore); ++ + typedef enum { + VIR_DOMAIN_IOAPIC_NONE = 0, + VIR_DOMAIN_IOAPIC_QEMU, +@@ -2573,6 +2581,7 @@ struct _virDomainOSDef { + size_t nacpiTables; + virDomainOSACPITableDef **acpiTables; + virDomainLoaderDef *loader; ++ virDomainVarstoreDef *varstore; + char *bootloader; + char *bootloaderArgs; + int smbios_mode; +diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng +index 92f82c8fbf..7215db3fc1 100644 +--- a/src/conf/schemas/domaincommon.rng ++++ b/src/conf/schemas/domaincommon.rng +@@ -349,7 +349,10 @@ + + + +- ++ ++ ++ ++ + + + +@@ -456,6 +459,23 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h +index 6e2573035a..0596791a4d 100644 +--- a/src/conf/virconftypes.h ++++ b/src/conf/virconftypes.h +@@ -164,6 +164,8 @@ typedef struct _virDomainLeaseDef virDomainLeaseDef; + + typedef struct _virDomainLoaderDef virDomainLoaderDef; + ++typedef struct _virDomainVarstoreDef virDomainVarstoreDef; ++ + typedef struct _virDomainMemballoonDef virDomainMemballoonDef; + + typedef struct _virDomainMemoryDef virDomainMemoryDef; +diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms +index effe44fe57..1308fa2e51 100644 +--- a/src/libvirt_private.syms ++++ b/src/libvirt_private.syms +@@ -718,6 +718,8 @@ virDomainTPMProfileRemoveDisabledTypeToString; + virDomainTPMVersionTypeFromString; + virDomainTPMVersionTypeToString; + virDomainUSBDeviceDefForeach; ++virDomainVarstoreDefFree; ++virDomainVarstoreDefNew; + virDomainVideoDefaultRAM; + virDomainVideoDefClear; + virDomainVideoDefFree; +-- +2.53.0 diff --git a/libvirt-conf-Update-validation-to-consider-varstore-element.patch b/libvirt-conf-Update-validation-to-consider-varstore-element.patch new file mode 100644 index 0000000..c48c277 --- /dev/null +++ b/libvirt-conf-Update-validation-to-consider-varstore-element.patch @@ -0,0 +1,378 @@ +From f47031d4e6439d1daf5711d4117c0fa647196944 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Andrea Bolognani +Date: Thu, 22 Jan 2026 19:27:03 +0100 +Subject: [PATCH] conf: Update validation to consider varstore element +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The code is reworked quite significantly, but most of the +existing checks are preserved. Those that aren't, notably the +one that allowed pflash as the only acceptable non-stateless +firmware type, are intentionally removed because they will no +longer reflect reality once support for the uefi-vars QEMU +device is introduced. + +As a side effect, reworking the function in this fashion +resolves a subtle bug: due to the early exits that were being +performed when the loader element was missing, the checks at +the bottom of the function (related to the shim and kernel +elements) were effectively never performed. This is no longer +the case. + +Signed-off-by: Andrea Bolognani +Reviewed-by: Michal Privoznik +Acked-by: Gerd Hoffmann +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 1c2dbdf3ac5bed84caeacf585d5143dcf32df75e) + +https://issues.redhat.com/browse/RHEL-82645 + +Signed-off-by: Andrea Bolognani +--- + src/conf/domain_validate.c | 100 +++++++----------- + ...-auto-bios-not-stateless.x86_64-latest.err | 2 +- + ...-auto-bios-not-stateless.x86_64-latest.xml | 35 ++++++ + ...firmware-auto-bios-nvram.x86_64-latest.err | 2 +- + ...nual-bios-not-stateless.x86_64-latest.args | 32 ++++++ + ...anual-bios-not-stateless.x86_64-latest.err | 1 - + ...anual-bios-not-stateless.x86_64-latest.xml | 28 +++++ + ...nual-efi-nvram-stateless.x86_64-latest.err | 2 +- + ...nvram-template-stateless.x86_64-latest.err | 2 +- + ...ware-manual-efi-rw-nvram.x86_64-latest.err | 2 +- + tests/qemuxmlconftest.c | 7 +- + 11 files changed, 144 insertions(+), 69 deletions(-) + create mode 100644 tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.xml + create mode 100644 tests/qemuxmlconfdata/firmware-manual-bios-not-stateless.x86_64-latest.args + delete mode 100644 tests/qemuxmlconfdata/firmware-manual-bios-not-stateless.x86_64-latest.err + create mode 100644 tests/qemuxmlconfdata/firmware-manual-bios-not-stateless.x86_64-latest.xml + +diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c +index 7346a61731..163095d55c 100644 +--- a/src/conf/domain_validate.c ++++ b/src/conf/domain_validate.c +@@ -1723,95 +1723,46 @@ virDomainDefOSValidate(const virDomainDef *def, + virDomainXMLOption *xmlopt) + { + virDomainLoaderDef *loader = def->os.loader; ++ virDomainVarstoreDef *varstore = def->os.varstore; ++ virDomainOsDefFirmware firmware = def->os.firmware; ++ int *firmwareFeatures = def->os.firmwareFeatures; ++ bool usesNvram = loader && (loader->nvram || loader->nvramTemplate || loader->nvramTemplateFormat); + +- if (def->os.firmware) { ++ if (firmware) { + if (xmlopt && !(xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT)) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("firmware auto selection not implemented for this driver")); + return -1; + } + +- if (def->os.firmwareFeatures && +- def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS] == VIR_TRISTATE_BOOL_YES && +- def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] == VIR_TRISTATE_BOOL_NO) { ++ if (firmwareFeatures && ++ firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS] == VIR_TRISTATE_BOOL_YES && ++ firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] == VIR_TRISTATE_BOOL_NO) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("firmware feature 'enrolled-keys' cannot be enabled when firmware feature 'secure-boot' is disabled")); + return -1; + } +- +- if (!loader) +- return 0; +- +- if (loader->nvram && def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) { +- virReportError(VIR_ERR_XML_DETAIL, +- _("firmware type '%1$s' does not support nvram"), +- virDomainOsDefFirmwareTypeToString(def->os.firmware)); +- return -1; +- } + } else { +- if (def->os.firmwareFeatures) { ++ if (firmwareFeatures) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("cannot use feature-based firmware autoselection when firmware autoselection is disabled")); + return -1; + } + +- if (!loader) +- return 0; +- +- if (!loader->path) { ++ if (loader && !loader->path) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("no loader path specified and firmware auto selection disabled")); + return -1; + } + } + +- if (loader->readonly == VIR_TRISTATE_BOOL_NO) { +- if (loader->type == VIR_DOMAIN_LOADER_TYPE_ROM) { ++ if (loader && loader->type == VIR_DOMAIN_LOADER_TYPE_ROM) { ++ if (loader->readonly == VIR_TRISTATE_BOOL_NO) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("ROM loader type cannot be used as read/write")); + return -1; + } + +- if (loader->nvramTemplate) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("NVRAM template is not permitted when loader is read/write")); +- return -1; +- } +- +- if (loader->nvram) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("NVRAM is not permitted when loader is read/write")); +- return -1; +- } +- } +- +- if (loader->stateless == VIR_TRISTATE_BOOL_YES) { +- if (loader->nvramTemplate) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("NVRAM template is not permitted when loader is stateless")); +- return -1; +- } +- +- if (loader->nvram) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("NVRAM is not permitted when loader is stateless")); +- return -1; +- } +- } else if (loader->stateless == VIR_TRISTATE_BOOL_NO) { +- if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) { +- if (def->os.loader->type != VIR_DOMAIN_LOADER_TYPE_PFLASH) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("Only pflash loader type permits NVRAM")); +- return -1; +- } +- } else if (def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) { +- virReportError(VIR_ERR_XML_DETAIL, "%s", +- _("Only EFI firmware permits NVRAM")); +- return -1; +- } +- } +- +- if (loader->type == VIR_DOMAIN_LOADER_TYPE_ROM) { + if (loader->format && + loader->format != VIR_STORAGE_FILE_RAW) { + virReportError(VIR_ERR_XML_DETAIL, +@@ -1821,6 +1772,33 @@ virDomainDefOSValidate(const virDomainDef *def, + } + } + ++ if (usesNvram && varstore) { ++ virReportError(VIR_ERR_XML_DETAIL, "%s", ++ _("Only one of NVRAM/varstore can be used")); ++ return -1; ++ } ++ ++ if (usesNvram || varstore) { ++ if (firmware && firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) { ++ virReportError(VIR_ERR_XML_DETAIL, ++ _("Firmware type '%1$s' does not support variable storage (NVRAM/varstore)"), ++ virDomainOsDefFirmwareTypeToString(firmware)); ++ return -1; ++ } ++ ++ if (loader && loader->stateless == VIR_TRISTATE_BOOL_YES) { ++ virReportError(VIR_ERR_XML_DETAIL, "%s", ++ _("Variable storage (NVRAM/varstore) is not permitted when loader is stateless")); ++ return -1; ++ } ++ ++ if (loader && loader->readonly == VIR_TRISTATE_BOOL_NO) { ++ virReportError(VIR_ERR_XML_DETAIL, "%s", ++ _("Variable storage (NVRAM/varstore) is not permitted when loader is read/write")); ++ return -1; ++ } ++ } ++ + if (def->os.shim && !def->os.kernel) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("shim only allowed with kernel option")); +diff --git a/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.err b/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.err +index b058f970a4..743fe27a97 100644 +--- a/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.err ++++ b/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.err +@@ -1 +1 @@ +-Only EFI firmware permits NVRAM ++operation failed: Unable to find 'bios' firmware that is compatible with the current configuration +diff --git a/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.xml b/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.xml +new file mode 100644 +index 0000000000..062835e351 +--- /dev/null ++++ b/tests/qemuxmlconfdata/firmware-auto-bios-not-stateless.x86_64-latest.xml +@@ -0,0 +1,35 @@ ++ ++ guest ++ 63840878-0deb-4095-97e6-fc444d9bc9fa ++ 1048576 ++ 1048576 ++ 1 ++ ++ hvm ++ ++ ++ ++ ++ ++ ++ ++ qemu64 ++ ++ ++ destroy ++ restart ++ destroy ++ ++ /usr/bin/qemu-system-x86_64 ++ ++ ++
++ ++ ++ ++ ++