From 0e162cc4f4df610f07f4796ec3ded97dc3ce28d5 Mon Sep 17 00:00:00 2001 From: upstream Date: Sat, 26 Sep 2026 08:41:15 +0000 Subject: [PATCH] Upstream proxmox-git/qemu-server state of the touched files before 167f3d70a5 --- src/PVE/QemuServer/Memory.pm | 15 +++++++-- src/test/run_memory_tests.pl | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/test/run_memory_tests.pl diff --git a/src/PVE/QemuServer/Memory.pm b/src/PVE/QemuServer/Memory.pm index 9b7c24f..585589e 100644 --- a/src/PVE/QemuServer/Memory.pm +++ b/src/PVE/QemuServer/Memory.pm @@ -438,7 +438,16 @@ sub config { # > specify a policy other than default my $policy = $numa->{policy}; die "you need to define a policy for hostnode $hostnodes\n" if !$policy; - $mem_object .= ",host-nodes=$hostnodes,policy=$policy"; + + # QEMU splits object options at commas, so each range needs its own host-nodes key + my $host_node_ranges = join( + ',host-nodes=', + map { + my ($start, $end) = @$_; + defined($end) ? "$start-$end" : $start + } @$hostnodelists, + ); + $mem_object .= ",host-nodes=$host_node_ranges,policy=$policy"; } else { die "numa hostnodes need to be defined to use hugepages" if $conf->{hugepages}; } @@ -518,7 +527,7 @@ sub print_numa_hostnodes { my $hostnodes; foreach my $hostnoderange (@$hostnodelists) { my ($start, $end) = @$hostnoderange; - $hostnodes .= ',' if $hostnodes; + $hostnodes .= ',' if defined($hostnodes); $hostnodes .= $start; $hostnodes .= "-$end" if defined($end); $end //= $start; @@ -640,7 +649,7 @@ sub hugepages_topology { my $hostnodes = print_numa_hostnodes($hostnodelists); die "more than 1 hostnode value in numa node is not supported when hugepages are enabled" - if $hostnodes !~ m/^\d+$/; + if $hostnodes !~ m/^(\d)$/; my $hugepages_size = hugepages_size($conf, $numa_memory); $hugepages_topology->{$hugepages_size}->{$hostnodes} += hugepages_nr($numa_memory, $hugepages_size); diff --git a/src/test/run_memory_tests.pl b/src/test/run_memory_tests.pl new file mode 100644 index 0000000..e8484e8 --- /dev/null +++ b/src/test/run_memory_tests.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use lib qw(..); + +use Test::MockModule; +use Test::More; + +use PVE::QemuServer::Memory; + +my $memory_module = Test::MockModule->new('PVE::QemuServer::Memory'); +$memory_module->mock('host_numanode_exists', sub { return 1; }); +$memory_module->mock('hugepages_chunk_size_supported', sub { return 1; }); + +my $tests = [ + ['0', '0', 1], + ['9', '9', 1], + ['0;1', '0,1', 0], + ['0;10', '0,10', 0], + ['0;1;10', '0,1,10', 0], + ['1;2', '1,2', 0], + ['0-1', '0-1', 0], + ['0;1-2', '0,1-2', 0], +]; + +for my $test (@$tests) { + my ($hostnodes, $expected, $single_node) = @$test; + + subtest "host nodes $hostnodes" => sub { + my $numa_config = "cpus=0,memory=1024,hostnodes=$hostnodes,policy=bind"; + my $numa = PVE::QemuServer::Memory::parse_numa($numa_config); + is( + PVE::QemuServer::Memory::print_numa_hostnodes($numa->{hostnodes}), + $expected, + 'preserve node and range separators', + ); + + my $conf = { + numa => 1, + memory => 1024, + hugepages => 2, + numa0 => $numa_config, + }; + my $topology = eval { PVE::QemuServer::Memory::hugepages_topology($conf, 0); }; + if ($single_node) { + is($@, '', 'accept a single host node for hugepages'); + is_deeply($topology, { 2 => { $hostnodes => 512 } }, 'use the requested host node'); + } else { + like( + $@, + qr/^more than 1 hostnode value in numa node is not supported when hugepages are enabled/, + 'reject host node lists and ranges for hugepages', + ); + } + }; +} + +done_testing();