From b8ff1a5efe7ae9a173fb1a6fdf47f3c76de9b73a Mon Sep 17 00:00:00 2001
From: lutong <lutong14@huawei.com>
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.33.0