diff --git a/conf-add-support-for-MB-min_bandwidth.patch b/conf-add-support-for-MB-min_bandwidth.patch
new file mode 100644
index 0000000000000000000000000000000000000000..86c1340a9b0f22948b18e232d2c18872b8718507
--- /dev/null
+++ b/conf-add-support-for-MB-min_bandwidth.patch
@@ -0,0 +1,99 @@
+From 2b6d170cc9b511657cd0aefc8515bdc4b7467a94 Mon Sep 17 00:00:00 2001
+From: hy <941973499@qq.com>
+Date: Mon, 18 Aug 2025 11:02:40 +0800
+Subject: [PATCH 1/6] conf: add support for memorytune XML processing for MB
+ min_bandwidth
+
+---
+ src/conf/domain_conf.c | 12 ++++++++++--
+ src/conf/schemas/domaincommon.rng | 5 +++++
+ src/util/virresctrl.c | 8 +++++++-
+ 3 files changed, 22 insertions(+), 3 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 28b2157424..04f2642109 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -18239,6 +18239,7 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ unsigned int bandwidth;
+ unsigned int hard_limit = UINT_MAX;
+ unsigned int priority = UINT_MAX;
++ unsigned int min_bandwidth = UINT_MAX;
+
+ ctxt->node = node;
+
+@@ -18252,20 +18253,27 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ if (virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_BANDWIDTH, id, bandwidth) < 0)
+ return -1;
+
+- if (virXMLPropUIntDefault(node, "hardlimit", 10, 0, &hard_limit, UINT_MAX) < 0)
++ if (virXMLPropUIntDefault(node, "hardlimit", 10, VIR_XML_PROP_NONE, &hard_limit, UINT_MAX) < 0)
+ return -1;
+
+ if (hard_limit != UINT_MAX &&
+ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_HARDLIMIT, id, hard_limit) < 0)
+ return -1;
+
+- if (virXMLPropUIntDefault(node, "priority", 10, 0, &priority, UINT_MAX) < 0)
++ if (virXMLPropUIntDefault(node, "priority", 10, VIR_XML_PROP_NONE, &priority, UINT_MAX) < 0)
+ return -1;
+
+ if (priority != UINT_MAX &&
+ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_PRIORITY, id, priority) < 0)
+ return -1;
+
++ if (virXMLPropUIntDefault(node, "min_bandwidth", 10, VIR_XML_PROP_NONE, &min_bandwidth, UINT_MAX) < 0)
++ return -1;
++
++ if (min_bandwidth != UINT_MAX &&
++ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_MIN_BANDWIDTH, id, min_bandwidth) < 0)
++ return -1;
++
+ return 0;
+ }
+
+diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
+index 1b2256fddf..a2e0119888 100644
+--- a/src/conf/schemas/domaincommon.rng
++++ b/src/conf/schemas/domaincommon.rng
+@@ -1195,6 +1195,11 @@
+
+
+
++
++
++
++
++
+
+
+
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 06935cc11f..afd70c821a 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -87,7 +87,7 @@ VIR_ENUM_IMPL(virMemory,
+ "bandwidth",
+ "hardlimit",
+ "priority",
+- "minbandwidth",
++ "min_bandwidth",
+ );
+
+ VIR_ENUM_DECL(virResctrlMemory);
+@@ -1423,6 +1423,12 @@ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ }
+ break;
+ case VIR_MEMORY_TYPE_MIN_BANDWIDTH:
++ if (value > 100) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth min_bandwidth value exceeding 100 is invalid."));
++ return -1;
++ }
++ break;
+ case VIR_MEMORY_TYPE_LAST:
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+--
+2.43.0
+
diff --git a/conf-add-support-for-cachetune-L3-priority.patch b/conf-add-support-for-cachetune-L3-priority.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b6ad6136484063e88f8dd56079f37c142003c3db
--- /dev/null
+++ b/conf-add-support-for-cachetune-L3-priority.patch
@@ -0,0 +1,125 @@
+From f6081b2631ac124b73be5953a9b9ce4922bf505e Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Mon, 28 Jul 2025 14:59:13 +0800
+Subject: [PATCH 3/6] conf: add support for cachetune/L3 priority
+
+---
+ src/conf/domain_conf.c | 29 +++++++++++++++++++++--------
+ src/conf/schemas/domaincommon.rng | 1 +
+ src/util/virresctrl.c | 27 +++++++++++++++++++++++++--
+ 3 files changed, 47 insertions(+), 10 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 9643b9ce01..5dbbe5cd3c 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -17680,10 +17680,15 @@ virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
+ VIR_XML_PROP_REQUIRED, &type) < 0)
+ return -1;
+
+- if (virParseScaledValue("./@size", "./@unit",
+- ctxt, &size, 1024,
+- ULLONG_MAX, true) < 0)
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ if (virXMLPropULongLong(node, "size", 10, VIR_XML_PROP_REQUIRED, &size) < 0)
++ return -1;
++ } else {
++ if (virParseScaledValue("./@size", "./@unit",
++ ctxt, &size, 1024,
++ ULLONG_MAX, true) < 0)
+ return -1;
++ }
+
+ if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0)
+ return -1;
+@@ -26749,11 +26754,19 @@ virDomainCachetuneDefFormatHelper(unsigned int level,
+ virBuffer *buf = opaque;
+ unsigned long long short_size = virFormatIntPretty(size, &unit);
+
+- virBufferAsprintf(buf,
+- "\n",
+- cache, level, virCacheTypeToString(type),
+- short_size, unit);
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ virBufferAsprintf(buf,
++ "\n",
++ cache, level, virCacheTypeToString(type),
++ size);
++ } else {
++ virBufferAsprintf(buf,
++ "\n",
++ cache, level, virCacheTypeToString(type),
++ short_size, unit);
++ }
+
+ return 0;
+ }
+diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
+index f31603b2fd..7278b699e0 100644
+--- a/src/conf/schemas/domaincommon.rng
++++ b/src/conf/schemas/domaincommon.rng
+@@ -1147,6 +1147,7 @@
+ both
+ code
+ data
++ priority
+
+
+
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 3d2be7560c..7bdbc85bc9 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -1184,6 +1184,15 @@ virResctrlAllocCheckCollision(virResctrlAlloc *alloc,
+ if (!a_level)
+ return false;
+
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ a_type = a_level->types[VIR_CACHE_TYPE_PRIORITY];
++
++ if (a_type && a_type->nsizes > cache && a_type->sizes[cache])
++ return true;
++
++ return false;
++ }
++
+ a_type = a_level->types[VIR_CACHE_TYPE_BOTH];
+
+ /* If there is an allocation for type 'both', there can be no other
+@@ -2330,14 +2339,28 @@ virResctrlAllocAssign(virResctrlInfo *resctrl,
+ if (!a_type->sizes[cache])
+ continue;
+
+- if (!a_type->priorities[cache]) {
++ if (a_type->npriorities == 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Cache level %1$d does not support tuning for scope type '%2$s'"),
+ level, virCacheTypeToString(type));
+ return -1;
+ }
+
+- *a_type->priorities[cache] = *a_type->sizes[cache];
++ if (a_type->npriorities <= cache || !a_type->priorities[cache]) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
++ _("Cache id %1$d does not exist for level %2$d"),
++ cache, level);
++ return -1;
++ }
++
++ if (*(a_type->sizes[cache]) > 3) {
++ virReportError(VIR_ERR_XML_ERROR,
++ _("Cache level %1$d id %2$d priority value just support 0-3"),
++ level, cache);
++ return -1;
++ }
++
++ *(a_type->priorities[cache]) = *(a_type->sizes[cache]);
+ }
+ continue;
+ }
+--
+2.43.0
+
diff --git a/conf-add-support-for-memorytune-XML-processing-for-MB-h.patch b/conf-add-support-for-memorytune-XML-processing-for-MB-h.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f82233c28bf1fc1f8ca3acd74da7bad8210d2334
--- /dev/null
+++ b/conf-add-support-for-memorytune-XML-processing-for-MB-h.patch
@@ -0,0 +1,195 @@
+From f2d73e1d3b02894f40dbb9513f6ed923d2132e88 Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Mon, 4 Aug 2025 10:19:41 +0800
+Subject: [PATCH 5/6] conf: add support for memorytune XML processing for MB
+ hardlimit
+
+---
+ src/conf/domain_conf.c | 23 ++++++++++++++---
+ src/conf/schemas/domaincommon.rng | 5 ++++
+ src/util/virresctrl.c | 43 ++++++++++++++++++++++++-------
+ src/util/virresctrl.h | 3 ++-
+ 4 files changed, 59 insertions(+), 15 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 12e02e4893..6c25d62998 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -18237,6 +18237,7 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ VIR_XPATH_NODE_AUTORESTORE(ctxt)
+ unsigned int id;
+ unsigned int bandwidth;
++ unsigned int hard_limit = UINT_MAX;
+
+ ctxt->node = node;
+
+@@ -18250,6 +18251,13 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ if (virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_BANDWIDTH, id, bandwidth) < 0)
+ return -1;
+
++ if (virXMLPropUIntDefault(node, "hardlimit", 10, 0, &hard_limit, UINT_MAX) < 0)
++ return -1;
++
++ if (hard_limit != UINT_MAX &&
++ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_HARDLIMIT, id, hard_limit) < 0)
++ return -1;
++
+ return 0;
+ }
+
+@@ -26846,14 +26854,21 @@ virDomainCachetuneDefFormat(virBuffer *buf,
+
+ static int
+ virDomainMemorytuneDefFormatHelper(unsigned int id,
+- unsigned int bandwidth,
++ unsigned int *types,
++ unsigned int *values,
+ void *opaque)
+ {
+ virBuffer *buf = opaque;
++ size_t i;
+
+- virBufferAsprintf(buf,
+- "\n",
+- id, bandwidth);
++ virBufferAsprintf(buf, "\n");
+ return 0;
+ }
+
+diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
+index 7278b699e0..504147a0b5 100644
+--- a/src/conf/schemas/domaincommon.rng
++++ b/src/conf/schemas/domaincommon.rng
+@@ -1185,6 +1185,11 @@
+
+
+
++
++
++
++
++
+
+
+
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 77abf03a3c..e262deae73 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -1383,10 +1383,11 @@ virResctrlAllocForeachCache(virResctrlAlloc *alloc,
+
+ /* virResctrlAllocSetMemoryBandwidth
+ * @alloc: Pointer to an active allocation
++ * @type: type of memory bandwidth to be set
+ * @id: node id of MBA to be set
+- * @memory_bandwidth: new memory bandwidth value
++ * @value: new memory bandwidth type value
+ *
+- * Set the @memory_bandwidth for the node @id entry in the @alloc.
++ * Set the @value for the node @id entry in the @alloc.
+ *
+ * Returns 0 on success, -1 on failure with error message set.
+ */
+@@ -1394,13 +1395,13 @@ int
+ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ virMemoryType type,
+ unsigned int id,
+- unsigned int memory_bandwidth)
++ unsigned int value)
+ {
+ virResctrlAllocMemBW *mem_bw = alloc->mem_bw;
+ virResctrlAllocMemPerType *a_type = NULL;
+
+ if (type == VIR_MEMORY_TYPE_HARDLIMIT) {
+- if (memory_bandwidth > 1) {
++ if (value > 1) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Memory Bandwidth hard limit value just support 0 or 1."));
+ return -1;
+@@ -1408,7 +1409,7 @@ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ }
+
+ if (type == VIR_MEMORY_TYPE_BANDWIDTH) {
+- if (memory_bandwidth > 100) {
++ if (value > 100) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Memory Bandwidth value exceeding 100 is invalid."));
+ return -1;
+@@ -1437,7 +1438,7 @@ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ }
+
+ a_type->user_values[id] = g_new0(unsigned int, 1);
+- *(a_type->user_values[id]) = memory_bandwidth;
++ *(a_type->user_values[id]) = value;
+
+ return 0;
+ }
+@@ -1458,18 +1459,40 @@ virResctrlAllocForeachMemory(virResctrlAlloc *alloc,
+ virResctrlAllocForeachMemoryCallback cb,
+ void *opaque)
+ {
+- size_t i = 0;
++ unsigned int i, type;
+ virResctrlAllocMemBW *mem_bw;
++ g_autofree unsigned int *types = NULL;
++ g_autofree unsigned int *values = NULL;
+
+ if (!alloc || !alloc->mem_bw)
+ return 0;
+
+ mem_bw = alloc->mem_bw;
++
++ if (!mem_bw->types)
++ return 0;
++
++ if (!mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH])
++ return 0;
++
++ types = g_new0(unsigned int, VIR_MEMORY_TYPE_LAST);
++ values = g_new0(unsigned int, VIR_MEMORY_TYPE_LAST);
++
+ for (i = 0; i < mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->nuser_values; i++) {
+- if (mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]) {
+- if (cb(i, *mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i], opaque) < 0)
+- return -1;
++ if (!mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i])
++ continue;
++
++ for (type = 0; type < VIR_MEMORY_TYPE_LAST; type++) {
++ virResctrlAllocMemPerType *a_type = mem_bw->types[type];
++ if (!a_type || a_type->nuser_values <= i || !a_type->user_values[i]) {
++ types[type] = VIR_MEMORY_TYPE_LAST;
++ continue;
++ }
++
++ types[type] = type;
++ values[type] = *(a_type->user_values[i]);
+ }
++ cb(i, types, values, opaque);
+ }
+
+ return 0;
+diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
+index 9ee76eb225..9b317187f5 100644
+--- a/src/util/virresctrl.h
++++ b/src/util/virresctrl.h
+@@ -129,7 +129,8 @@ typedef int virResctrlAllocForeachCacheCallback(unsigned int level,
+ void *opaque);
+
+ typedef int virResctrlAllocForeachMemoryCallback(unsigned int id,
+- unsigned int size,
++ unsigned int *types,
++ unsigned int *values,
+ void *opaque);
+
+ virResctrlAlloc *
+--
+2.43.0
+
diff --git a/conf-add-support-for-memorytune-XML-processing-for-MB-p.patch b/conf-add-support-for-memorytune-XML-processing-for-MB-p.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ebc7d1053b8f11ef1f1e2460c594da5921fedda8
--- /dev/null
+++ b/conf-add-support-for-memorytune-XML-processing-for-MB-p.patch
@@ -0,0 +1,109 @@
+From 3d24834d3cccccfe8e482ae3c86c10fa990d560f Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Tue, 12 Aug 2025 15:26:52 +0800
+Subject: [PATCH 6/6] conf: add support for memorytune XML processing for MB
+ priority
+
+---
+ src/conf/domain_conf.c | 8 +++++++
+ src/conf/schemas/domaincommon.rng | 5 ++++
+ src/util/virresctrl.c | 40 +++++++++++++++++++++----------
+ 3 files changed, 40 insertions(+), 13 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 6c25d62998..28b2157424 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -18238,6 +18238,7 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ unsigned int id;
+ unsigned int bandwidth;
+ unsigned int hard_limit = UINT_MAX;
++ unsigned int priority = UINT_MAX;
+
+ ctxt->node = node;
+
+@@ -18258,6 +18259,13 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_HARDLIMIT, id, hard_limit) < 0)
+ return -1;
+
++ if (virXMLPropUIntDefault(node, "priority", 10, 0, &priority, UINT_MAX) < 0)
++ return -1;
++
++ if (priority != UINT_MAX &&
++ virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_PRIORITY, id, priority) < 0)
++ return -1;
++
+ return 0;
+ }
+
+diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
+index 504147a0b5..1b2256fddf 100644
+--- a/src/conf/schemas/domaincommon.rng
++++ b/src/conf/schemas/domaincommon.rng
+@@ -1190,6 +1190,11 @@
+
+
+
++
++
++
++
++
+
+
+
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index e262deae73..06935cc11f 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -1400,20 +1400,34 @@ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ virResctrlAllocMemBW *mem_bw = alloc->mem_bw;
+ virResctrlAllocMemPerType *a_type = NULL;
+
+- if (type == VIR_MEMORY_TYPE_HARDLIMIT) {
+- if (value > 1) {
+- virReportError(VIR_ERR_XML_ERROR, "%s",
+- _("Memory Bandwidth hard limit value just support 0 or 1."));
+- return -1;
+- }
+- }
+-
+- if (type == VIR_MEMORY_TYPE_BANDWIDTH) {
+- if (value > 100) {
+- virReportError(VIR_ERR_XML_ERROR, "%s",
+- _("Memory Bandwidth value exceeding 100 is invalid."));
++ switch (type) {
++ case VIR_MEMORY_TYPE_BANDWIDTH:
++ if (value > 100) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth value exceeding 100 is invalid."));
++ return -1;
++ }
++ break;
++ case VIR_MEMORY_TYPE_HARDLIMIT:
++ if (value > 1) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth hard limit value just support 0 or 1."));
++ return -1;
++ }
++ break;
++ case VIR_MEMORY_TYPE_PRIORITY:
++ if (value > 7) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth priority value just support 0-7."));
++ return -1;
++ }
++ break;
++ case VIR_MEMORY_TYPE_MIN_BANDWIDTH:
++ case VIR_MEMORY_TYPE_LAST:
++ default:
++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++ _("Invalid memory bandwidth type."));
+ return -1;
+- }
+ }
+
+ if (!mem_bw) {
+--
+2.43.0
+
diff --git a/libvirt.spec b/libvirt.spec
index 6c5e52f1afb4deb456c719bc43707d2ffb85ead2..c4b84dad6e3629372bad5176c2bbf0f84d4143ff 100644
--- a/libvirt.spec
+++ b/libvirt.spec
@@ -262,7 +262,7 @@
Summary: Library providing a simple virtualization API
Name: libvirt
Version: 9.10.0
-Release: 17
+Release: 18
License: LGPLv2+
URL: https://libvirt.org/
@@ -341,6 +341,13 @@ Patch0067: Revert-qemu-Remove-pre-blockdev-PFLASH-setup.patch
Patch0068: qemu_command-get-disk-alias-from-disk-instead-of-nod.patch
Patch0069: migration-Add-the-VIR_MIGRATE_RETURNPATH-flag.patch
Patch0070: Revert-Automatically-unbind-all-devices-driver-under.patch
+Patch0071: util-resctrl-add-parsing-process-for-the-new-paramet.patch
+Patch0072: util-resctrl-add-L3-priority-parse-and-format-method.patch
+Patch0073: conf-add-support-for-cachetune-L3-priority.patch
+Patch0074: util-resctrl-define-memory-bandwidth-types-add-parse.patch
+Patch0075: conf-add-support-for-memorytune-XML-processing-for-MB-h.patch
+Patch0076: conf-add-support-for-memorytune-XML-processing-for-MB-p.patch
+Patch0077: conf-add-support-for-MB-min_bandwidth.patch
Requires: libvirt-daemon = %{version}-%{release}
@@ -2633,6 +2640,16 @@ exit 0
%endif
%changelog
+* Mon Aug 25 2025 lutong - 9.10.0-18
+- util: resctrl: Adapt MPAM QoS interface
+- util-resctrl-add-parsing-process-for-the-new-paramet.patch
+- util-resctrl-add-L3-priority-parse-and-format-method.patch
+- conf-add-support-for-cachetune-L3-priority.patch
+- util-resctrl-define-memory-bandwidth-types-add-parse.patch
+- conf-add-support-for-memorytune-XML-processing-for-MB-h.patch
+- conf-add-support-for-memorytune-XML-processing-for-MB-p.patch
+- conf-add-support-for-MB-min_bandwidth.patch
+
* Wed Jul 30 2025 JiaboFeng - 9.10.0-17
- Revert "Automatically unbind all devices' driver under same root port and bind to vfio-pci in the context of CVM."
diff --git a/util-resctrl-add-L3-priority-parse-and-format-method.patch b/util-resctrl-add-L3-priority-parse-and-format-method.patch
new file mode 100644
index 0000000000000000000000000000000000000000..41d1d949ef27da7e7a5b04209a26e7919c081ae4
--- /dev/null
+++ b/util-resctrl-add-L3-priority-parse-and-format-method.patch
@@ -0,0 +1,365 @@
+From fbc6ac1471bdc6ffdb5fe5e47bf6df2753acf949 Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Sat, 26 Jul 2025 17:40:03 +0800
+Subject: [PATCH 2/6] util:resctrl:add L3 priority parse and format method
+
+---
+ src/libvirt_private.syms | 2 +
+ src/util/virresctrl.c | 168 +++++++++++++++++++++++++++++++-------
+ src/util/virresctrl.h | 1 +
+ src/util/virresctrlpriv.h | 7 ++
+ tests/virresctrltest.c | 5 ++
+ 5 files changed, 153 insertions(+), 30 deletions(-)
+
+diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
+index cd99cc302a..a3799a0108 100644
+--- a/src/libvirt_private.syms
++++ b/src/libvirt_private.syms
+@@ -3252,11 +3252,13 @@ virCacheKernelTypeToString;
+ virCacheTypeFromString;
+ virCacheTypeToString;
+ virResctrlAllocAddPID;
++virResctrlAllocCopyCacheProperties;
+ virResctrlAllocCreate;
+ virResctrlAllocDeterminePath;
+ virResctrlAllocForeachCache;
+ virResctrlAllocForeachMemory;
+ virResctrlAllocFormat;
++virResctrlAllocGetDefault;
+ virResctrlAllocGetID;
+ virResctrlAllocGetUnused;
+ virResctrlAllocIsEmpty;
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 7d661e944f..3d2be7560c 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -60,6 +60,7 @@ VIR_ENUM_IMPL(virCacheKernel,
+ "Unified",
+ "Instruction",
+ "Data",
++ "Priority",
+ );
+
+ /* Cache name mapping for our XML naming. */
+@@ -68,6 +69,7 @@ VIR_ENUM_IMPL(virCache,
+ "both",
+ "code",
+ "data",
++ "priority",
+ );
+
+ /* Cache name mapping for resctrl interface naming. */
+@@ -77,6 +79,7 @@ VIR_ENUM_IMPL(virResctrl,
+ "",
+ "CODE",
+ "DATA",
++ "PRI",
+ );
+
+ /* Monitor feature name prefix mapping for monitor naming */
+@@ -313,6 +316,10 @@ struct _virResctrlAllocPerType {
+ /* cache id map for each cache */
+ unsigned int **cache_ids;
+ size_t ncache_ids;
++
++ /* priority for each cahe */
++ unsigned long long **priorities;
++ size_t npriorities;
+ };
+
+ struct _virResctrlAllocPerLevel {
+@@ -401,9 +408,13 @@ virResctrlAllocDispose(void *obj)
+ for (k = 0; k < type->ncache_ids; k++)
+ g_free(type->cache_ids[k]);
+
++ for (k = 0; k < type->npriorities; k++)
++ g_free(type->priorities[k]);
++
+ g_free(type->sizes);
+ g_free(type->masks);
+ g_free(type->cache_ids);
++ g_free(type->priorities);
+ g_free(type);
+ }
+ g_free(level->types);
+@@ -1518,19 +1529,25 @@ virResctrlAllocFormatCache(virResctrlAlloc *alloc,
+
+ virBufferAsprintf(buf, "L%u%s:", level, virResctrlTypeToString(type));
+
+- for (cache = 0; cache < a_type->nmasks; cache++) {
+- virBitmap *mask = a_type->masks[cache];
+- char *mask_str = NULL;
+-
+- if (!mask)
+- continue;
+-
+- mask_str = virBitmapToString(mask);
+- if (!mask_str)
+- return -1;
+-
+- virBufferAsprintf(buf, "%u=%s;", *a_type->cache_ids[cache], mask_str);
+- VIR_FREE(mask_str);
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ for (cache = 0; cache < a_type->npriorities; cache++) {
++ virBufferAsprintf(buf, "%u=%llu;", *(a_type->cache_ids[cache]), *(a_type->priorities[cache]));
++ }
++ } else {
++ for (cache = 0; cache < a_type->nmasks; cache++) {
++ virBitmap *mask = a_type->masks[cache];
++ char *mask_str = NULL;
++
++ if (!mask)
++ continue;
++
++ mask_str = virBitmapToString(mask);
++ if (!mask_str)
++ return -1;
++
++ virBufferAsprintf(buf, "%u=%s;", *(a_type->cache_ids[cache]), mask_str);
++ VIR_FREE(mask_str);
++ }
+ }
+
+ virBufferTrim(buf, ";");
+@@ -1604,6 +1621,41 @@ virResctrlAllocParseProcessCacheId(virResctrlInfo *resctrl,
+ }
+
+
++static int
++virResctrlAllocParseProcessCachePriority(virResctrlAlloc *alloc,
++ unsigned int level,
++ virCacheType type,
++ char *value,
++ unsigned int node_id)
++{
++ unsigned int priority = 0;
++ virResctrlAllocPerType *a_type = NULL;
++
++ if (virStrToLong_uip(value, NULL, 10, &priority) < 0) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("Invalid priority '%1$s'"), value);
++ return -1;
++ }
++
++ a_type = virResctrlAllocGetType(alloc, level, type);
++
++ if (!a_type)
++ return -1;
++
++ if (a_type->npriorities <= node_id) {
++ VIR_EXPAND_N(a_type->priorities, a_type->npriorities,
++ node_id - a_type->npriorities + 1);
++ }
++
++ if (!a_type->priorities[node_id])
++ a_type->priorities[node_id] = g_new0(unsigned long long, 1);
++
++ *(a_type->priorities[node_id]) = priority;
++
++ return 0;
++}
++
++
+ static int
+ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+ virResctrlAlloc *alloc,
+@@ -1613,7 +1665,6 @@ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+ unsigned int node_id)
+ {
+ char *tmp = strchr(cache, '=');
+- unsigned int cache_id = 0;
+ g_autoptr(virBitmap) mask = NULL;
+
+ if (!tmp)
+@@ -1626,10 +1677,8 @@ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+ return -1;
+ }
+
+- if (virStrToLong_uip(cache, NULL, 10, &cache_id) < 0) {
+- virReportError(VIR_ERR_INTERNAL_ERROR,
+- _("Invalid cache id '%1$s'"), cache);
+- return -1;
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ return virResctrlAllocParseProcessCachePriority(alloc, level, type, tmp, node_id);
+ }
+
+ mask = virBitmapNewString(tmp);
+@@ -1762,7 +1811,7 @@ virResctrlAllocGetGroup(virResctrlInfo *resctrl,
+ }
+
+
+-static virResctrlAlloc *
++virResctrlAlloc *
+ virResctrlAllocGetDefault(virResctrlInfo *resctrl)
+ {
+ virResctrlAlloc *ret = NULL;
+@@ -2128,9 +2177,9 @@ virResctrlAllocCopyMemBW(virResctrlAlloc *dst,
+ }
+
+
+-static int
+-virResctrlAllocCopyCache(virResctrlAlloc *dst,
+- virResctrlAlloc *src)
++int
++virResctrlAllocCopyCacheProperties(virResctrlAlloc *dst,
++ virResctrlAlloc *src)
+ {
+ unsigned int level = 0;
+
+@@ -2153,13 +2202,6 @@ virResctrlAllocCopyCache(virResctrlAlloc *dst,
+ if (!d_type)
+ return -1;
+
+- for (cache = 0; cache < s_type->nmasks; cache++) {
+- virBitmap *mask = s_type->masks[cache];
+-
+- if (mask && virResctrlAllocUpdateMask(dst, level, type, cache, mask) < 0)
+- return -1;
+- }
+-
+ if (s_type->ncache_ids > d_type->ncache_ids)
+ VIR_EXPAND_N(d_type->cache_ids, d_type->ncache_ids,
+ s_type->ncache_ids - d_type->ncache_ids);
+@@ -2168,7 +2210,18 @@ virResctrlAllocCopyCache(virResctrlAlloc *dst,
+ if (d_type->cache_ids[cache])
+ continue;
+ d_type->cache_ids[cache] = g_new0(unsigned int, 1);
+- *d_type->cache_ids[cache] = *s_type->cache_ids[cache];
++ *(d_type->cache_ids[cache]) = *(s_type->cache_ids[cache]);
++ }
++
++ if (s_type->npriorities > d_type->npriorities)
++ VIR_EXPAND_N(d_type->priorities, d_type->npriorities,
++ s_type->npriorities - d_type->npriorities);
++
++ for (cache = 0; cache < s_type->npriorities; cache++) {
++ if (d_type->priorities[cache])
++ continue;
++ d_type->priorities[cache] = g_new0(long long unsigned int, 1);
++ *(d_type->priorities[cache]) = *(s_type->priorities[cache]);
+ }
+ }
+ }
+@@ -2177,6 +2230,44 @@ virResctrlAllocCopyCache(virResctrlAlloc *dst,
+ }
+
+
++static int
++virResctrlAllocCopyCache(virResctrlAlloc *dst,
++ virResctrlAlloc *src)
++{
++ unsigned int level = 0;
++
++ for (level = 0; level < src->nlevels; level++) {
++ virResctrlAllocPerLevel *s_level = src->levels[level];
++ unsigned int type = 0;
++
++ if (!s_level)
++ continue;
++
++ for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) {
++ virResctrlAllocPerType *s_type = s_level->types[type];
++ virResctrlAllocPerType *d_type = NULL;
++ unsigned int cache = 0;
++
++ if (!s_type)
++ continue;
++
++ d_type = virResctrlAllocGetType(dst, level, type);
++ if (!d_type)
++ return -1;
++
++ for (cache = 0; cache < s_type->nmasks; cache++) {
++ virBitmap *mask = s_type->masks[cache];
++
++ if (mask && virResctrlAllocUpdateMask(dst, level, type, cache, mask) < 0)
++ return -1;
++ }
++ }
++ }
++
++ return virResctrlAllocCopyCacheProperties(dst, src);
++}
++
++
+ /*
+ * This function is called when creating an allocation in the system.
+ * What it does is that it gets all the unused resources using
+@@ -2234,6 +2325,23 @@ virResctrlAllocAssign(virResctrlInfo *resctrl,
+ if (!a_type)
+ continue;
+
++ if (type == VIR_CACHE_TYPE_PRIORITY) {
++ for (cache = 0; cache < a_type->nsizes; cache++) {
++ if (!a_type->sizes[cache])
++ continue;
++
++ if (!a_type->priorities[cache]) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
++ _("Cache level %1$d does not support tuning for scope type '%2$s'"),
++ level, virCacheTypeToString(type));
++ return -1;
++ }
++
++ *a_type->priorities[cache] = *a_type->sizes[cache];
++ }
++ continue;
++ }
++
+ if (!f_type) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Cache level %1$d does not support tuning for scope type '%2$s'"),
+diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
+index 0e4b535f9e..af135520d7 100644
+--- a/src/util/virresctrl.h
++++ b/src/util/virresctrl.h
+@@ -27,6 +27,7 @@ typedef enum {
+ VIR_CACHE_TYPE_BOTH,
+ VIR_CACHE_TYPE_CODE,
+ VIR_CACHE_TYPE_DATA,
++ VIR_CACHE_TYPE_PRIORITY,
+
+ VIR_CACHE_TYPE_LAST
+ } virCacheType;
+diff --git a/src/util/virresctrlpriv.h b/src/util/virresctrlpriv.h
+index 803f9b62ab..646b040761 100644
+--- a/src/util/virresctrlpriv.h
++++ b/src/util/virresctrlpriv.h
+@@ -26,3 +26,10 @@
+
+ virResctrlAlloc *
+ virResctrlAllocGetUnused(virResctrlInfo *resctrl);
++
++virResctrlAlloc *
++virResctrlAllocGetDefault(virResctrlInfo *resctrl);
++
++int
++virResctrlAllocCopyCacheProperties(virResctrlAlloc *dst,
++ virResctrlAlloc *src);
+\ No newline at end of file
+diff --git a/tests/virresctrltest.c b/tests/virresctrltest.c
+index c5733a7972..d5bc37259c 100644
+--- a/tests/virresctrltest.c
++++ b/tests/virresctrltest.c
+@@ -21,6 +21,7 @@ test_virResctrlGetUnused(const void *opaque)
+ g_autofree char *system_dir = NULL;
+ g_autofree char *resctrl_dir = NULL;
+ g_autoptr(virResctrlAlloc) alloc = NULL;
++ g_autoptr(virResctrlAlloc) default_alloc = NULL;
+ g_autofree char *schemata_str = NULL;
+ g_autofree char *schemata_file = NULL;
+ g_autoptr(virCaps) caps = NULL;
+@@ -44,6 +45,10 @@ test_virResctrlGetUnused(const void *opaque)
+ }
+
+ alloc = virResctrlAllocGetUnused(caps->host.resctrl);
++ default_alloc = virResctrlAllocGetDefault(caps->host.resctrl);
++
++ if (virResctrlAllocCopyCacheProperties(alloc, default_alloc) < 0)
++ return -1;
+
+ virFileWrapperClearPrefixes();
+
+--
+2.43.0
+
diff --git a/util-resctrl-add-parsing-process-for-the-new-paramet.patch b/util-resctrl-add-parsing-process-for-the-new-paramet.patch
new file mode 100644
index 0000000000000000000000000000000000000000..fe5db7b80ba7367d08466efd14b6573c1c364a42
--- /dev/null
+++ b/util-resctrl-add-parsing-process-for-the-new-paramet.patch
@@ -0,0 +1,184 @@
+From a63abd6836364df141e2428df2f76b233ebc56aa Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Sat, 26 Jul 2025 15:49:33 +0800
+Subject: [PATCH 1/6] util: resctrl: add parsing process for the new parameters
+ cache id
+
+---
+ src/util/virresctrl.c | 83 +++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 76 insertions(+), 7 deletions(-)
+
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 30ae25c487..7d661e944f 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -309,6 +309,10 @@ struct _virResctrlAllocPerType {
+ /* Mask for each cache */
+ virBitmap **masks;
+ size_t nmasks;
++
++ /* cache id map for each cache */
++ unsigned int **cache_ids;
++ size_t ncache_ids;
+ };
+
+ struct _virResctrlAllocPerLevel {
+@@ -394,8 +398,12 @@ virResctrlAllocDispose(void *obj)
+ for (k = 0; k < type->nmasks; k++)
+ virBitmapFree(type->masks[k]);
+
++ for (k = 0; k < type->ncache_ids; k++)
++ g_free(type->cache_ids[k]);
++
+ g_free(type->sizes);
+ g_free(type->masks);
++ g_free(type->cache_ids);
+ g_free(type);
+ }
+ g_free(level->types);
+@@ -1521,7 +1529,7 @@ virResctrlAllocFormatCache(virResctrlAlloc *alloc,
+ if (!mask_str)
+ return -1;
+
+- virBufferAsprintf(buf, "%u=%s;", cache, mask_str);
++ virBufferAsprintf(buf, "%u=%s;", *a_type->cache_ids[cache], mask_str);
+ VIR_FREE(mask_str);
+ }
+
+@@ -1552,12 +1560,57 @@ virResctrlAllocFormat(virResctrlAlloc *alloc)
+ }
+
+
++static int
++virResctrlAllocParseProcessCacheId(virResctrlInfo *resctrl,
++ virResctrlAlloc *alloc,
++ unsigned int level,
++ virCacheType type,
++ char *cache,
++ unsigned int node_id)
++{
++ unsigned int cache_id = 0;
++ virResctrlAllocPerType *a_type = NULL;
++
++ if (virStrToLong_uip(cache, NULL, 10, &cache_id) < 0) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("Invalid cache id '%1$s'"), cache);
++ return -1;
++ }
++
++ if (!resctrl ||
++ level >= resctrl->nlevels ||
++ !resctrl->levels[level]) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("Missing or inconsistent resctrl info for level '%1$u'"),
++ level);
++ return -1;
++ }
++
++ a_type = virResctrlAllocGetType(alloc, level, type);
++ if (!a_type)
++ return -1;
++
++ if (a_type->ncache_ids <= node_id) {
++ VIR_EXPAND_N(a_type->cache_ids, a_type->ncache_ids,
++ node_id - a_type->ncache_ids + 1);
++ }
++
++ if (!a_type->cache_ids[node_id])
++ a_type->cache_ids[node_id] = g_new0(unsigned int, 1);
++
++ *(a_type->cache_ids[node_id]) = cache_id;
++
++ return 0;
++}
++
++
+ static int
+ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+ virResctrlAlloc *alloc,
+ unsigned int level,
+ virCacheType type,
+- char *cache)
++ char *cache,
++ unsigned int node_id)
+ {
+ char *tmp = strchr(cache, '=');
+ unsigned int cache_id = 0;
+@@ -1569,6 +1622,10 @@ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+ *tmp = '\0';
+ tmp++;
+
++ if (virResctrlAllocParseProcessCacheId(resctrl, alloc, level, type, cache, node_id) < 0) {
++ return -1;
++ }
++
+ if (virStrToLong_uip(cache, NULL, 10, &cache_id) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid cache id '%1$s'"), cache);
+@@ -1591,7 +1648,7 @@ virResctrlAllocParseProcessCache(virResctrlInfo *resctrl,
+
+ virBitmapShrink(mask, resctrl->levels[level]->types[type]->bits);
+
+- if (virResctrlAllocUpdateMask(alloc, level, type, cache_id, mask) < 0)
++ if (virResctrlAllocUpdateMask(alloc, level, type, node_id, mask) < 0)
+ return -1;
+
+ return 0;
+@@ -1607,6 +1664,7 @@ virResctrlAllocParseCacheLine(virResctrlInfo *resctrl,
+ GStrv next;
+ char *tmp = NULL;
+ unsigned int level = 0;
++ unsigned int node_id = 0;
+ int type = -1;
+
+ /* For no reason there can be spaces */
+@@ -1643,8 +1701,8 @@ virResctrlAllocParseCacheLine(virResctrlInfo *resctrl,
+ if (!caches)
+ return 0;
+
+- for (next = caches; *next; next++) {
+- if (virResctrlAllocParseProcessCache(resctrl, alloc, level, type, *next) < 0)
++ for (next = caches; *next; next++, node_id++) {
++ if (virResctrlAllocParseProcessCache(resctrl, alloc, level, type, *next, node_id) < 0)
+ return -1;
+ }
+
+@@ -2071,7 +2129,7 @@ virResctrlAllocCopyMemBW(virResctrlAlloc *dst,
+
+
+ static int
+-virResctrlAllocCopyMasks(virResctrlAlloc *dst,
++virResctrlAllocCopyCache(virResctrlAlloc *dst,
+ virResctrlAlloc *src)
+ {
+ unsigned int level = 0;
+@@ -2101,6 +2159,17 @@ virResctrlAllocCopyMasks(virResctrlAlloc *dst,
+ if (mask && virResctrlAllocUpdateMask(dst, level, type, cache, mask) < 0)
+ return -1;
+ }
++
++ if (s_type->ncache_ids > d_type->ncache_ids)
++ VIR_EXPAND_N(d_type->cache_ids, d_type->ncache_ids,
++ s_type->ncache_ids - d_type->ncache_ids);
++
++ for (cache = 0; cache < s_type->ncache_ids; cache++) {
++ if (d_type->cache_ids[cache])
++ continue;
++ d_type->cache_ids[cache] = g_new0(unsigned int, 1);
++ *d_type->cache_ids[cache] = *s_type->cache_ids[cache];
++ }
+ }
+ }
+
+@@ -2133,7 +2202,7 @@ virResctrlAllocAssign(virResctrlInfo *resctrl,
+ if (virResctrlAllocMemoryBandwidth(resctrl, alloc) < 0)
+ return -1;
+
+- if (virResctrlAllocCopyMasks(alloc, alloc_default) < 0)
++ if (virResctrlAllocCopyCache(alloc, alloc_default) < 0)
+ return -1;
+
+ if (virResctrlAllocCopyMemBW(alloc, alloc_default) < 0)
+--
+2.43.0
+
diff --git a/util-resctrl-define-memory-bandwidth-types-add-parse.patch b/util-resctrl-define-memory-bandwidth-types-add-parse.patch
new file mode 100644
index 0000000000000000000000000000000000000000..19824377e014a09a729f589cfa8715e97378ec8e
--- /dev/null
+++ b/util-resctrl-define-memory-bandwidth-types-add-parse.patch
@@ -0,0 +1,526 @@
+From b8ff1a5efe7ae9a173fb1a6fdf47f3c76de9b73a Mon Sep 17 00:00:00 2001
+From: lutong
+Date: Wed, 30 Jul 2025 15:22:05 +0800
+Subject: [PATCH 4/6] util: resctrl: define memory bandwidth types & add parse
+ and format method
+
+---
+ src/conf/domain_conf.c | 2 +-
+ src/util/virresctrl.c | 274 ++++++++++++++++++++++++++++++++---------
+ src/util/virresctrl.h | 13 +-
+ 3 files changed, 227 insertions(+), 62 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 5dbbe5cd3c..12e02e4893 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -18247,7 +18247,7 @@ virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ &bandwidth) < 0)
+ return -1;
+
+- if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0)
++ if (virResctrlAllocSetMemoryBandwidth(alloc, VIR_MEMORY_TYPE_BANDWIDTH, id, bandwidth) < 0)
+ return -1;
+
+ return 0;
+diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
+index 7bdbc85bc9..77abf03a3c 100644
+--- a/src/util/virresctrl.c
++++ b/src/util/virresctrl.c
+@@ -82,6 +82,23 @@ VIR_ENUM_IMPL(virResctrl,
+ "PRI",
+ );
+
++VIR_ENUM_IMPL(virMemory,
++ VIR_MEMORY_TYPE_LAST,
++ "bandwidth",
++ "hardlimit",
++ "priority",
++ "minbandwidth",
++);
++
++VIR_ENUM_DECL(virResctrlMemory);
++VIR_ENUM_IMPL(virResctrlMemory,
++ VIR_MEMORY_TYPE_LAST,
++ "",
++ "HDL",
++ "PRI",
++ "MIN",
++);
++
+ /* Monitor feature name prefix mapping for monitor naming */
+ VIR_ENUM_IMPL(virResctrlMonitorPrefix,
+ VIR_RESCTRL_MONITOR_TYPE_LAST,
+@@ -106,6 +123,8 @@ typedef struct _virResctrlAllocPerType virResctrlAllocPerType;
+
+ typedef struct _virResctrlAllocPerLevel virResctrlAllocPerLevel;
+
++typedef struct _virResctrlAllocMemPerType virResctrlAllocMemPerType;
++
+ typedef struct _virResctrlAllocMemBW virResctrlAllocMemBW;
+
+
+@@ -333,9 +352,18 @@ struct _virResctrlAllocPerLevel {
+ * Since it can have several last level caches in a NUMA system, it is
+ * also represented as a nested sparse arrays as virRestrlAllocPerLevel.
+ */
++struct _virResctrlAllocMemPerType {
++ unsigned int **values;
++ size_t nvalues;
++
++ unsigned int **user_values;
++ size_t nuser_values;
++};
++
+ struct _virResctrlAllocMemBW {
+- unsigned int **bandwidths;
+- size_t nbandwidths;
++ virResctrlAllocMemPerType **types; /* Indexed with enum virMemoryType */
++ /* There is no `ntypes` member variable as it is always allocated for
++ * VIR_MEMORY_TYPE_LAST number of items */
+ };
+
+ struct _virResctrlAlloc {
+@@ -423,9 +451,24 @@ virResctrlAllocDispose(void *obj)
+
+ if (alloc->mem_bw) {
+ virResctrlAllocMemBW *mem_bw = alloc->mem_bw;
+- for (i = 0; i < mem_bw->nbandwidths; i++)
+- g_free(mem_bw->bandwidths[i]);
+- g_free(alloc->mem_bw->bandwidths);
++
++ for (i = 0; i < VIR_MEMORY_TYPE_LAST; i++) {
++ virResctrlAllocMemPerType *type = mem_bw->types[i];
++
++ if (!type)
++ continue;
++
++ for (j = 0; j < type->nvalues; j++)
++ g_free(type->values[j]);
++
++ for (j = 0; j < type->nuser_values; j++)
++ g_free(type->user_values[j]);
++
++ g_free(type->values);
++ g_free(type->user_values);
++ g_free(type);
++ }
++ g_free(mem_bw->types);
+ g_free(alloc->mem_bw);
+ }
+
+@@ -1104,6 +1147,28 @@ virResctrlAllocGetType(virResctrlAlloc *alloc,
+ }
+
+
++static virResctrlAllocMemPerType *
++virResctrlAllocGetMemType(virResctrlAlloc *alloc,
++ virMemoryType type)
++{
++ virResctrlAllocMemBW *mem_bw = NULL;
++
++ if (!alloc->mem_bw)
++ alloc->mem_bw = g_new0(virResctrlAllocMemBW, 1);
++
++ mem_bw = alloc->mem_bw;
++
++ if (!mem_bw->types) {
++ mem_bw->types = g_new0(virResctrlAllocMemPerType *, VIR_MEMORY_TYPE_LAST);
++ }
++
++ if (!mem_bw->types[type])
++ mem_bw->types[type] = g_new0(virResctrlAllocMemPerType, 1);
++
++ return mem_bw->types[type];
++}
++
++
+ static int
+ virResctrlAllocUpdateMask(virResctrlAlloc *alloc,
+ unsigned int level,
+@@ -1154,6 +1219,41 @@ virResctrlAllocUpdateSize(virResctrlAlloc *alloc,
+ }
+
+
++static int
++virResctrlAllocUpdateMBValue(virResctrlAlloc *alloc)
++{
++ virResctrlAllocMemBW *mem_bw = alloc->mem_bw;
++ virResctrlAllocMemPerType *a_type = NULL;
++ unsigned int type = 0;
++ unsigned int id = 0;
++
++ if (!mem_bw || !mem_bw->types)
++ return 0;
++
++ for (type = 0; type < VIR_MEMORY_TYPE_LAST; type++) {
++ a_type = mem_bw->types[type];
++ if (!a_type)
++ continue;
++
++ for (id = 0; id < a_type->nuser_values; id++) {
++ if (!a_type->user_values[id])
++ continue;
++
++ if (a_type->nvalues <= id || !a_type->values[id]) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
++ _("Node %1$u memory bandwidth does not support tuning for scope type '%2$s'"),
++ id, virMemoryTypeToString(type));
++ return -1;
++ }
++
++ *(a_type->values[id]) = *(a_type->user_values[id]);
++ }
++ }
++
++ return 0;
++}
++
++
+ /*
+ * Check if there is an allocation for this level/type/cache already. Called
+ * before updating the structure. VIR_CACHE_TYPE_BOTH collides with any type,
+@@ -1292,15 +1392,27 @@ virResctrlAllocForeachCache(virResctrlAlloc *alloc,
+ */
+ int
+ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
++ virMemoryType type,
+ unsigned int id,
+ unsigned int memory_bandwidth)
+ {
+ virResctrlAllocMemBW *mem_bw = alloc->mem_bw;
++ virResctrlAllocMemPerType *a_type = NULL;
+
+- if (memory_bandwidth > 100) {
+- virReportError(VIR_ERR_XML_ERROR, "%s",
+- _("Memory Bandwidth value exceeding 100 is invalid."));
+- return -1;
++ if (type == VIR_MEMORY_TYPE_HARDLIMIT) {
++ if (memory_bandwidth > 1) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth hard limit value just support 0 or 1."));
++ return -1;
++ }
++ }
++
++ if (type == VIR_MEMORY_TYPE_BANDWIDTH) {
++ if (memory_bandwidth > 100) {
++ virReportError(VIR_ERR_XML_ERROR, "%s",
++ _("Memory Bandwidth value exceeding 100 is invalid."));
++ return -1;
++ }
+ }
+
+ if (!mem_bw) {
+@@ -1308,19 +1420,25 @@ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
+ alloc->mem_bw = mem_bw;
+ }
+
+- if (mem_bw->nbandwidths <= id)
+- VIR_EXPAND_N(mem_bw->bandwidths, mem_bw->nbandwidths,
+- id - mem_bw->nbandwidths + 1);
++ a_type = virResctrlAllocGetMemType(alloc, type);
++ if (!a_type)
++ return -1;
+
+- if (mem_bw->bandwidths[id]) {
++ if (a_type->nuser_values <= id)
++ VIR_EXPAND_N(a_type->user_values, a_type->nuser_values,
++ id - a_type->nuser_values + 1);
++
++ if (a_type->user_values[id]) {
+ virReportError(VIR_ERR_XML_ERROR,
+- _("Memory Bandwidth already defined for node %1$u"),
++ _("Memory Bandwidth type '%1$s' already defined for node %2$u"),
++ virMemoryTypeToString(type),
+ id);
+ return -1;
+ }
+
+- mem_bw->bandwidths[id] = g_new0(unsigned int, 1);
+- *(mem_bw->bandwidths[id]) = memory_bandwidth;
++ a_type->user_values[id] = g_new0(unsigned int, 1);
++ *(a_type->user_values[id]) = memory_bandwidth;
++
+ return 0;
+ }
+
+@@ -1347,9 +1465,9 @@ virResctrlAllocForeachMemory(virResctrlAlloc *alloc,
+ return 0;
+
+ mem_bw = alloc->mem_bw;
+- for (i = 0; i < mem_bw->nbandwidths; i++) {
+- if (mem_bw->bandwidths[i]) {
+- if (cb(i, *mem_bw->bandwidths[i], opaque) < 0)
++ for (i = 0; i < mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->nuser_values; i++) {
++ if (mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]) {
++ if (cb(i, *mem_bw->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i], opaque) < 0)
+ return -1;
+ }
+ }
+@@ -1408,22 +1526,26 @@ static int
+ virResctrlAllocMemoryBandwidthFormat(virResctrlAlloc *alloc,
+ virBuffer *buf)
+ {
+- size_t i;
++ unsigned int i, type;
+
+ if (!alloc->mem_bw)
+ return 0;
+
+- virBufferAddLit(buf, "MB:");
++ for (type = 0; type < VIR_MEMORY_TYPE_LAST; type++) {
++ virResctrlAllocMemPerType *a_type = alloc->mem_bw->types[type];
++ if (!a_type)
++ continue;
++
++ virBufferAsprintf(buf, "MB%s:", virResctrlMemoryTypeToString(type));
+
+- for (i = 0; i < alloc->mem_bw->nbandwidths; i++) {
+- if (alloc->mem_bw->bandwidths[i]) {
+- virBufferAsprintf(buf, "%zd=%u;", i,
+- *(alloc->mem_bw->bandwidths[i]));
++ for (i = 0; i < a_type->nvalues; i++) {
++ virBufferAsprintf(buf, "%u=%u;", i, *(a_type->values[i]));
+ }
++
++ virBufferTrim(buf, ";");
++ virBufferAddChar(buf, '\n');
+ }
+
+- virBufferTrim(buf, ";");
+- virBufferAddChar(buf, '\n');
+ return 0;
+ }
+
+@@ -1431,11 +1553,13 @@ virResctrlAllocMemoryBandwidthFormat(virResctrlAlloc *alloc,
+ static int
+ virResctrlAllocParseProcessMemoryBandwidth(virResctrlInfo *resctrl,
+ virResctrlAlloc *alloc,
++ virMemoryType type,
+ char *mem_bw)
+ {
+- unsigned int bandwidth;
++ unsigned int value;
+ unsigned int id;
+ char *tmp = NULL;
++ virResctrlAllocMemPerType *a_type = NULL;
+
+ tmp = strchr(mem_bw, '=');
+ if (!tmp)
+@@ -1448,26 +1572,32 @@ virResctrlAllocParseProcessMemoryBandwidth(virResctrlInfo *resctrl,
+ _("Invalid node id %1$u "), id);
+ return -1;
+ }
+- if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) {
++ if (virStrToLong_uip(tmp, NULL, 10, &value) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+- _("Invalid bandwidth %1$u"), bandwidth);
++ _("Invalid MB%1$s value %2$u"),
++ virResctrlMemoryTypeToString(type), value);
+ return -1;
+ }
+- if (bandwidth < resctrl->membw_info->min_bandwidth ||
++ if ((type == VIR_MEMORY_TYPE_BANDWIDTH && value < resctrl->membw_info->min_bandwidth) ||
+ id > resctrl->membw_info->max_id) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing or inconsistent resctrl info for memory bandwidth node '%1$u'"),
+ id);
+ return -1;
+ }
+- if (alloc->mem_bw->nbandwidths <= id) {
+- VIR_EXPAND_N(alloc->mem_bw->bandwidths, alloc->mem_bw->nbandwidths,
+- id - alloc->mem_bw->nbandwidths + 1);
++
++ a_type = virResctrlAllocGetMemType(alloc, type);
++ if (!a_type)
++ return -1;
++
++ if (a_type->nvalues <= id) {
++ VIR_EXPAND_N(a_type->values, a_type->nvalues,
++ id - a_type->nvalues + 1);
+ }
+- if (!alloc->mem_bw->bandwidths[id])
+- alloc->mem_bw->bandwidths[id] = g_new0(unsigned int, 1);
++ if (!a_type->values[id])
++ a_type->values[id] = g_new0(unsigned int, 1);
+
+- *(alloc->mem_bw->bandwidths[id]) = bandwidth;
++ *(a_type->values[id]) = value;
+ return 0;
+ }
+
+@@ -1483,6 +1613,7 @@ virResctrlAllocParseMemoryBandwidthLine(virResctrlInfo *resctrl,
+ g_auto(GStrv) mbs = NULL;
+ GStrv next;
+ char *tmp = NULL;
++ int type = -1;
+
+ /* For no reason there can be spaces */
+ virSkipSpaces((const char **) &line);
+@@ -1504,11 +1635,19 @@ virResctrlAllocParseMemoryBandwidthLine(virResctrlInfo *resctrl,
+ tmp = strchr(line, ':');
+ if (!tmp)
+ return 0;
++ *tmp = '\0';
+ tmp++;
+
++ type = virResctrlMemoryTypeFromString(line + 2);
++ if (type < 0) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("Cannot parse resctrl schemata line '%1$s'"), line);
++ return -1;
++ }
++
+ mbs = g_strsplit(tmp, ";", 0);
+ for (next = mbs; *next; next++) {
+- if (virResctrlAllocParseProcessMemoryBandwidth(resctrl, alloc, *next) < 0)
++ if (virResctrlAllocParseProcessMemoryBandwidth(resctrl, alloc, type, *next) < 0)
+ return -1;
+ }
+
+@@ -1908,14 +2047,19 @@ virResctrlAllocNewFromInfo(virResctrlInfo *info)
+
+ /* set default free memory bandwidth to 100% */
+ if (info->membw_info) {
++ virResctrlAllocMemPerType *type = NULL;
+ ret->mem_bw = g_new0(virResctrlAllocMemBW, 1);
+
+- VIR_EXPAND_N(ret->mem_bw->bandwidths, ret->mem_bw->nbandwidths,
++ type = virResctrlAllocGetMemType(ret, VIR_MEMORY_TYPE_BANDWIDTH);
++ if (!type)
++ return NULL;
++
++ VIR_EXPAND_N(type->values, type->nvalues,
+ info->membw_info->max_id + 1);
+
+- for (i = 0; i < ret->mem_bw->nbandwidths; i++) {
+- ret->mem_bw->bandwidths[i] = g_new0(unsigned int, 1);
+- *(ret->mem_bw->bandwidths[i]) = 100;
++ for (i = 0; i < type->nvalues; i++) {
++ type->values[i] = g_new0(unsigned int, 1);
++ *(type->values[i]) = 100;
+ }
+ }
+
+@@ -2126,21 +2270,21 @@ virResctrlAllocMemoryBandwidth(virResctrlInfo *resctrl,
+ return -1;
+ }
+
+- for (i = 0; i < mem_bw_alloc->nbandwidths; i++) {
+- if (!mem_bw_alloc->bandwidths[i])
++ for (i = 0; i < mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->nuser_values; i++) {
++ if (!mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i])
+ continue;
+
+- if (*(mem_bw_alloc->bandwidths[i]) % mem_bw_info->bandwidth_granularity) {
++ if (*(mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]) % mem_bw_info->bandwidth_granularity) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Memory Bandwidth allocation of size %1$u is not divisible by granularity %2$u"),
+- *(mem_bw_alloc->bandwidths[i]),
++ *(mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]),
+ mem_bw_info->bandwidth_granularity);
+ return -1;
+ }
+- if (*(mem_bw_alloc->bandwidths[i]) < mem_bw_info->min_bandwidth) {
++ if (*(mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]) < mem_bw_info->min_bandwidth) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Memory Bandwidth allocation of size %1$u is smaller than the minimum allowed allocation %2$u"),
+- *(mem_bw_alloc->bandwidths[i]),
++ *(mem_bw_alloc->types[VIR_MEMORY_TYPE_BANDWIDTH]->user_values[i]),
+ mem_bw_info->min_bandwidth);
+ return -1;
+ }
+@@ -2160,26 +2304,33 @@ virResctrlAllocCopyMemBW(virResctrlAlloc *dst,
+ virResctrlAlloc *src)
+ {
+ size_t i = 0;
+- virResctrlAllocMemBW *dst_bw = NULL;
++ unsigned int type = 0;
+ virResctrlAllocMemBW *src_bw = src->mem_bw;
+
+ if (!src->mem_bw)
+ return 0;
+
+- if (!dst->mem_bw)
+- dst->mem_bw = g_new0(virResctrlAllocMemBW, 1);
++ for (type = 0; type < VIR_MEMORY_TYPE_LAST; type++) {
++ virResctrlAllocMemPerType *s_type = src_bw->types[type];
++ virResctrlAllocMemPerType *d_type = NULL;
+
+- dst_bw = dst->mem_bw;
++ if (!s_type)
++ continue;
+
+- if (src_bw->nbandwidths > dst_bw->nbandwidths)
+- VIR_EXPAND_N(dst_bw->bandwidths, dst_bw->nbandwidths,
+- src_bw->nbandwidths - dst_bw->nbandwidths);
++ d_type = virResctrlAllocGetMemType(dst, type);
++ if (!d_type)
++ return -1;
+
+- for (i = 0; i < src_bw->nbandwidths; i++) {
+- if (dst_bw->bandwidths[i])
+- continue;
+- dst_bw->bandwidths[i] = g_new0(unsigned int, 1);
+- *dst_bw->bandwidths[i] = *src_bw->bandwidths[i];
++ if (s_type->nvalues > d_type->nvalues)
++ VIR_EXPAND_N(d_type->values, d_type->nvalues,
++ s_type->nvalues - d_type->nvalues);
++
++ for (i = 0; i < s_type->nvalues; i++) {
++ if (d_type->values[i])
++ continue;
++ d_type->values[i] = g_new0(unsigned int, 1);
++ *(d_type->values[i]) = *(s_type->values[i]);
++ }
+ }
+
+ return 0;
+@@ -2308,6 +2459,9 @@ virResctrlAllocAssign(virResctrlInfo *resctrl,
+ if (virResctrlAllocCopyMemBW(alloc, alloc_default) < 0)
+ return -1;
+
++ if (virResctrlAllocUpdateMBValue(alloc) < 0)
++ return -1;
++
+ for (level = 0; level < alloc->nlevels; level++) {
+ virResctrlAllocPerLevel *a_level = alloc->levels[level];
+ virResctrlAllocPerLevel *f_level = NULL;
+diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
+index af135520d7..9ee76eb225 100644
+--- a/src/util/virresctrl.h
++++ b/src/util/virresctrl.h
+@@ -32,8 +32,18 @@ typedef enum {
+ VIR_CACHE_TYPE_LAST
+ } virCacheType;
+
++typedef enum {
++ VIR_MEMORY_TYPE_BANDWIDTH,
++ VIR_MEMORY_TYPE_HARDLIMIT,
++ VIR_MEMORY_TYPE_PRIORITY,
++ VIR_MEMORY_TYPE_MIN_BANDWIDTH,
++
++ VIR_MEMORY_TYPE_LAST
++} virMemoryType;
++
+ VIR_ENUM_DECL(virCache);
+ VIR_ENUM_DECL(virCacheKernel);
++VIR_ENUM_DECL(virMemory);
+
+ typedef enum {
+ VIR_RESCTRL_MONITOR_TYPE_UNSUPPORT,
+@@ -142,8 +152,9 @@ virResctrlAllocForeachCache(virResctrlAlloc *alloc,
+
+ int
+ virResctrlAllocSetMemoryBandwidth(virResctrlAlloc *alloc,
++ virMemoryType type,
+ unsigned int id,
+- unsigned int memory_bandwidth);
++ unsigned int value);
+
+ int
+ virResctrlAllocForeachMemory(virResctrlAlloc *alloc,
+--
+2.43.0
+