On Mon, Aug 31, 2026 at 14:56:14 +0200, Thomas Prescher wrote:
> From: Stefan Kober <[email protected]>
>
> Make use of the numa configuration the user has configured via
> libvirt and transform it into the right cloud hypervisor API calls.
>
> On-behalf-of: SAP [email protected]
> On-behalf-of: SAP [email protected]
>
> Signed-off-by: Stefan Kober <[email protected]>
> Signed-off-by: Thomas Prescher <[email protected]>
> ---
> src/ch/ch_monitor.c | 140 +++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 138 insertions(+), 2 deletions(-)
>
> diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
> index 52f281cf73..9bd5eb0114 100644
> --- a/src/ch/ch_monitor.c
> +++ b/src/ch/ch_monitor.c
> @@ -99,6 +99,71 @@ virCHMonitorBuildCPUJson(virJSONValue *content,
> virDomainDef *vmdef)
> return 0;
> }
>
> +/**
> + * CHV example NUMA cmdline:
> + *--numa guest_numa_id=0,cpus=[0,1],memory_zones=[fast_mem] \
> + --numa guest_numa_id=1,cpus=[2,3],memory_zones=[bulk_mem] \
> + --memory-zone
> id=fast_mem,size=2G,host_numa_node=0,hugepages=on,hugepage_size=1G,prefault=on
> \
> + --memory-zone
> id=bulk_mem,size=6G,host_numa_node=1,hugepages=on,hugepage_size=2M \
If you are about to use this styling of comment use the * leaders
consistently.
> + */
> +static int
> +virCHMonitorBuildNumaJson(virJSONValue *content, virDomainDef *def)
JSON in name and one argument per line.
> +{
> + size_t ncells = virDomainNumaGetNodeCount(def->numa);
> + size_t i = 0;
> + size_t j = 0;
> + virBitmap *cpus = NULL;
> + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
> + g_autoptr(virJSONValue) numas = virJSONValueNewArray();
> +
> + if (ncells == 0) {
> + return 0;
> + }
> +
> + for (i = 0; i < ncells; i++) {
> + ssize_t lastcpu = 0;
> + g_autoptr(virJSONValue) numa = virJSONValueNewObject();
> + g_autoptr(virJSONValue) mem_zones = virJSONValueNewArray();
> + char *mem_zone_str = g_strdup_printf("zone%lu", i);
> + g_autoptr(virJSONValue) mem_zone_id =
> virJSONValueNewString(mem_zone_str);
> + g_autoptr(virJSONValue) cpu_arr = virJSONValueNewArray();
> +
> + cpus = virDomainNumaGetNodeCpumask(def->numa, i);
> + lastcpu = virBitmapLastSetBit(cpus);
> +
> + // Go through bitmap and check set bits which correspond to CPUs
> + // We create an array of vCPU IDs: [1,2,3] of CPUs belonging to the
> + // respective NUMA node.
Do not use line comments (//) see coding style docs.
> + for (j = 0; j < lastcpu + 1; j++) {
> + if (virBitmapIsBitSet(cpus, j)) {
> + g_autoptr(virJSONValue) cpu_id =
> virJSONValueNewNumberUint(j);
> + if (virJSONValueArrayAppend(cpu_arr, &cpu_id) < 0)
> + return -1;
> + }
> +
> + }
> +
> + if (virJSONValueObjectAppendNumberUlong(numa, "guest_numa_id", i) <
> 0)
> + return -1;
> +
> + if (virJSONValueArrayAppend(mem_zones, &mem_zone_id) < 0)
> + return -1;
> +
> + if (virJSONValueObjectAppend(numa, "memory_zones", &mem_zones) < 0)
> + return -1;
> +
> + if (virJSONValueObjectAppend(numa, "cpus", &cpu_arr) < 0)
> + return -1;
Consider using virJSONValueObjectAdd to construct objects instead of
multiple calls.
> +
> + if (virJSONValueArrayAppend(numas, &numa) < 0)
> + return -1;
> + }
> +
> + if (virJSONValueObjectAppend(content, "numa", &numas) < 0)
> + return -1;
> + return 0;
> +}
> +
> static int
> virCHMonitorBuildConsoleJson(virJSONValue *content,
> virDomainDef *vmdef)
> @@ -222,16 +287,84 @@ virCHMonitorBuildKernelRelatedJson(virJSONValue
> *content, virDomainDef *vmdef)
> return 0;
> }
>
> +/**
> + * CHV example NUMA cmdline:
> + *--numa guest_numa_id=0,cpus=[0,1],memory_zones=[fast_mem] \
> + --numa guest_numa_id=1,cpus=[2,3],memory_zones=[bulk_mem] \
> + --memory-zone
> id=fast_mem,size=2G,host_numa_node=0,hugepages=on,hugepage_size=1G,prefault=on
> \
> + --memory-zone
> id=bulk_mem,size=6G,host_numa_node=1,hugepages=on,hugepage_size=2M \
> +
> + In this function we only define the memory zones
> + */
> +static int
> +virCHMonitorBuildMemoryZonesJson(virJSONValue *content, virDomainDef *def)
> +{
> + size_t ncells = virDomainNumaGetNodeCount(def->numa);
> + size_t i = 0;
> + g_autoptr(virJSONValue) zones = virJSONValueNewArray();
> +
> + VIR_INFO("Found NUMA cell configuration. Creating %ld NUMA nodes for
> guest VM.", ncells);
Do not use VIR_INFO. It's either VIR_DEBUG or it doesn't belong into the
log.
> +
> + for (i = 0; i < ncells; i++) {
> + g_autofree char *id = g_strdup_printf("zone%zu", i);
> + // Memory returned is in KiB, so we multiply by 1024
Everything that I wrote above applies to this function too.
> + unsigned long long memsize =
> virDomainNumaGetNodeMemorySize(def->numa, i) * 1024;
> + g_autoptr(virJSONValue) zone = virJSONValueNewObject();
> + virBitmap *nodes = virDomainNumatuneGetNodeset(def->numa, NULL, i);
> + g_autofree char *nodeset = virBitmapFormat(nodes);
> + size_t hostNodeCount = virBitmapCountBits(nodes);
> + size_t hostNode = virBitmapLastSetBit(nodes);
> +
> + if (hostNodeCount > 1) {
> + VIR_ERROR(_("There are %ld host nodes specified but Cloud
> Hypervisor only supports 1"), hostNodeCount);
VIR_ERROR is an internal macro. If you want to raise an error you must
use virReportError instead. The line is also very long.
> + return -1;
> + }
> +
> + if (virJSONValueObjectAppendString(zone, "id", id) < 0)
> + return -1;
> +
> + if (virJSONValueObjectAppendNumberUlong(zone, "size", memsize) < 0)
> + return -1;
> +
> + if (hostNodeCount == 1) {
> + VIR_DEBUG("Associating guest node %lu with host node %s", i ,
> nodeset);
> +
> + if (virJSONValueObjectAppendNumberUlong(zone, "host_numa_node",
> hostNode) < 0)
> + return -1;
> + }
> +
> + if (virJSONValueArrayAppend(zones, &zone) < 0)
> + return -1;
> + }
> + if (virJSONValueObjectAppend(content, "zones", &zones) < 0)
> + return -1;
> +
> + return 0;
> +}
> +
> static int
> virCHMonitorBuildMemoryJson(virJSONValue *content, virDomainDef *vmdef)
> {
> unsigned long long total_memory = virDomainDefGetMemoryInitial(vmdef) *
> 1024;
> + size_t ncells = virDomainNumaGetNodeCount(vmdef->numa);
>
> if (total_memory != 0) {
> g_autoptr(virJSONValue) memory = virJSONValueNewObject();
>
> - if (virJSONValueObjectAppendNumberUlong(memory, "size",
> total_memory) < 0)
> - return -1;
> + // If we have multiple NUMA nodes, then we define memory zones. When
> + // memory zones are defined, the "size" field in the CHV memory
> config
> + // must be 0
> + if (ncells >= 1) {
> + if (virCHMonitorBuildMemoryZonesJson(memory, vmdef) < 0) {
> + return -1;
> + }
> + if (virJSONValueObjectAppendNumberUlong(memory, "size", 0) < 0) {
> + return -1;
> + }
> + } else {
> + if (virJSONValueObjectAppendNumberUlong(memory, "size",
> total_memory) < 0)
> + return -1;
> + }
>
> if (virJSONValueObjectAppend(content, "memory", &memory) < 0)
> return -1;
> @@ -560,6 +693,9 @@ virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef
> *vmdef,
> if (virCHMonitorBuildMemoryJson(content, vmdef) < 0)
> return -1;
>
> + if (virCHMonitorBuildNumaJson(content, vmdef) < 0)
> + return -1;
> +
> if (virBitmapIsBitSet(driver->chCaps, CH_KERNEL_API_DEPRCATED)) {
> if (virCHMonitorBuildPayloadJson(content, vmdef) < 0)
> return -1;
> --
> 2.53.0
>