Skip to contentSkip to conversation

NUMA series, and hostmem improvements

PATCH In review Hu Tao 5 participants 50 messages May 6, 2014 – Jun 10, 2014

Subsystems (2)
lore
Share & tools
lore

Hu Tao

author
#0 lore raw

cover NUMA series, and hostmem improvements

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Changes from v3:

- address comment by Paolo
- fix bugs in string input visitor
- no conversion to memory_region_allocate_system_memory of all boards I had patch for it but don't know how to test it for all boards.

Hu Tao (7):
hostmem: add properties for NUMA memory policy
Introduce signed range.
qapi: make string input visitor parse int list
qapi: make string output visitor parse int list
qom: introduce object_property_get_enum and
object_property_get_uint16List
qmp: add query-memdev
hmp: add info memdev

Igor Mammedov (2):
qmp: allow object-add completion handler to get canonical path add memdev backend infrastructure

Luiz Capitulino (1):
man: improve -numa doc

Paolo Bonzini (15):
vl: redo -object parsing
qmp: improve error reporting for -object and object-add
pc: pass QEMUMachineInitArgs to pc_memory_init
numa: introduce memory_region_allocate_system_memory
numa: add -numa node,memdev= option
memory: reorganize file-based allocation
memory: move mem_path handling to memory_region_allocate_system_memory
memory: add error propagation to file-based RAM allocation
memory: move preallocation code out of exec.c
memory: move RAM_PREALLOC_MASK to exec.c, rename
hostmem: add file-based HostMemoryBackend
hostmem: separate allocation from UserCreatable complete method
hostmem: add merge and dump properties
hostmem: allow preallocation of any memory region
hostmem: add property to map memory with MAP_SHARED

Wanlong Gao (6):
NUMA: move numa related code to new file numa.c
NUMA: check if the total numa memory size is equal to ram_size
NUMA: Add numa_info structure to contain numa nodes info
NUMA: convert -numa option to use OptsVisitor
NUMA: expand MAX_NODES from 64 to 128
configure: add Linux libnuma detection

 Makefile.target                    |   2 +-
 backends/Makefile.objs             |   3 +
 backends/hostmem-file.c            | 134 ++++++++++++++
 backends/hostmem-ram.c             |  53 ++++++
 backends/hostmem.c                 | 349 ++++++++++++++++++++++++++++++++++++
 configure                          |  33 ++++
 cpus.c                             |  14 --
 exec.c                             | 211 +++++++++++-----------
 hmp.c                              |  36 ++++
 hmp.h                              |   1 +
 hw/i386/pc.c                       |  27 +--
 hw/i386/pc_piix.c                  |   8 +-
 hw/i386/pc_q35.c                   |   4 +-
 hw/ppc/spapr.c                     |  11 +-
 include/exec/cpu-all.h             |   8 -
 include/exec/cpu-common.h          |   2 +
 include/exec/memory.h              |  33 ++++
 include/exec/ram_addr.h            |   4 +
 include/hw/boards.h                |   6 +-
 include/hw/i386/pc.h               |   7 +-
 include/qemu/osdep.h               |  12 ++
 include/qemu/range.h               | 124 +++++++++++++
 include/qom/object.h               |  28 +++
 include/sysemu/cpus.h              |   1 -
 include/sysemu/hostmem.h           |  66 +++++++
 include/sysemu/sysemu.h            |  18 +-
 memory.c                           |  29 +++
 monitor.c                          |   9 +-
 numa.c                             | 355 +++++++++++++++++++++++++++++++++++++
 qapi-schema.json                   |  91 ++++++++++
 qapi/string-input-visitor.c        | 181 ++++++++++++++++++-
 qapi/string-output-visitor.c       | 230 ++++++++++++++++++++++--
 qemu-options.hx                    |  16 +-
 qmp-commands.hx                    |  32 ++++
 qmp.c                              |  14 +-
 qom/object.c                       |  35 ++++
 tests/test-string-input-visitor.c  |  39 ++++
 tests/test-string-output-visitor.c |  34 ++++
 util/oslib-posix.c                 |  73 ++++++++
 vl.c                               | 238 +++++--------------------
 40 files changed, 2187 insertions(+), 384 deletions(-)
 create mode 100644 backends/hostmem-file.c
 create mode 100644 backends/hostmem-ram.c
 create mode 100644 backends/hostmem.c
 create mode 100644 include/sysemu/hostmem.h
 create mode 100644 numa.c
Signature · 1 line
1.8.5.2.229.g4448466
author
#1 lore raw

PATCH 1/31 NUMA: move numa related code to new file numa.c

From: Wanlong Gao <[email protected]>

---
 Makefile.target           |   2 +-
 cpus.c                    |  14 ----
 include/exec/cpu-all.h    |   2 -
 include/exec/cpu-common.h |   2 +
 include/sysemu/cpus.h     |   1 -
 include/sysemu/sysemu.h   |   3 +
 numa.c                    | 186 ++++++++++++++++++++++++++++++++++++++++++++++
 vl.c                      | 139 +---------------------------------
 8 files changed, 193 insertions(+), 156 deletions(-)
 create mode 100644 numa.c
Makefile.target +1 -1
index ba12340..234d9cd 100644
@@ -109,7 +109,7 @@ endif #CONFIG_BSD_USER
 #########################################################
 # System emulator target
 ifdef CONFIG_SOFTMMU
-obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o
+obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
 obj-y += qtest.o
 obj-y += hw/
 obj-$(CONFIG_FDT) += device_tree.o
cpus.c +0 -14
index 7bbe153..7f87adb 100644
@@ -1313,20 +1313,6 @@ static void tcg_exec_all(void)
     exit_request = 0;
 }
 
-void set_numa_modes(void)
-{
-    CPUState *cpu;
-    int i;
-
-    CPU_FOREACH(cpu) {
-        for (i = 0; i < nb_numa_nodes; i++) {
-            if (test_bit(cpu->cpu_index, node_cpumask[i])) {
-                cpu->numa_node = i;
-            }
-        }
-    }
-}
-
 void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
 {
     /* XXX: implement xxx_cpu_list for targets that still miss it */
include/exec/cpu-all.h +0 -2
index fb649a4..758a928 100644
@@ -414,8 +414,6 @@ CPUArchState *cpu_copy(CPUArchState *env);
 
 /* memory API */
 
-extern ram_addr_t ram_size;
-
 /* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
 #define RAM_PREALLOC_MASK   (1 << 0)
 
include/exec/cpu-common.h +2 -0
index a21b65a..e8c7970 100644
@@ -45,6 +45,8 @@ typedef uintptr_t ram_addr_t;
 #  define RAM_ADDR_FMT "%" PRIxPTR
 #endif
 
+extern ram_addr_t ram_size;
+
 /* memory API */
 
 typedef void CPUWriteMemoryFunc(void *opaque, hwaddr addr, uint32_t value);
include/sysemu/cpus.h +0 -1
index 6502488..4f79081 100644
@@ -23,7 +23,6 @@ extern int smp_threads;
 #define smp_threads 1
 #endif
 
-void set_numa_modes(void);
 void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg);
 
 #endif
include/sysemu/sysemu.h +3 -0
index ba5c7f8..565c8f6 100644
@@ -144,6 +144,9 @@ extern QEMUClockType rtc_clock;
 extern int nb_numa_nodes;
 extern uint64_t node_mem[MAX_NODES];
 extern unsigned long *node_cpumask[MAX_NODES];
+void numa_add(const char *optarg);
+void set_numa_nodes(void);
+void set_numa_modes(void);
 
 #define MAX_OPTION_ROMS 16
 typedef struct QEMUOptionRom {
numa.c new file +186 -0

186 lines not on this page · raw message

vl.c +1 -138

151 lines not on this page · raw message

Signature · 1 line
1.8.5.2.229.g4448466
author
#2 lore raw

PATCH 2/31 NUMA: check if the total numa memory size is equal to ram_size

In reply to Hu Tao

From: Wanlong Gao <[email protected]>

If the total number of the assigned numa nodes memory is not equal to the assigned ram size, it will write the wrong data to ACPI table, then the guest will ignore the wrong ACPI table and recognize all memory to one node. It's buggy, we should check it to ensure that we write the right data to ACPI table.

---
 numa.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
numa.c +14 -0
index 395c14f..1d2f761 100644
@@ -27,6 +27,8 @@
 #include "exec/cpu-common.h"
 #include "qemu/bitmap.h"
 #include "qom/cpu.h"
+#include "qemu/error-report.h"
+#include "include/exec/cpu-common.h" /* for RAM_ADDR_FMT */
 
 static void numa_node_parse_cpus(int nodenr, const char *cpus)
 {
@@ -127,6 +129,7 @@ void numa_add(const char *optarg)
 void set_numa_nodes(void)
 {
     if (nb_numa_nodes > 0) {
+        uint64_t numa_total;
         int i;
 
         if (nb_numa_nodes > MAX_NODES) {
@@ -154,6 +157,17 @@ void set_numa_nodes(void)
             node_mem[i] = ram_size - usedmem;
         }
 
+        numa_total = 0;
+        for (i = 0; i < nb_numa_nodes; i++) {
+            numa_total += node_mem[i];
+        }
+        if (numa_total != ram_size) {
+            error_report("qemu: total memory size for NUMA nodes (%" PRIu64 ")"
+                         " should equal to ram_size (" RAM_ADDR_FMT ")\n",
+                         numa_total, ram_size);
+            exit(1);
+        }
+
         for (i = 0; i < nb_numa_nodes; i++) {
             if (!bitmap_empty(node_cpumask[i], MAX_CPUMASK_BITS)) {
                 break;
Signature · 1 line
1.8.5.2.229.g4448466
author
#3 lore raw

PATCH 3/31 NUMA: Add numa_info structure to contain numa nodes info

In reply to Hu Tao

From: Wanlong Gao <[email protected]>

Add the numa_info structure to contain the numa nodes memory, VCPUs information and the future added numa nodes host memory policies.

---
 hw/i386/pc.c            | 12 ++++++++----
 hw/ppc/spapr.c          | 11 ++++++-----
 include/sysemu/sysemu.h |  8 ++++++--
 monitor.c               |  2 +-
 numa.c                  | 23 ++++++++++++-----------
 vl.c                    |  7 +++----
 6 files changed, 36 insertions(+), 27 deletions(-)
hw/i386/pc.c +8 -4
index 14f0d91..249e504 100644
@@ -675,14 +675,14 @@ static FWCfgState *bochs_bios_init(void)
         unsigned int apic_id = x86_cpu_apic_id_from_index(i);
         assert(apic_id < apic_id_limit);
         for (j = 0; j < nb_numa_nodes; j++) {
-            if (test_bit(i, node_cpumask[j])) {
+            if (test_bit(i, numa_info[j].node_cpu)) {
                 numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
                 break;
             }
         }
     }
     for (i = 0; i < nb_numa_nodes; i++) {
-        numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(node_mem[i]);
+        numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem);
     }
     fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
                      (1 + apic_id_limit + nb_numa_nodes) *
@@ -1093,8 +1093,12 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
     guest_info->apic_id_limit = pc_apic_id_limit(max_cpus);
     guest_info->apic_xrupt_override = kvm_allows_irq0_override();
     guest_info->numa_nodes = nb_numa_nodes;
-    guest_info->node_mem = g_memdup(node_mem, guest_info->numa_nodes *
+    guest_info->node_mem = g_malloc0(guest_info->numa_nodes *
                                     sizeof *guest_info->node_mem);
+    for (i = 0; i < nb_numa_nodes; i++) {
+        guest_info->node_mem[i] = numa_info[i].node_mem;
+    }
+
     guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit *
                                      sizeof *guest_info->node_cpu);
 
@@ -1102,7 +1106,7 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
         unsigned int apic_id = x86_cpu_apic_id_from_index(i);
         assert(apic_id < guest_info->apic_id_limit);
         for (j = 0; j < nb_numa_nodes; j++) {
-            if (test_bit(i, node_cpumask[j])) {
+            if (test_bit(i, numa_info[j].node_cpu)) {
                 guest_info->node_cpu[apic_id] = j;
                 break;
             }
hw/ppc/spapr.c +6 -5
index a11e121..89f71a8 100644
@@ -538,8 +538,8 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
     int i, off;
 
     /* memory node(s) */
-    if (nb_numa_nodes > 1 && node_mem[0] < ram_size) {
-        node0_size = node_mem[0];
+    if (nb_numa_nodes > 1 && numa_info[0].node_mem < ram_size) {
+        node0_size = numa_info[0].node_mem;
     } else {
         node0_size = ram_size;
     }
@@ -577,7 +577,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
         if (mem_start >= ram_size) {
             node_size = 0;
         } else {
-            node_size = node_mem[i];
+            node_size = numa_info[i].node_mem;
             if (node_size > ram_size - mem_start) {
                 node_size = ram_size - mem_start;
             }
@@ -722,7 +722,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr)
 
     /* Update the RMA size if necessary */
     if (spapr->vrma_adjust) {
-        hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
+        hwaddr node0_size = (nb_numa_nodes > 1) ?
+            numa_info[0].node_mem : ram_size;
         spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift);
     }
 }
@@ -1155,7 +1156,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     hwaddr rma_alloc_size;
-    hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
+    hwaddr node0_size = (nb_numa_nodes > 1) ? numa_info[0].node_mem : ram_size;
     uint32_t initrd_base = 0;
     long kernel_size = 0, initrd_size = 0;
     long load_limit, rtas_limit, fw_size;
include/sysemu/sysemu.h +6 -2
index 565c8f6..3a9308b 100644
@@ -9,6 +9,7 @@
 #include "qapi-types.h"
 #include "qemu/notify.h"
 #include "qemu/main-loop.h"
+#include "qemu/bitmap.h"
 
 /* vl.c */
 
@@ -142,8 +143,11 @@ extern QEMUClockType rtc_clock;
 #define MAX_CPUMASK_BITS 255
 
 extern int nb_numa_nodes;
-extern uint64_t node_mem[MAX_NODES];
-extern unsigned long *node_cpumask[MAX_NODES];
+typedef struct node_info {
+    uint64_t node_mem;
+    DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS);
+} NodeInfo;
+extern NodeInfo numa_info[MAX_NODES];
 void numa_add(const char *optarg);
 void set_numa_nodes(void);
 void set_numa_modes(void);
monitor.c +1 -1
index 1266ba0..db17a31 100644
@@ -2005,7 +2005,7 @@ static void do_info_numa(Monitor *mon, const QDict *qdict)
         }
         monitor_printf(mon, "\n");
         monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
-            node_mem[i] >> 20);
+            numa_info[i].node_mem >> 20);
     }
 }
 
numa.c +12 -11
index 1d2f761..48d8bcc 100644
@@ -66,7 +66,7 @@ static void numa_node_parse_cpus(int nodenr, const char *cpus)
         goto error;
     }
 
-    bitmap_set(node_cpumask[nodenr], value, endvalue-value+1);
+    bitmap_set(numa_info[nodenr].node_cpu, value, endvalue-value+1);
     return;
 
 error:
@@ -106,7 +106,7 @@ void numa_add(const char *optarg)
         }
 
         if (get_param_value(option, 128, "mem", optarg) == 0) {
-            node_mem[nodenr] = 0;
+            numa_info[nodenr].node_mem = 0;
         } else {
             int64_t sval;
             sval = strtosz(option, &endptr);
@@ -114,7 +114,7 @@ void numa_add(const char *optarg)
                 fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
                 exit(1);
             }
-            node_mem[nodenr] = sval;
+            numa_info[nodenr].node_mem = sval;
         }
         if (get_param_value(option, 128, "cpus", optarg) != 0) {
             numa_node_parse_cpus(nodenr, option);
@@ -140,7 +140,7 @@ void set_numa_nodes(void)
          * and distribute the available memory equally across all nodes
          */
         for (i = 0; i < nb_numa_nodes; i++) {
-            if (node_mem[i] != 0) {
+            if (numa_info[i].node_mem != 0) {
                 break;
             }
         }
@@ -151,15 +151,16 @@ void set_numa_nodes(void)
              * the final node gets the rest.
              */
             for (i = 0; i < nb_numa_nodes - 1; i++) {
-                node_mem[i] = (ram_size / nb_numa_nodes) & ~((1 << 23UL) - 1);
-                usedmem += node_mem[i];
+                numa_info[i].node_mem = (ram_size / nb_numa_nodes) &
+                                        ~((1 << 23UL) - 1);
+                usedmem += numa_info[i].node_mem;
             }
-            node_mem[i] = ram_size - usedmem;
+            numa_info[i].node_mem = ram_size - usedmem;
         }
 
         numa_total = 0;
         for (i = 0; i < nb_numa_nodes; i++) {
-            numa_total += node_mem[i];
+            numa_total += numa_info[i].node_mem;
         }
         if (numa_total != ram_size) {
             error_report("qemu: total memory size for NUMA nodes (%" PRIu64 ")"
@@ -169,7 +170,7 @@ void set_numa_nodes(void)
         }
 
         for (i = 0; i < nb_numa_nodes; i++) {
-            if (!bitmap_empty(node_cpumask[i], MAX_CPUMASK_BITS)) {
+            if (!bitmap_empty(numa_info[i].node_cpu, MAX_CPUMASK_BITS)) {
                 break;
             }
         }
@@ -179,7 +180,7 @@ void set_numa_nodes(void)
          */
         if (i == nb_numa_nodes) {
             for (i = 0; i < max_cpus; i++) {
-                set_bit(i, node_cpumask[i % nb_numa_nodes]);
+                set_bit(i, numa_info[i % nb_numa_nodes].node_cpu);
             }
         }
     }
@@ -192,7 +193,7 @@ void set_numa_modes(void)
 
     CPU_FOREACH(cpu) {
         for (i = 0; i < nb_numa_nodes; i++) {
-            if (test_bit(cpu->cpu_index, node_cpumask[i])) {
+            if (test_bit(cpu->cpu_index, numa_info[i].node_cpu)) {
                 cpu->numa_node = i;
             }
         }
vl.c +3 -4
index 303884c..77e77b5 100644
@@ -195,8 +195,7 @@ static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
     QTAILQ_HEAD_INITIALIZER(fw_boot_order);
 
 int nb_numa_nodes;
-uint64_t node_mem[MAX_NODES];
-unsigned long *node_cpumask[MAX_NODES];
+NodeInfo numa_info[MAX_NODES];
 
 uint8_t qemu_uuid[16];
 bool qemu_uuid_set;
@@ -2929,8 +2928,8 @@ int main(int argc, char **argv, char **envp)
     translation = BIOS_ATA_TRANSLATION_AUTO;
 
     for (i = 0; i < MAX_NODES; i++) {
-        node_mem[i] = 0;
-        node_cpumask[i] = bitmap_new(MAX_CPUMASK_BITS);
+        numa_info[i].node_mem = 0;
+        bitmap_zero(numa_info[i].node_cpu, MAX_CPUMASK_BITS);
     }
 
     nb_numa_nodes = 0;
Signature · 1 line
1.8.5.2.229.g4448466
author
#4 lore raw

PATCH 4/31 NUMA: convert -numa option to use OptsVisitor

In reply to Hu Tao

From: Wanlong Gao <[email protected]>

---
 include/sysemu/sysemu.h |   3 +-
 numa.c                  | 145 +++++++++++++++++++++++-------------------------
 qapi-schema.json        |  32 +++++++++++
 vl.c                    |  11 +++-
 4 files changed, 114 insertions(+), 77 deletions(-)
include/sysemu/sysemu.h +2 -1
index 3a9308b..4102be3 100644
@@ -148,9 +148,10 @@ typedef struct node_info {
     DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS);
 } NodeInfo;
 extern NodeInfo numa_info[MAX_NODES];
-void numa_add(const char *optarg);
 void set_numa_nodes(void);
 void set_numa_modes(void);
+extern QemuOptsList qemu_numa_opts;
+int numa_init_func(QemuOpts *opts, void *opaque);
 
 #define MAX_OPTION_ROMS 16
 typedef struct QEMUOptionRom {
numa.c +70 -75

171 lines not on this page · raw message

qapi-schema.json +32 -0
index 0b00427..9394494 100644
@@ -4695,3 +4695,35 @@
               'btn'     : 'InputBtnEvent',
               'rel'     : 'InputMoveEvent',
               'abs'     : 'InputMoveEvent' } }
+
+##
+# @NumaOptions
+#
+# A discriminated record of NUMA options. (for OptsVisitor)
+#
+# Since 2.1
+##
+{ 'union': 'NumaOptions',
+  'data': {
+    'node': 'NumaNodeOptions' }}
+
+##
+# @NumaNodeOptions
+#
+# Create a guest NUMA node. (for OptsVisitor)
+#
+# @nodeid: #optional NUMA node ID (increase by 1 from 0 if omitted)
+#
+# @cpus: #optional VCPUs belonging to this node (assign VCPUS round-robin
+#         if omitted)
+#
+# @mem: #optional memory size of this node (equally divide total memory among
+#        nodes if omitted)
+#
+# Since: 2.1
+##
+{ 'type': 'NumaNodeOptions',
+  'data': {
+   '*nodeid': 'uint16',
+   '*cpus':   ['uint16'],
+   '*mem':    'size' }}
vl.c +10 -1
index 77e77b5..78144c4 100644
@@ -2907,6 +2907,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_realtime_opts);
     qemu_add_opts(&qemu_msg_opts);
     qemu_add_opts(&qemu_name_opts);
+    qemu_add_opts(&qemu_numa_opts);
 
     runstate_init();
 
@@ -3103,7 +3104,10 @@ int main(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_numa:
-                numa_add(optarg);
+                opts = qemu_opts_parse(qemu_find_opts("numa"), optarg, 1);
+                if (!opts) {
+                    exit(1);
+                }
                 break;
             case QEMU_OPTION_display:
                 display_type = select_display(optarg);
@@ -4225,6 +4229,11 @@ int main(int argc, char **argv, char **envp)
 
     register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
 
+    if (qemu_opts_foreach(qemu_find_opts("numa"), numa_init_func,
+                          NULL, 1) != 0) {
+        exit(1);
+    }
+
     set_numa_nodes();
 
     if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) {
Signature · 1 line
1.8.5.2.229.g4448466
author
#5 lore raw

PATCH 5/31 NUMA: expand MAX_NODES from 64 to 128

In reply to Hu Tao

From: Wanlong Gao <[email protected]>

libnuma choosed 128 for MAX_NODES, so we follow libnuma here.

---
 include/sysemu/sysemu.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
include/sysemu/sysemu.h +1 -1
index 4102be3..423d49e 100644
@@ -133,7 +133,7 @@ extern size_t boot_splash_filedata_size;
 extern uint8_t qemu_extra_params_fw[2];
 extern QEMUClockType rtc_clock;
 
-#define MAX_NODES 64
+#define MAX_NODES 128
 
 /* The following shall be true for all CPUs:
  *   cpu->cpu_index < max_cpus <= MAX_CPUMASK_BITS
Signature · 1 line
1.8.5.2.229.g4448466
author
#6 lore raw

PATCH 6/31 man: improve -numa doc

In reply to Hu Tao

From: Luiz Capitulino <[email protected]>

The -numa option documentation in qemu's manpage lacks the command-line options and some information regarding how it relates to options -m and -smp. This commit fills in the missing text.

---
 qemu-options.hx | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
qemu-options.hx +7 -3
index 781af14..c676397 100644
@@ -97,10 +97,14 @@ ETEXI
 DEF("numa", HAS_ARG, QEMU_OPTION_numa,
     "-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
 STEXI
-@item -numa @var{opts}
+@item -numa node[,mem=@var{size}][,cpus=@var{cpu[-cpu]}][,nodeid=@var{node}]
 @findex -numa
-Simulate a multi node NUMA system. If mem and cpus are omitted, resources
-are split equally.
+Simulate a multi node NUMA system. If @samp{mem}
+and @samp{cpus} are omitted, resources are split equally. Also, note
+that the -@option{numa} option doesn't allocate any of the specified
+resources. That is, it just assigns existing resources to NUMA nodes. This
+means that one still has to use the @option{-m}, @option{-smp} options
+to respectively allocate RAM and vCPUs.
 ETEXI
 
 DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd,
Signature · 1 line
1.8.5.2.229.g4448466
author
#7 lore raw

PATCH 7/31 vl: redo -object parsing

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Follow the lines of the HMP implementation, using OptsVisitor to parse the options. This gives access to OptsVisitor's rich parsing of integer lists.

---
 vl.c | 87 +++++++++++++++++++++++++++-----------------------------------------
 1 file changed, 35 insertions(+), 52 deletions(-)
vl.c +35 -52
index 78144c4..167856e 100644
@@ -115,8 +115,7 @@ int main(int argc, char **argv)
 #include "qemu/osdep.h"
 
 #include "ui/qemu-spice.h"
-#include "qapi/string-input-visitor.h"
-#include "qom/object_interfaces.h"
+#include "qapi/opts-visitor.h"
 
 #define DEFAULT_RAM_SIZE 128
 
@@ -2768,69 +2767,53 @@ static void free_and_trace(gpointer mem)
     free(mem);
 }
 
-static int object_set_property(const char *name, const char *value, void *opaque)
-{
-    Object *obj = OBJECT(opaque);
-    StringInputVisitor *siv;
-    Error *local_err = NULL;
-
-    if (strcmp(name, "qom-type") == 0 || strcmp(name, "id") == 0) {
-        return 0;
-    }
-
-    siv = string_input_visitor_new(value);
-    object_property_set(obj, string_input_get_visitor(siv), name, &local_err);
-    string_input_visitor_cleanup(siv);
-
-    if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-        return -1;
-    }
-
-    return 0;
-}
-
 static int object_create(QemuOpts *opts, void *opaque)
 {
-    const char *type = qemu_opt_get(opts, "qom-type");
-    const char *id = qemu_opts_id(opts);
-    Error *local_err = NULL;
-    Object *obj;
-
-    g_assert(type != NULL);
-
-    if (id == NULL) {
-        qerror_report(QERR_MISSING_PARAMETER, "id");
-        return -1;
+    Error *err = NULL;
+    char *type = NULL;
+    char *id = NULL;
+    void *dummy = NULL;
+    OptsVisitor *ov;
+    QDict *pdict;
+
+    ov = opts_visitor_new(opts);
+    pdict = qemu_opts_to_qdict(opts, NULL);
+
+    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+    if (err) {
+        goto out;
     }
 
-    obj = object_new(type);
-    if (qemu_opt_foreach(opts, object_set_property, obj, 1) < 0) {
-        object_unref(obj);
-        return -1;
+    qdict_del(pdict, "qom-type");
+    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+    if (err) {
+        goto out;
     }
 
-    if (!object_dynamic_cast(obj, TYPE_USER_CREATABLE)) {
-        error_setg(&local_err, "object '%s' isn't supported by -object",
-                   id);
+    qdict_del(pdict, "id");
+    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+    if (err) {
         goto out;
     }
 
-    user_creatable_complete(obj, &local_err);
-    if (local_err) {
+    object_add(type, id, pdict, opts_get_visitor(ov), &err);
+    if (err) {
         goto out;
     }
-
-    object_property_add_child(container_get(object_get_root(), "/objects"),
-                              id, obj, &local_err);
+    visit_end_struct(opts_get_visitor(ov), &err);
+    if (err) {
+        qmp_object_del(id, NULL);
+    }
 
 out:
-    object_unref(obj);
-    if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-        return -1;
+    opts_visitor_cleanup(ov);
+
+    QDECREF(pdict);
+    g_free(id);
+    g_free(type);
+    g_free(dummy);
+    if (err) {
+        qerror_report_err(err);
     }
     return 0;
 }
Signature · 1 line
1.8.5.2.229.g4448466
author
#8 lore raw

PATCH 8/31 qmp: allow object-add completion handler to get canonical path

In reply to Hu Tao

From: Igor Mammedov <[email protected]>

Add object to /objects before calling user_creatable_complete() handler, so that object might be able to call object_get_canonical_path() in its completion handler.

---
 qmp.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
qmp.c +8 -3
index 74107be..ee99d3a 100644
@@ -571,13 +571,18 @@ void object_add(const char *type, const char *id, const QDict *qdict,
         }
     }
 
-    user_creatable_complete(obj, &local_err);
+    object_property_add_child(container_get(object_get_root(), "/objects"),
+                              id, obj, &local_err);
     if (local_err) {
         goto out;
     }
 
-    object_property_add_child(container_get(object_get_root(), "/objects"),
-                              id, obj, &local_err);
+    user_creatable_complete(obj, &local_err);
+    if (local_err) {
+        object_property_del(container_get(object_get_root(), "/objects"),
+                            id, &error_abort);
+        goto out;
+    }
 out:
     if (local_err) {
         error_propagate(errp, local_err);
Signature · 1 line
1.8.5.2.229.g4448466
author
#9 lore raw

PATCH 9/31 qmp: improve error reporting for -object and object-add

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Use QERR_INVALID_PARAMETER_VALUE for consistency.

---
 qmp.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
qmp.c +2 -1
index ee99d3a..636e6f2 100644
@@ -546,7 +546,8 @@ void object_add(const char *type, const char *id, const QDict *qdict,
 
     klass = object_class_by_name(type);
     if (!klass) {
-        error_setg(errp, "invalid class name");
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  "qom-type", "a valid class name");
         return;
     }
 
Signature · 1 line
1.8.5.2.229.g4448466
author
#10 lore raw

PATCH 10/31 pc: pass QEMUMachineInitArgs to pc_memory_init

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

---
 hw/i386/pc.c         | 11 +++++------
 hw/i386/pc_piix.c    |  8 +++-----
 hw/i386/pc_q35.c     |  4 +---
 include/hw/i386/pc.h |  7 +++----
 4 files changed, 12 insertions(+), 18 deletions(-)
hw/i386/pc.c +5 -6
index 249e504..d5fb884 100644
@@ -1161,10 +1161,8 @@ void pc_acpi_init(const char *default_dsdt)
     }
 }
 
-FWCfgState *pc_memory_init(MemoryRegion *system_memory,
-                           const char *kernel_filename,
-                           const char *kernel_cmdline,
-                           const char *initrd_filename,
+FWCfgState *pc_memory_init(QEMUMachineInitArgs *args,
+                           MemoryRegion *system_memory,
                            ram_addr_t below_4g_mem_size,
                            ram_addr_t above_4g_mem_size,
                            MemoryRegion *rom_memory,
@@ -1176,7 +1174,7 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory,
     MemoryRegion *ram_below_4g, *ram_above_4g;
     FWCfgState *fw_cfg;
 
-    linux_boot = (kernel_filename != NULL);
+    linux_boot = (args->kernel_filename != NULL);
 
     /* Allocate RAM.  We allocate it as a single memory region and use
      * aliases to address portions of it, mostly for backwards compatibility
@@ -1217,7 +1215,8 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory,
     rom_set_fw(fw_cfg);
 
     if (linux_boot) {
-        load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+        load_linux(fw_cfg, args->kernel_filename, args->initrd_filename,
+                   args->kernel_cmdline, below_4g_mem_size);
     }
 
     for (i = 0; i < nb_option_roms; i++) {
hw/i386/pc_piix.c +3 -5
index 7930a26..3bd8821 100644
@@ -151,11 +151,9 @@ static void pc_init1(QEMUMachineInitArgs *args,
 
     /* allocate ram and load rom/bios */
     if (!xen_enabled()) {
-        fw_cfg = pc_memory_init(system_memory,
-                       args->kernel_filename, args->kernel_cmdline,
-                       args->initrd_filename,
-                       below_4g_mem_size, above_4g_mem_size,
-                       rom_memory, &ram_memory, guest_info);
+        fw_cfg = pc_memory_init(args, system_memory,
+                                below_4g_mem_size, above_4g_mem_size,
+                                rom_memory, &ram_memory, guest_info);
     }
 
     gsi_state = g_malloc0(sizeof(*gsi_state));
hw/i386/pc_q35.c +1 -3
index c844dc2..01794fc 100644
@@ -138,9 +138,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
 
     /* allocate ram and load rom/bios */
     if (!xen_enabled()) {
-        pc_memory_init(get_system_memory(),
-                       args->kernel_filename, args->kernel_cmdline,
-                       args->initrd_filename,
+        pc_memory_init(args, get_system_memory(),
                        below_4g_mem_size, above_4g_mem_size,
                        rom_memory, &ram_memory, guest_info);
     }
include/hw/i386/pc.h +3 -4
index 9010246..8fc0527 100644
@@ -3,6 +3,7 @@
 
 #include "qemu-common.h"
 #include "exec/memory.h"
+#include "hw/boards.h"
 #include "hw/isa/isa.h"
 #include "hw/block/fdc.h"
 #include "net/net.h"
@@ -134,10 +135,8 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
 void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
                             MemoryRegion *pci_address_space);
 
-FWCfgState *pc_memory_init(MemoryRegion *system_memory,
-                           const char *kernel_filename,
-                           const char *kernel_cmdline,
-                           const char *initrd_filename,
+FWCfgState *pc_memory_init(QEMUMachineInitArgs *args,
+                           MemoryRegion *system_memory,
                            ram_addr_t below_4g_mem_size,
                            ram_addr_t above_4g_mem_size,
                            MemoryRegion *rom_memory,
Signature · 1 line
1.8.5.2.229.g4448466
Reviewed-by on v1 10/31
#11 lore raw

Re: [Qemu-devel] [PATCH v3.1 10/31] pc: pass QEMUMachineInitArgs to pc_memory_init

Quoted 4 lines · Hu Tao

From: Paolo Bonzini <[email protected]>

Quoted 105 lines
---
 hw/i386/pc.c         | 11 +++++------
 hw/i386/pc_piix.c    |  8 +++-----
 hw/i386/pc_q35.c     |  4 +---
 include/hw/i386/pc.h |  7 +++----
 4 files changed, 12 insertions(+), 18 deletions(-)
hw/i386/pc.c +5 -6
index 249e504..d5fb884 100644
@@ -1161,10 +1161,8 @@ void pc_acpi_init(const char *default_dsdt)
     }
 }
 
-FWCfgState *pc_memory_init(MemoryRegion *system_memory,
-                           const char *kernel_filename,
-                           const char *kernel_cmdline,
-                           const char *initrd_filename,
+FWCfgState *pc_memory_init(QEMUMachineInitArgs *args,
+                           MemoryRegion *system_memory,
                            ram_addr_t below_4g_mem_size,
                            ram_addr_t above_4g_mem_size,
                            MemoryRegion *rom_memory,
@@ -1176,7 +1174,7 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory,
     MemoryRegion *ram_below_4g, *ram_above_4g;
     FWCfgState *fw_cfg;
 
-    linux_boot = (kernel_filename != NULL);
+    linux_boot = (args->kernel_filename != NULL);
 
     /* Allocate RAM.  We allocate it as a single memory region and use
      * aliases to address portions of it, mostly for backwards compatibility
@@ -1217,7 +1215,8 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory,
     rom_set_fw(fw_cfg);
 
     if (linux_boot) {
-        load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+        load_linux(fw_cfg, args->kernel_filename, args->initrd_filename,
+                   args->kernel_cmdline, below_4g_mem_size);
     }
 
     for (i = 0; i < nb_option_roms; i++) {
hw/i386/pc_piix.c +3 -5
index 7930a26..3bd8821 100644
@@ -151,11 +151,9 @@ static void pc_init1(QEMUMachineInitArgs *args,
 
     /* allocate ram and load rom/bios */
     if (!xen_enabled()) {
-        fw_cfg = pc_memory_init(system_memory,
-                       args->kernel_filename, args->kernel_cmdline,
-                       args->initrd_filename,
-                       below_4g_mem_size, above_4g_mem_size,
-                       rom_memory, &ram_memory, guest_info);
+        fw_cfg = pc_memory_init(args, system_memory,
+                                below_4g_mem_size, above_4g_mem_size,
+                                rom_memory, &ram_memory, guest_info);
     }
 
     gsi_state = g_malloc0(sizeof(*gsi_state));
hw/i386/pc_q35.c +1 -3
index c844dc2..01794fc 100644
@@ -138,9 +138,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
 
     /* allocate ram and load rom/bios */
     if (!xen_enabled()) {
-        pc_memory_init(get_system_memory(),
-                       args->kernel_filename, args->kernel_cmdline,
-                       args->initrd_filename,
+        pc_memory_init(args, get_system_memory(),
                        below_4g_mem_size, above_4g_mem_size,
                        rom_memory, &ram_memory, guest_info);
     }
include/hw/i386/pc.h +3 -4
index 9010246..8fc0527 100644
@@ -3,6 +3,7 @@
 
 #include "qemu-common.h"
 #include "exec/memory.h"
+#include "hw/boards.h"
 #include "hw/isa/isa.h"
 #include "hw/block/fdc.h"
 #include "net/net.h"
@@ -134,10 +135,8 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
 void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
                             MemoryRegion *pci_address_space);
 
-FWCfgState *pc_memory_init(MemoryRegion *system_memory,
-                           const char *kernel_filename,
-                           const char *kernel_cmdline,
-                           const char *initrd_filename,
+FWCfgState *pc_memory_init(QEMUMachineInitArgs *args,
+                           MemoryRegion *system_memory,
                            ram_addr_t below_4g_mem_size,
                            ram_addr_t above_4g_mem_size,
                            MemoryRegion *rom_memory,
Signature · 1 line
1.8.5.2.229.g4448466
author
#12 lore raw

PATCH 11/31 numa: introduce memory_region_allocate_system_memory

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

---
 hw/i386/pc.c            | 4 +---
 include/hw/boards.h     | 6 +++++-
 include/sysemu/sysemu.h | 1 +
 numa.c                  | 9 +++++++++
 4 files changed, 16 insertions(+), 4 deletions(-)
hw/i386/pc.c +1 -3
index d5fb884..571dc72 100644
@@ -1181,9 +1181,7 @@ FWCfgState *pc_memory_init(QEMUMachineInitArgs *args,
      * with older qemus that used qemu_ram_alloc().
      */
     ram = g_malloc(sizeof(*ram));
-    memory_region_init_ram(ram, NULL, "pc.ram",
-                           below_4g_mem_size + above_4g_mem_size);
-    vmstate_register_ram_global(ram);
+    memory_region_allocate_system_memory(ram, NULL, "pc.ram", args->ram_size);
     *ram_memory = ram;
     ram_below_4g = g_malloc(sizeof(*ram_below_4g));
     memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
include/hw/boards.h +5 -1
index dd2c70d..1ea52c0 100644
@@ -50,9 +50,13 @@ struct QEMUMachine {
     const char *hw_version;
 };
 
-#define TYPE_MACHINE_SUFFIX "-machine"
+void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
+                                          const char *name,
+                                          uint64_t ram_size);
+
 int qemu_register_machine(QEMUMachine *m);
 
+#define TYPE_MACHINE_SUFFIX "-machine"
 #define TYPE_MACHINE "machine"
 #undef MACHINE  /* BSD defines it and QEMU does not use it */
 #define MACHINE(obj) \
include/sysemu/sysemu.h +1 -0
index 423d49e..caf88dd 100644
@@ -10,6 +10,7 @@
 #include "qemu/notify.h"
 #include "qemu/main-loop.h"
 #include "qemu/bitmap.h"
+#include "qom/object.h"
 
 /* vl.c */
 
numa.c +9 -0
index 439df87..bcd7b04 100644
@@ -33,6 +33,7 @@
 #include "qapi/opts-visitor.h"
 #include "qapi/dealloc-visitor.h"
 #include "qapi/qmp/qerror.h"
+#include "hw/boards.h"
 
 QemuOptsList qemu_numa_opts = {
     .name = "numa",
@@ -194,3 +195,11 @@ void set_numa_modes(void)
         }
     }
 }
+
+void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
+                                          const char *name,
+                                          uint64_t ram_size)
+{
+    memory_region_init_ram(mr, owner, name, ram_size);
+    vmstate_register_ram_global(mr);
+}
Signature · 1 line
1.8.5.2.229.g4448466
author
#13 lore raw

PATCH 12/31 add memdev backend infrastructure

In reply to Hu Tao

From: Igor Mammedov <[email protected]>

Provides framework for splitting host RAM allocation/ policies into a separate backend that could be used by devices.

Initially only legacy RAM backend is provided, which uses memory_region_init_ram() allocator and compatible with every CLI option that affects memory_region_init_ram().

---
 backends/Makefile.objs   |   2 +
 backends/hostmem-ram.c   |  54 +++++++++++++++++++++++
 backends/hostmem.c       | 109 +++++++++++++++++++++++++++++++++++++++++++++++
 include/sysemu/hostmem.h |  58 +++++++++++++++++++++++++
 4 files changed, 223 insertions(+)
 create mode 100644 backends/hostmem-ram.c
 create mode 100644 backends/hostmem.c
 create mode 100644 include/sysemu/hostmem.h
backends/Makefile.objs +2 -0
index 42557d5..e6bdc11 100644
@@ -6,3 +6,5 @@ common-obj-$(CONFIG_BRLAPI) += baum.o
 $(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) 
 
 common-obj-$(CONFIG_TPM) += tpm.o
+
+common-obj-y += hostmem.o hostmem-ram.o
backends/hostmem-ram.c new file +54 -0

54 lines not on this page · raw message

backends/hostmem.c new file +109 -0

109 lines not on this page · raw message

include/sysemu/hostmem.h new file +58 -0

58 lines not on this page · raw message

Signature · 1 line
1.8.5.2.229.g4448466
author
#14 lore raw

PATCH 13/31 numa: add -numa node, memdev= option

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

This option provides the infrastructure for binding guest NUMA nodes to host NUMA nodes. For example:

-object memory-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 \
-numa node,nodeid=0,cpus=0,memdev=ram-node0 \
-object memory-ram,size=1024M,policy=interleave,host-nodes=1-3,id=ram-node1 \
-numa node,nodeid=1,cpus=1,memdev=ram-node1

The option replaces "-numa node,mem=".

---
 include/sysemu/sysemu.h |  1 +
 numa.c                  | 62 +++++++++++++++++++++++++++++++++++++++++++++++--
 qapi-schema.json        | 11 ++++++---
 qemu-options.hx         | 12 ++++++----
 4 files changed, 77 insertions(+), 9 deletions(-)
include/sysemu/sysemu.h +1 -0
index caf88dd..1e141e3 100644
@@ -147,6 +147,7 @@ extern int nb_numa_nodes;
 typedef struct node_info {
     uint64_t node_mem;
     DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS);
+    struct HostMemoryBackend *node_memdev;
 } NodeInfo;
 extern NodeInfo numa_info[MAX_NODES];
 void set_numa_nodes(void);
numa.c +60 -2

94 lines not on this page · raw message

qapi-schema.json +8 -3
index 9394494..5dd30eb 100644
@@ -4717,8 +4717,12 @@
 # @cpus: #optional VCPUs belonging to this node (assign VCPUS round-robin
 #         if omitted)
 #
-# @mem: #optional memory size of this node (equally divide total memory among
-#        nodes if omitted)
+# @mem: #optional memory size of this node; mutually exclusive with @memdev.
+#       Equally divide total memory among nodes if both @mem and @memdev are
+#       omitted.
+#
+# @memdev: #optional memory backend object.  If specified for one node,
+#          it must be specified for all nodes.
 #
 # Since: 2.1
 ##
@@ -4726,4 +4730,5 @@
   'data': {
    '*nodeid': 'uint16',
    '*cpus':   ['uint16'],
-   '*mem':    'size' }}
+   '*mem':    'size',
+   '*memdev': 'str' }}
qemu-options.hx +8 -4
index c676397..7f4ab83 100644
@@ -95,16 +95,20 @@ specifies the maximum number of hotpluggable CPUs.
 ETEXI
 
 DEF("numa", HAS_ARG, QEMU_OPTION_numa,
-    "-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
+    "-numa node[,mem=size][,memdev=id][,cpus=cpu[-cpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
 STEXI
-@item -numa node[,mem=@var{size}][,cpus=@var{cpu[-cpu]}][,nodeid=@var{node}]
+@item -numa node[,mem=@var{size}][,memdev=@var{id}][,cpus=@var{cpu[-cpu]}][,nodeid=@var{node}]
 @findex -numa
-Simulate a multi node NUMA system. If @samp{mem}
+Simulate a multi node NUMA system. If @samp{mem}, @samp{memdev}
 and @samp{cpus} are omitted, resources are split equally. Also, note
 that the -@option{numa} option doesn't allocate any of the specified
 resources. That is, it just assigns existing resources to NUMA nodes. This
 means that one still has to use the @option{-m}, @option{-smp} options
-to respectively allocate RAM and vCPUs.
+to respectively allocate RAM and vCPUs, and possibly @option{-object}
+to specify the memory backend for the @samp{memdev} suboption.
+
+@samp{mem} and @samp{memdev} are mutually exclusive.  Furthermore, if one
+node uses @samp{memdev}, all of them have to use it.
 ETEXI
 
 DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd,
Signature · 1 line
1.8.5.2.229.g4448466
author
#15 lore raw

PATCH 14/31 memory: reorganize file-based allocation

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Split the internal interface in exec.c to a separate function, and push the check on mem_path up to memory_region_init_ram.

---
 exec.c                  | 105 +++++++++++++++++++++++++++++-------------------
 include/exec/cpu-all.h  |   3 --
 include/exec/ram_addr.h |   2 +
 include/sysemu/sysemu.h |   2 +
 memory.c                |   7 +++-
 5 files changed, 73 insertions(+), 46 deletions(-)
exec.c +63 -42
index 91513c6..8ae8b95 100644
@@ -1247,56 +1247,30 @@ static int memory_try_enable_merging(void *addr, size_t len)
     return qemu_madvise(addr, len, QEMU_MADV_MERGEABLE);
 }
 
-ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
-                                   MemoryRegion *mr)
+static ram_addr_t ram_block_add(RAMBlock *new_block)
 {
-    RAMBlock *block, *new_block;
+    RAMBlock *block;
     ram_addr_t old_ram_size, new_ram_size;
 
     old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
 
-    size = TARGET_PAGE_ALIGN(size);
-    new_block = g_malloc0(sizeof(*new_block));
-    new_block->fd = -1;
-
     /* This assumes the iothread lock is taken here too.  */
     qemu_mutex_lock_ramlist();
-    new_block->mr = mr;
-    new_block->offset = find_ram_offset(size);
-    if (host) {
-        new_block->host = host;
-        new_block->flags |= RAM_PREALLOC_MASK;
-    } else if (xen_enabled()) {
-        if (mem_path) {
-            fprintf(stderr, "-mem-path not supported with Xen\n");
-            exit(1);
-        }
-        xen_ram_alloc(new_block->offset, size, mr);
-    } else {
-        if (mem_path) {
-            if (phys_mem_alloc != qemu_anon_ram_alloc) {
-                /*
-                 * file_ram_alloc() needs to allocate just like
-                 * phys_mem_alloc, but we haven't bothered to provide
-                 * a hook there.
-                 */
-                fprintf(stderr,
-                        "-mem-path not supported with this accelerator\n");
-                exit(1);
-            }
-            new_block->host = file_ram_alloc(new_block, size, mem_path);
-        }
-        if (!new_block->host) {
-            new_block->host = phys_mem_alloc(size);
+    new_block->offset = find_ram_offset(new_block->length);
+
+    if (!new_block->host) {
+        if (xen_enabled()) {
+            xen_ram_alloc(new_block->offset, new_block->length, new_block->mr);
+        } else {
+            new_block->host = phys_mem_alloc(new_block->length);
             if (!new_block->host) {
                 fprintf(stderr, "Cannot set up guest memory '%s': %s\n",
                         new_block->mr->name, strerror(errno));
                 exit(1);
             }
-            memory_try_enable_merging(new_block->host, size);
+            memory_try_enable_merging(new_block->host, new_block->length);
         }
     }
-    new_block->length = size;
 
     /* Keep the list sorted from biggest to smallest block.  */
     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
@@ -1324,18 +1298,65 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    old_ram_size, new_ram_size);
        }
     }
-    cpu_physical_memory_set_dirty_range(new_block->offset, size);
+    cpu_physical_memory_set_dirty_range(new_block->offset, new_block->length);
 
-    qemu_ram_setup_dump(new_block->host, size);
-    qemu_madvise(new_block->host, size, QEMU_MADV_HUGEPAGE);
-    qemu_madvise(new_block->host, size, QEMU_MADV_DONTFORK);
+    qemu_ram_setup_dump(new_block->host, new_block->length);
+    qemu_madvise(new_block->host, new_block->length, QEMU_MADV_HUGEPAGE);
+    qemu_madvise(new_block->host, new_block->length, QEMU_MADV_DONTFORK);
 
-    if (kvm_enabled())
-        kvm_setup_guest_memory(new_block->host, size);
+    if (kvm_enabled()) {
+        kvm_setup_guest_memory(new_block->host, new_block->length);
+    }
 
     return new_block->offset;
 }
 
+ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
+                                    const char *mem_path)
+{
+    RAMBlock *new_block;
+
+    if (xen_enabled()) {
+        fprintf(stderr, "-mem-path not supported with Xen\n");
+        exit(1);
+    }
+
+    if (phys_mem_alloc != qemu_anon_ram_alloc) {
+        /*
+         * file_ram_alloc() needs to allocate just like
+         * phys_mem_alloc, but we haven't bothered to provide
+         * a hook there.
+         */
+        fprintf(stderr,
+                "-mem-path not supported with this accelerator\n");
+        exit(1);
+    }
+
+    size = TARGET_PAGE_ALIGN(size);
+    new_block = g_malloc0(sizeof(*new_block));
+    new_block->mr = mr;
+    new_block->length = size;
+    new_block->host = file_ram_alloc(new_block, size, mem_path);
+    return ram_block_add(new_block);
+}
+
+ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
+                                   MemoryRegion *mr)
+{
+    RAMBlock *new_block;
+
+    size = TARGET_PAGE_ALIGN(size);
+    new_block = g_malloc0(sizeof(*new_block));
+    new_block->mr = mr;
+    new_block->length = size;
+    new_block->fd = -1;
+    new_block->host = host;
+    if (host) {
+        new_block->flags |= RAM_PREALLOC_MASK;
+    }
+    return ram_block_add(new_block);
+}
+
 ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr)
 {
     return qemu_ram_alloc_from_ptr(size, NULL, mr);
include/exec/cpu-all.h +0 -3
index 758a928..dfdd071 100644
@@ -442,9 +442,6 @@ typedef struct RAMList {
 } RAMList;
 extern RAMList ram_list;
 
-extern const char *mem_path;
-extern int mem_prealloc;
-
 /* Flags stored in the low bits of the TLB virtual address.  These are
    defined so that fast path ram access is all zeros.  */
 /* Zero if TLB entry is valid.  */
include/exec/ram_addr.h +2 -0
index 2edfa96..dedb258 100644
@@ -22,6 +22,8 @@
 #ifndef CONFIG_USER_ONLY
 #include "hw/xen/xen.h"
 
+ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
+                                    const char *mem_path);
 ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    MemoryRegion *mr);
 ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
include/sysemu/sysemu.h +2 -0
index 1e141e3..277230d 100644
@@ -133,6 +133,8 @@ extern uint8_t *boot_splash_filedata;
 extern size_t boot_splash_filedata_size;
 extern uint8_t qemu_extra_params_fw[2];
 extern QEMUClockType rtc_clock;
+extern const char *mem_path;
+extern int mem_prealloc;
 
 #define MAX_NODES 128
 
memory.c +6 -1
index 3f1df23..daaeb7e 100644
@@ -23,6 +23,7 @@
 
 #include "exec/memory-internal.h"
 #include "exec/ram_addr.h"
+#include "sysemu/sysemu.h"
 
 //#define DEBUG_UNASSIGNED
 
@@ -1016,7 +1017,11 @@ void memory_region_init_ram(MemoryRegion *mr,
     mr->ram = true;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_ram;
-    mr->ram_addr = qemu_ram_alloc(size, mr);
+    if (mem_path) {
+        mr->ram_addr = qemu_ram_alloc_from_file(size, mr, mem_path);
+    } else {
+        mr->ram_addr = qemu_ram_alloc(size, mr);
+    }
 }
 
 void memory_region_init_ram_ptr(MemoryRegion *mr,
Signature · 1 line
1.8.5.2.229.g4448466
author
#16 lore raw

PATCH 15/31 memory: move mem_path handling to memory_region_allocate_system_memory

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Like the previous patch did in exec.c, split memory_region_init_ram and memory_region_init_ram_from_file, and push mem_path one step further up. Other RAM regions than system memory will now be backed by regular RAM.

Also, boards that do not use memory_region_allocate_system_memory will not support -mem-path anymore. This can be changed before the patches are merged by migrating boards to use the function.

---
 exec.c                | 10 ++--------
 include/exec/memory.h | 18 ++++++++++++++++++
 memory.c              | 21 ++++++++++++++++-----
 numa.c                | 11 ++++++++++-
 4 files changed, 46 insertions(+), 14 deletions(-)
exec.c +2 -8
index 8ae8b95..379472d 100644
@@ -1130,14 +1130,6 @@ error:
     }
     return NULL;
 }
-#else
-static void *file_ram_alloc(RAMBlock *block,
-                            ram_addr_t memory,
-                            const char *path)
-{
-    fprintf(stderr, "-mem-path not supported on this host\n");
-    exit(1);
-}
 #endif
 
 static ram_addr_t find_ram_offset(ram_addr_t size)
@@ -1311,6 +1303,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block)
     return new_block->offset;
 }
 
+#ifdef __linux__
 ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
                                     const char *mem_path)
 {
@@ -1339,6 +1332,7 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
     new_block->host = file_ram_alloc(new_block, size, mem_path);
     return ram_block_add(new_block);
 }
+#endif
 
 ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    MemoryRegion *mr)
include/exec/memory.h +18 -0
index c084db2..75d4635 100644
@@ -311,6 +311,24 @@ void memory_region_init_ram(MemoryRegion *mr,
                             const char *name,
                             uint64_t size);
 
+#ifdef __linux__
+/**
+ * memory_region_init_ram_from_file:  Initialize RAM memory region with a
+ *                                    mmap-ed backend.
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @owner: the object that tracks the region's reference count
+ * @name: the name of the region.
+ * @size: size of the region.
+ * @path: the path in which to allocate the RAM.
+ */
+void memory_region_init_ram_from_file(MemoryRegion *mr,
+                                      struct Object *owner,
+                                      const char *name,
+                                      uint64_t size,
+                                      const char *path);
+#endif
+
 /**
  * memory_region_init_ram_ptr:  Initialize RAM memory region from a
  *                              user-provided pointer.  Accesses into the
memory.c +16 -5
index daaeb7e..677b8f3 100644
@@ -1017,13 +1017,24 @@ void memory_region_init_ram(MemoryRegion *mr,
     mr->ram = true;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_ram;
-    if (mem_path) {
-        mr->ram_addr = qemu_ram_alloc_from_file(size, mr, mem_path);
-    } else {
-        mr->ram_addr = qemu_ram_alloc(size, mr);
-    }
+    mr->ram_addr = qemu_ram_alloc(size, mr);
 }
 
+#ifdef __linux__
+void memory_region_init_ram_from_file(MemoryRegion *mr,
+                                      struct Object *owner,
+                                      const char *name,
+                                      uint64_t size,
+                                      const char *path)
+{
+    memory_region_init(mr, owner, name, size);
+    mr->ram = true;
+    mr->terminates = true;
+    mr->destructor = memory_region_destructor_ram;
+    mr->ram_addr = qemu_ram_alloc_from_file(size, mr, path);
+}
+#endif
+
 void memory_region_init_ram_ptr(MemoryRegion *mr,
                                 Object *owner,
                                 const char *name,
numa.c +10 -1
index b9850d7..e136612 100644
@@ -229,7 +229,16 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner,
                                            const char *name,
                                            uint64_t ram_size)
 {
-    memory_region_init_ram(mr, owner, name, ram_size);
+    if (mem_path) {
+#ifdef __linux__
+        memory_region_init_ram_from_file(mr, owner, name, ram_size, mem_path);
+#else
+        fprintf(stderr, "-mem-path not supported on this host\n");
+        exit(1);
+#endif
+    } else {
+        memory_region_init_ram(mr, owner, name, ram_size);
+    }
     vmstate_register_ram_global(mr);
 }
 
Signature · 1 line
1.8.5.2.229.g4448466
author
#17 lore raw

PATCH 16/31 memory: add error propagation to file-based RAM allocation

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Right now, -mem-path will fall back to RAM-based allocation in some cases. This should never happen with "-object memory-file", prepare the code by adding correct error propagation.

---
 exec.c                  | 36 ++++++++++++++++++++++++------------
 include/exec/memory.h   |  5 ++++-
 include/exec/ram_addr.h |  2 +-
 memory.c                |  5 +++--
 numa.c                  | 13 ++++++++++++-
 5 files changed, 44 insertions(+), 17 deletions(-)
exec.c +24 -12
index 379472d..1e51b2f 100644
@@ -1021,7 +1021,8 @@ static void sigbus_handler(int signal)
 
 static void *file_ram_alloc(RAMBlock *block,
                             ram_addr_t memory,
-                            const char *path)
+                            const char *path,
+                            Error **errp)
 {
     char *filename;
     char *sanitized_name;
@@ -1040,7 +1041,8 @@ static void *file_ram_alloc(RAMBlock *block,
     }
 
     if (kvm_enabled() && !kvm_has_sync_mmu()) {
-        fprintf(stderr, "host lacks kvm mmu notifiers, -mem-path unsupported\n");
+        error_setg(errp,
+                   "host lacks kvm mmu notifiers, -mem-path unsupported\n");
         goto error;
     }
 
@@ -1057,7 +1059,8 @@ static void *file_ram_alloc(RAMBlock *block,
 
     fd = mkstemp(filename);
     if (fd < 0) {
-        perror("unable to create backing store for hugepages");
+        error_setg_errno(errp, errno,
+                         "unable to create backing store for hugepages");
         g_free(filename);
         goto error;
     }
@@ -1072,12 +1075,14 @@ static void *file_ram_alloc(RAMBlock *block,
      * If anything goes wrong with it under other filesystems,
      * mmap will fail.
      */
-    if (ftruncate(fd, memory))
+    if (ftruncate(fd, memory)) {
         perror("ftruncate");
+    }
 
     area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
     if (area == MAP_FAILED) {
-        perror("file_ram_alloc: can't mmap RAM pages");
+        error_setg_errno(errp, errno,
+                         "unable to map backing store for hugepages");
         close(fd);
         goto error;
     }
@@ -1305,13 +1310,14 @@ static ram_addr_t ram_block_add(RAMBlock *new_block)
 
 #ifdef __linux__
 ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                    const char *mem_path)
+                                    const char *mem_path,
+                                    Error **errp)
 {
     RAMBlock *new_block;
 
     if (xen_enabled()) {
-        fprintf(stderr, "-mem-path not supported with Xen\n");
-        exit(1);
+        error_setg(errp, "-mem-path not supported with Xen\n");
+        return -1;
     }
 
     if (phys_mem_alloc != qemu_anon_ram_alloc) {
@@ -1320,16 +1326,22 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
          * phys_mem_alloc, but we haven't bothered to provide
          * a hook there.
          */
-        fprintf(stderr,
-                "-mem-path not supported with this accelerator\n");
-        exit(1);
+        error_setg(errp,
+                   "-mem-path not supported with this accelerator\n");
+        return -1;
     }
 
     size = TARGET_PAGE_ALIGN(size);
     new_block = g_malloc0(sizeof(*new_block));
     new_block->mr = mr;
     new_block->length = size;
-    new_block->host = file_ram_alloc(new_block, size, mem_path);
+    new_block->host = file_ram_alloc(new_block, size,
+                                     mem_path, errp);
+    if (!new_block->host) {
+        g_free(new_block);
+        return -1;
+    }
+
     return ram_block_add(new_block);
 }
 #endif
include/exec/memory.h +4 -1
index 75d4635..4e606dd 100644
@@ -31,6 +31,7 @@
 #include "qemu/queue.h"
 #include "qemu/int128.h"
 #include "qemu/notify.h"
+#include "qapi/error.h"
 
 #define MAX_PHYS_ADDR_SPACE_BITS 62
 #define MAX_PHYS_ADDR            (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
@@ -321,12 +322,14 @@ void memory_region_init_ram(MemoryRegion *mr,
  * @name: the name of the region.
  * @size: size of the region.
  * @path: the path in which to allocate the RAM.
+ * @errp: pointer to Error*, to store an error if it happens.
  */
 void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       struct Object *owner,
                                       const char *name,
                                       uint64_t size,
-                                      const char *path);
+                                      const char *path,
+                                      Error **errp);
 #endif
 
 /**
include/exec/ram_addr.h +1 -1
index dedb258..f9518a6 100644
@@ -23,7 +23,7 @@
 #include "hw/xen/xen.h"
 
 ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                    const char *mem_path);
+                                    const char *mem_path, Error **errp);
 ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    MemoryRegion *mr);
 ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
memory.c +3 -2
index 677b8f3..6b0c116 100644
@@ -1025,13 +1025,14 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       struct Object *owner,
                                       const char *name,
                                       uint64_t size,
-                                      const char *path)
+                                      const char *path,
+                                      Error **errp)
 {
     memory_region_init(mr, owner, name, size);
     mr->ram = true;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_ram;
-    mr->ram_addr = qemu_ram_alloc_from_file(size, mr, path);
+    mr->ram_addr = qemu_ram_alloc_from_file(size, mr, path, errp);
 }
 #endif
 
numa.c +12 -1
index e136612..966cacc 100644
@@ -231,7 +231,18 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner,
 {
     if (mem_path) {
 #ifdef __linux__
-        memory_region_init_ram_from_file(mr, owner, name, ram_size, mem_path);
+        Error *err = NULL;
+        memory_region_init_ram_from_file(mr, owner, name, ram_size,
+                                         mem_path, &err);
+
+        /* Legacy behavior: if allocation failed, fall back to
+         * regular RAM allocation.
+         */
+        if (!memory_region_size(mr)) {
+            qerror_report_err(err);
+            error_free(err);
+            memory_region_init_ram(mr, owner, name, ram_size);
+        }
 #else
         fprintf(stderr, "-mem-path not supported on this host\n");
         exit(1);
Signature · 1 line
1.8.5.2.229.g4448466
author
#18 lore raw

PATCH 17/31 memory: move preallocation code out of exec.c

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

So that backends can use it.

---
 exec.c               | 44 +------------------------------
 include/qemu/osdep.h |  2 ++
 util/oslib-posix.c   | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 43 deletions(-)
exec.c +1 -43
index 1e51b2f..710e025 100644
@@ -1012,13 +1012,6 @@ static long gethugepagesize(const char *path)
     return fs.f_bsize;
 }
 
-static sigjmp_buf sigjump;
-
-static void sigbus_handler(int signal)
-{
-    siglongjmp(sigjump, 1);
-}
-
 static void *file_ram_alloc(RAMBlock *block,
                             ram_addr_t memory,
                             const char *path,
@@ -1088,42 +1081,7 @@ static void *file_ram_alloc(RAMBlock *block,
     }
 
     if (mem_prealloc) {
-        int ret, i;
-        struct sigaction act, oldact;
-        sigset_t set, oldset;
-
-        memset(&act, 0, sizeof(act));
-        act.sa_handler = &sigbus_handler;
-        act.sa_flags = 0;
-
-        ret = sigaction(SIGBUS, &act, &oldact);
-        if (ret) {
-            perror("file_ram_alloc: failed to install signal handler");
-            exit(1);
-        }
-
-        /* unblock SIGBUS */
-        sigemptyset(&set);
-        sigaddset(&set, SIGBUS);
-        pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
-
-        if (sigsetjmp(sigjump, 1)) {
-            fprintf(stderr, "file_ram_alloc: failed to preallocate pages\n");
-            exit(1);
-        }
-
-        /* MAP_POPULATE silently ignores failures */
-        for (i = 0; i < (memory/hpagesize); i++) {
-            memset(area + (hpagesize*i), 0, 1);
-        }
-
-        ret = sigaction(SIGBUS, &oldact, NULL);
-        if (ret) {
-            perror("file_ram_alloc: failed to reinstall signal handler");
-            exit(1);
-        }
-
-        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+        os_mem_prealloc(fd, area, memory);
     }
 
     block->fd = fd;
include/qemu/osdep.h +2 -0
index ffb2966..9c1a119 100644
@@ -251,4 +251,6 @@ void qemu_init_auxval(char **envp);
 
 void qemu_set_tty_echo(int fd, bool echo);
 
+void os_mem_prealloc(int fd, char *area, size_t sz);
+
 #endif
util/oslib-posix.c +73 -0

91 lines not on this page · raw message

Signature · 1 line
1.8.5.2.229.g4448466
author
#19 lore raw

PATCH 18/31 memory: move RAM_PREALLOC_MASK to exec.c, rename

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

Prepare for adding more flags. The "_MASK" suffix is unique, kill it.

---
 exec.c                 | 9 ++++++---
 include/exec/cpu-all.h | 3 ---
 2 files changed, 6 insertions(+), 6 deletions(-)
exec.c +6 -3
index 710e025..691d216 100644
@@ -70,6 +70,9 @@ AddressSpace address_space_memory;
 MemoryRegion io_mem_rom, io_mem_notdirty;
 static MemoryRegion io_mem_unassigned;
 
+/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
+#define RAM_PREALLOC   (1 << 0)
+
 #endif
 
 struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
@@ -1316,7 +1319,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
     new_block->fd = -1;
     new_block->host = host;
     if (host) {
-        new_block->flags |= RAM_PREALLOC_MASK;
+        new_block->flags |= RAM_PREALLOC;
     }
     return ram_block_add(new_block);
 }
@@ -1355,7 +1358,7 @@ void qemu_ram_free(ram_addr_t addr)
             QTAILQ_REMOVE(&ram_list.blocks, block, next);
             ram_list.mru_block = NULL;
             ram_list.version++;
-            if (block->flags & RAM_PREALLOC_MASK) {
+            if (block->flags & RAM_PREALLOC) {
                 ;
             } else if (xen_enabled()) {
                 xen_invalidate_map_cache_entry(block->host);
@@ -1387,7 +1390,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
         offset = addr - block->offset;
         if (offset < block->length) {
             vaddr = block->host + offset;
-            if (block->flags & RAM_PREALLOC_MASK) {
+            if (block->flags & RAM_PREALLOC) {
                 ;
             } else if (xen_enabled()) {
                 abort();
include/exec/cpu-all.h +0 -3
index dfdd071..6960544 100644
@@ -414,9 +414,6 @@ CPUArchState *cpu_copy(CPUArchState *env);
 
 /* memory API */
 
-/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
-#define RAM_PREALLOC_MASK   (1 << 0)
-
 typedef struct RAMBlock {
     struct MemoryRegion *mr;
     uint8_t *host;
Signature · 1 line
1.8.5.2.229.g4448466
author
#20 lore raw

PATCH 19/31 hostmem: add file-based HostMemoryBackend

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

---
 backends/Makefile.objs  |   1 +
 backends/hostmem-file.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 109 insertions(+)
 create mode 100644 backends/hostmem-file.c
backends/Makefile.objs +1 -0
index e6bdc11..509e4a3 100644
@@ -8,3 +8,4 @@ $(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
 common-obj-$(CONFIG_TPM) += tpm.o
 
 common-obj-y += hostmem.o hostmem-ram.o
+common-obj-$(CONFIG_LINUX) += hostmem-file.o
backends/hostmem-file.c new file +108 -0

108 lines not on this page · raw message

Signature · 1 line
1.8.5.2.229.g4448466
author
#21 lore raw

PATCH 20/31 hostmem: separate allocation from UserCreatable complete method

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

This allows the superclass to set various policies on the memory region that the subclass creates.

---
 backends/hostmem-file.c  |  9 ++++-----
 backends/hostmem-ram.c   |  7 +++----
 backends/hostmem.c       | 12 ++++++++++--
 include/sysemu/hostmem.h |  2 ++
 4 files changed, 19 insertions(+), 11 deletions(-)
backends/hostmem-file.c +4 -5
index e22bcef..75a165a 100644
@@ -30,10 +30,9 @@ struct HostMemoryBackendFile {
 };
 
 static void
-file_backend_memory_init(UserCreatable *uc, Error **errp)
+file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 {
-    HostMemoryBackend *backend = MEMORY_BACKEND(uc);
-    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(uc);
+    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
 
     if (!backend->size) {
         error_setg(errp, "can't create backend with size 0");
@@ -58,9 +57,9 @@ file_backend_memory_init(UserCreatable *uc, Error **errp)
 static void
 file_backend_class_init(ObjectClass *oc, void *data)
 {
-    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
 
-    ucc->complete = file_backend_memory_init;
+    bc->alloc = file_backend_memory_alloc;
 }
 
 static char *get_mem_path(Object *o, Error **errp)
backends/hostmem-ram.c +3 -4
index cbf7e5a..7cbc051 100644
@@ -16,9 +16,8 @@
 
 
 static void
-ram_backend_memory_init(UserCreatable *uc, Error **errp)
+ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 {
-    HostMemoryBackend *backend = MEMORY_BACKEND(uc);
     char *path;
 
     if (!backend->size) {
@@ -35,9 +34,9 @@ ram_backend_memory_init(UserCreatable *uc, Error **errp)
 static void
 ram_backend_class_init(ObjectClass *oc, void *data)
 {
-    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
 
-    ucc->complete = ram_backend_memory_init;
+    bc->alloc = ram_backend_memory_alloc;
 }
 
 static const TypeInfo ram_backend_info = {
backends/hostmem.c +10 -2
index 6f26605..5b2117d 100644
@@ -68,8 +68,16 @@ static void host_memory_backend_finalize(Object *obj)
 static void
 host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 {
-    error_setg(errp, "memory_init is not implemented for type [%s]",
-               object_get_typename(OBJECT(uc)));
+    HostMemoryBackend *backend = MEMORY_BACKEND(uc);
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
+
+    if (!bc->alloc) {
+        error_setg(errp, "memory_alloc is not implemented for type [%s]",
+                   object_get_typename(OBJECT(uc)));
+        return;
+    }
+
+    bc->alloc(backend, errp);
 }
 
 MemoryRegion *
include/sysemu/hostmem.h +2 -0
index d396fd8..42c98bd 100644
@@ -32,6 +32,8 @@ typedef struct HostMemoryBackendClass HostMemoryBackendClass;
  */
 struct HostMemoryBackendClass {
     ObjectClass parent_class;
+
+    void (*alloc)(HostMemoryBackend *backend, Error **errp);
 };
 
 /**
Signature · 1 line
1.8.5.2.229.g4448466
author
#22 lore raw

PATCH 21/31 hostmem: add merge and dump properties

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

---
 backends/hostmem.c       | 85 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/qemu/osdep.h     | 10 ++++++
 include/sysemu/hostmem.h |  1 +
 3 files changed, 95 insertions(+), 1 deletion(-)
backends/hostmem.c +84 -1
index 5b2117d..3e5ebfc 100644
@@ -49,8 +49,74 @@ host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque,
     backend->size = value;
 }
 
+static bool host_memory_backend_get_merge(Object *obj, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    return backend->merge;
+}
+
+static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    if (!memory_region_size(&backend->mr)) {
+        backend->merge = value;
+        return;
+    }
+
+    if (value != backend->merge) {
+        void *ptr = memory_region_get_ram_ptr(&backend->mr);
+        uint64_t sz = memory_region_size(&backend->mr);
+
+        qemu_madvise(ptr, sz,
+                     value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE);
+        backend->merge = value;
+    }
+}
+
+static bool host_memory_backend_get_dump(Object *obj, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    return backend->dump;
+}
+
+static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    if (!memory_region_size(&backend->mr)) {
+        backend->dump = value;
+        return;
+    }
+
+    if (value != backend->dump) {
+        void *ptr = memory_region_get_ram_ptr(&backend->mr);
+        uint64_t sz = memory_region_size(&backend->mr);
+
+        qemu_madvise(ptr, sz,
+                     value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP);
+        backend->dump = value;
+    }
+}
+
+
 static void host_memory_backend_initfn(Object *obj)
 {
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    backend->merge = qemu_opt_get_bool(qemu_get_machine_opts(),
+                                       "mem-merge", true);
+    backend->dump = qemu_opt_get_bool(qemu_get_machine_opts(),
+                                      "dump-guest-core", true);
+
+    object_property_add_bool(obj, "merge",
+                        host_memory_backend_get_merge,
+                        host_memory_backend_set_merge, NULL);
+    object_property_add_bool(obj, "dump",
+                        host_memory_backend_get_dump,
+                        host_memory_backend_set_dump, NULL);
     object_property_add(obj, "size", "int",
                         host_memory_backend_get_size,
                         host_memory_backend_set_size, NULL, NULL, NULL);
@@ -70,6 +136,9 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 {
     HostMemoryBackend *backend = MEMORY_BACKEND(uc);
     HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
+    Error *local_err = NULL;
+    void *ptr;
+    uint64_t sz;
 
     if (!bc->alloc) {
         error_setg(errp, "memory_alloc is not implemented for type [%s]",
@@ -77,7 +146,21 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
         return;
     }
 
-    bc->alloc(backend, errp);
+    bc->alloc(backend, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    ptr = memory_region_get_ram_ptr(&backend->mr);
+    sz = memory_region_size(&backend->mr);
+
+    if (backend->merge) {
+        qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE);
+    }
+    if (!backend->dump) {
+        qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP);
+    }
 }
 
 MemoryRegion *
include/qemu/osdep.h +10 -0
index 9c1a119..820c5d0 100644
@@ -116,6 +116,16 @@ void qemu_anon_ram_free(void *ptr, size_t size);
 #else
 #define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
 #endif
+#ifdef MADV_UNMERGEABLE
+#define QEMU_MADV_UNMERGEABLE MADV_UNMERGEABLE
+#else
+#define QEMU_MADV_UNMERGEABLE QEMU_MADV_INVALID
+#endif
+#ifdef MADV_DODUMP
+#define QEMU_MADV_DODUMP MADV_DODUMP
+#else
+#define QEMU_MADV_DODUMP QEMU_MADV_INVALID
+#endif
 #ifdef MADV_DONTDUMP
 #define QEMU_MADV_DONTDUMP MADV_DONTDUMP
 #else
include/sysemu/hostmem.h +1 -0
index 42c98bd..a6b96eb 100644
@@ -50,6 +50,7 @@ struct HostMemoryBackend {
 
     /* protected */
     uint64_t size;
+    bool merge, dump;
 
     MemoryRegion mr;
 };
Signature · 1 line
1.8.5.2.229.g4448466
author
#23 lore raw

PATCH 22/31 hostmem: allow preallocation of any memory region

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

And allow preallocation of file-based memory even without -mem-prealloc. Some care is necessary because -mem-prealloc does not allow disabling preallocation for hostmem-file.

---
 backends/hostmem-file.c  |  3 +++
 backends/hostmem.c       | 42 ++++++++++++++++++++++++++++++++++++++++++
 exec.c                   |  7 +++++++
 include/exec/memory.h    | 10 ++++++++++
 include/exec/ram_addr.h  |  1 +
 include/sysemu/hostmem.h |  1 +
 memory.c                 | 11 +++++++++++
 7 files changed, 75 insertions(+)
backends/hostmem-file.c +3 -0
index 75a165a..67328b9 100644
@@ -9,7 +9,9 @@
  * This work is licensed under the terms of the GNU GPL, version 2 or later.
  * See the COPYING file in the top-level directory.
  */
+#include "qemu-common.h"
 #include "sysemu/hostmem.h"
+#include "sysemu/sysemu.h"
 #include "qom/object_interfaces.h"
 
 /* hostmem-file.c */
@@ -46,6 +48,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
     error_setg(errp, "-mem-path not supported on this host");
 #else
     if (!memory_region_size(&backend->mr)) {
+        backend->force_prealloc = mem_prealloc;
         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
                                  object_get_canonical_path(OBJECT(backend)),
                                  backend->size,
backends/hostmem.c +42 -0

66 lines not on this page · raw message

exec.c +7 -0
index 691d216..fab45b5 100644
@@ -1433,6 +1433,13 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
 }
 #endif /* !_WIN32 */
 
+int qemu_get_ram_fd(ram_addr_t addr)
+{
+    RAMBlock *block = qemu_get_ram_block(addr);
+
+    return block->fd;
+}
+
 /* Return a host pointer to ram allocated with qemu_ram_alloc.
    With the exception of the softmmu code in this file, this should
    only be used for local memory (e.g. video ram) that the device owns,
include/exec/memory.h +10 -0
index 4e606dd..846834e 100644
@@ -534,6 +534,16 @@ bool memory_region_is_logging(MemoryRegion *mr);
 bool memory_region_is_rom(MemoryRegion *mr);
 
 /**
+ * memory_region_get_fd: Get a file descriptor backing a RAM memory region.
+ *
+ * Returns a file descriptor backing a file-based RAM memory region,
+ * or -1 if the region is not a file-based RAM memory region.
+ *
+ * @mr: the RAM or alias memory region being queried.
+ */
+int memory_region_get_fd(MemoryRegion *mr);
+
+/**
  * memory_region_get_ram_ptr: Get a pointer into a RAM memory region.
  *
  * Returns a host pointer to a RAM memory region (created with
include/exec/ram_addr.h +1 -0
index f9518a6..d352f60 100644
@@ -27,6 +27,7 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
 ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    MemoryRegion *mr);
 ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
+int qemu_get_ram_fd(ram_addr_t addr);
 void *qemu_get_ram_ptr(ram_addr_t addr);
 void qemu_ram_free(ram_addr_t addr);
 void qemu_ram_free_from_ptr(ram_addr_t addr);
include/sysemu/hostmem.h +1 -0
index a6b96eb..819b72d 100644
@@ -51,6 +51,7 @@ struct HostMemoryBackend {
     /* protected */
     uint64_t size;
     bool merge, dump;
+    bool prealloc, force_prealloc;
 
     MemoryRegion mr;
 };
memory.c +11 -0
index 6b0c116..118704d 100644
@@ -1258,6 +1258,17 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
     cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
 }
 
+int memory_region_get_fd(MemoryRegion *mr)
+{
+    if (mr->alias) {
+        return memory_region_get_fd(mr->alias);
+    }
+
+    assert(mr->terminates);
+
+    return qemu_get_ram_fd(mr->ram_addr & TARGET_PAGE_MASK);
+}
+
 void *memory_region_get_ram_ptr(MemoryRegion *mr)
 {
     if (mr->alias) {
Signature · 1 line
1.8.5.2.229.g4448466
author
#24 lore raw

PATCH 23/31 hostmem: add property to map memory with MAP_SHARED

In reply to Hu Tao

From: Paolo Bonzini <[email protected]>

A new "share" property can be used with the "memory-file" backend to map memory with MAP_SHARED instead of MAP_PRIVATE.

---
 backends/hostmem-file.c | 26 +++++++++++++++++++++++++-
 exec.c                  | 18 ++++++++++--------
 include/exec/memory.h   |  2 ++
 include/exec/ram_addr.h |  3 ++-
 memory.c                |  3 ++-
 numa.c                  |  2 +-
 6 files changed, 42 insertions(+), 12 deletions(-)
backends/hostmem-file.c +25 -1
index 67328b9..7f30011 100644
@@ -28,6 +28,8 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
 
 struct HostMemoryBackendFile {
     HostMemoryBackend parent_obj;
+
+    bool share;
     char *mem_path;
 };
 
@@ -51,7 +53,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
         backend->force_prealloc = mem_prealloc;
         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
                                  object_get_canonical_path(OBJECT(backend)),
-                                 backend->size,
+                                 backend->size, fb->share,
                                  fb->mem_path, errp);
     }
 #endif
@@ -87,9 +89,31 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
     fb->mem_path = g_strdup(str);
 }
 
+static bool file_memory_backend_get_share(Object *o, Error **errp)
+{
+    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+
+    return fb->share;
+}
+
+static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(o);
+    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+
+    if (memory_region_size(&backend->mr)) {
+        error_setg(errp, "cannot change property value");
+        return;
+    }
+    fb->share = value;
+}
+
 static void
 file_backend_instance_init(Object *o)
 {
+    object_property_add_bool(o, "share",
+                        file_memory_backend_get_share,
+                        file_memory_backend_set_share, NULL);
     object_property_add_str(o, "mem-path", get_mem_path,
                             set_mem_path, NULL);
 }
exec.c +10 -8
index fab45b5..85fb85c 100644
@@ -73,6 +73,9 @@ static MemoryRegion io_mem_unassigned;
 /* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
 #define RAM_PREALLOC   (1 << 0)
 
+/* RAM is mmap-ed with MAP_SHARED */
+#define RAM_SHARED     (1 << 1)
+
 #endif
 
 struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
@@ -1075,7 +1078,9 @@ static void *file_ram_alloc(RAMBlock *block,
         perror("ftruncate");
     }
 
-    area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+    area = mmap(0, memory, PROT_READ | PROT_WRITE,
+                (block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE),
+                fd, 0);
     if (area == MAP_FAILED) {
         error_setg_errno(errp, errno,
                          "unable to map backing store for hugepages");
@@ -1271,7 +1276,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block)
 
 #ifdef __linux__
 ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                    const char *mem_path,
+                                    bool share, const char *mem_path,
                                     Error **errp)
 {
     RAMBlock *new_block;
@@ -1296,6 +1301,7 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
     new_block = g_malloc0(sizeof(*new_block));
     new_block->mr = mr;
     new_block->length = size;
+    new_block->flags = share ? RAM_SHARED : 0;
     new_block->host = file_ram_alloc(new_block, size,
                                      mem_path, errp);
     if (!new_block->host) {
@@ -1398,12 +1404,8 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
                 flags = MAP_FIXED;
                 munmap(vaddr, length);
                 if (block->fd >= 0) {
-#ifdef MAP_POPULATE
-                    flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED :
-                        MAP_PRIVATE;
-#else
-                    flags |= MAP_PRIVATE;
-#endif
+                    flags |= (block->flags & RAM_SHARED ?
+                              MAP_SHARED : MAP_PRIVATE);
                     area = mmap(vaddr, length, PROT_READ | PROT_WRITE,
                                 flags, block->fd, offset);
                 } else {
include/exec/memory.h +2 -0
index 846834e..21443bc 100644
@@ -321,6 +321,7 @@ void memory_region_init_ram(MemoryRegion *mr,
  * @owner: the object that tracks the region's reference count
  * @name: the name of the region.
  * @size: size of the region.
+ * @share: %true if memory must be mmaped with the MAP_SHARED flag
  * @path: the path in which to allocate the RAM.
  * @errp: pointer to Error*, to store an error if it happens.
  */
@@ -328,6 +329,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       struct Object *owner,
                                       const char *name,
                                       uint64_t size,
+                                      bool share,
                                       const char *path,
                                       Error **errp);
 #endif
include/exec/ram_addr.h +2 -1
index d352f60..1d4ac74 100644
@@ -23,7 +23,8 @@
 #include "hw/xen/xen.h"
 
 ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                    const char *mem_path, Error **errp);
+                                    bool share, const char *mem_path,
+                                    Error **errp);
 ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                    MemoryRegion *mr);
 ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
memory.c +2 -1
index 118704d..202869a 100644
@@ -1025,6 +1025,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       struct Object *owner,
                                       const char *name,
                                       uint64_t size,
+                                      bool share,
                                       const char *path,
                                       Error **errp)
 {
@@ -1032,7 +1033,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
     mr->ram = true;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_ram;
-    mr->ram_addr = qemu_ram_alloc_from_file(size, mr, path, errp);
+    mr->ram_addr = qemu_ram_alloc_from_file(size, mr, share, path, errp);
 }
 #endif
 
numa.c +1 -1
index 966cacc..fa117f0 100644
@@ -232,7 +232,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner,
     if (mem_path) {
 #ifdef __linux__
         Error *err = NULL;
-        memory_region_init_ram_from_file(mr, owner, name, ram_size,
+        memory_region_init_ram_from_file(mr, owner, name, ram_size, false,
                                          mem_path, &err);
 
         /* Legacy behavior: if allocation failed, fall back to
Signature · 1 line
1.8.5.2.229.g4448466
author
#25 lore raw

PATCH 24/31 configure: add Linux libnuma detection

In reply to Hu Tao

From: Wanlong Gao <[email protected]>

Add detection of libnuma (mostly contained in the numactl package) to the configure script. Can be enabled or disabled on the command line, default is use if available.

---
 configure | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
configure +33 -0
index 870c939..c3df5e6 100755
@@ -324,6 +324,7 @@ tpm="no"
 libssh2=""
 vhdx=""
 quorum="no"
+numa=""
 
 # parse CC options first
 for opt do
@@ -1087,6 +1088,10 @@ for opt do
   ;;
   --enable-quorum) quorum="yes"
   ;;
+  --disable-numa) numa="no"
+  ;;
+  --enable-numa) numa="yes"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1354,6 +1359,8 @@ Advanced options (experts only):
   --enable-vhdx            enable support for the Microsoft VHDX image format
   --disable-quorum         disable quorum block filter support
   --enable-quorum          enable quorum block filter support
+  --disable-numa           disable libnuma support
+  --enable-numa            enable libnuma support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -3113,6 +3120,27 @@ if compile_prog "" "" ; then
 fi
 
 ##########################################
+# libnuma probe
+
+if test "$numa" != "no" ; then
+  numa=no
+  cat > $TMPC << EOF
+#include <numa.h>
+int main(void) { return numa_available(); }
+EOF
+
+  if compile_prog "" "-lnuma" ; then
+    numa=yes
+    libs_softmmu="-lnuma $libs_softmmu"
+  else
+    if test "$numa" = "yes" ; then
+      feature_not_found "linux NUMA (install numactl?)"
+    fi
+    numa=no
+  fi
+fi
+
+##########################################
 # signalfd probe
 signalfd="no"
 cat > $TMPC << EOF
@@ -4186,6 +4214,7 @@ echo "vhdx              $vhdx"
 echo "Quorum            $quorum"
 echo "lzo support       $lzo"
 echo "snappy support    $snappy"
+echo "NUMA host support $numa"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -5153,6 +5182,10 @@ if [ "$dtc_internal" = "yes" ]; then
   echo "config-host.h: subdir-dtc" >> $config_host_mak
 fi
 
+if test "$numa" = "yes"; then
+  echo "CONFIG_NUMA=y" >> $config_host_mak
+fi
+
 # build tree in object directory in case the source is not in the current directory
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
 DIRS="$DIRS fsdev"
Signature · 1 line
1.8.5.2.229.g4448466
#26 lore raw

Re: [Qemu-devel] [PATCH v3.1 24/31] configure: add Linux libnuma detection

Quoted 55 lines · Hu Tao

From: Wanlong Gao <[email protected]>

Add detection of libnuma (mostly contained in the numactl package) to the configure script. Can be enabled or disabled on the command line, default is use if available.

---
 configure | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
configure +12 -0
index 870c939..c3df5e6 100755
@@ -324,6 +324,7 @@ tpm="no"
 libssh2=""
 vhdx=""
 quorum="no"
+numa=""
 
 # parse CC options first
 for opt do
@@ -1087,6 +1088,10 @@ for opt do
   ;;
   --enable-quorum) quorum="yes"
   ;;
+  --disable-numa) numa="no"
+  ;;
+  --enable-numa) numa="yes"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1354,6 +1359,8 @@ Advanced options (experts only):
   --enable-vhdx            enable support for the Microsoft VHDX image format
   --disable-quorum         disable quorum block filter support
   --enable-quorum          enable quorum block filter support
+  --disable-numa           disable libnuma support
+  --enable-numa            enable libnuma support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -3113,6 +3120,27 @@ if compile_prog "" "" ; then
 fi
 
 ##########################################
+# libnuma probe
+
+if test "$numa" != "no" ; then
+  numa=no
+  cat > $TMPC << EOF

configure if compile_prog "" "" ; then · line 3137

Quoted 10 lines
configure +10 -0
+#include <numa.h>
+int main(void) { return numa_available(); }
+EOF
+
+  if compile_prog "" "-lnuma" ; then
+    numa=yes
+    libs_softmmu="-lnuma $libs_softmmu"
+  else
+    if test "$numa" = "yes" ; then
+      feature_not_found "linux NUMA (install numactl?)"

Which distro?
At least for Fedora I think one needs numactl-devel?

Quoted 31 lines
configure +11 -0
+    fi
+    numa=no
+  fi
+fi
+
+##########################################
 # signalfd probe
 signalfd="no"
 cat > $TMPC << EOF
@@ -4186,6 +4214,7 @@ echo "vhdx              $vhdx"
 echo "Quorum            $quorum"
 echo "lzo support       $lzo"
 echo "snappy support    $snappy"
+echo "NUMA host support $numa"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -5153,6 +5182,10 @@ if [ "$dtc_internal" = "yes" ]; then
   echo "config-host.h: subdir-dtc" >> $config_host_mak
 fi
 
+if test "$numa" = "yes"; then
+  echo "CONFIG_NUMA=y" >> $config_host_mak
+fi
+
 # build tree in object directory in case the source is not in the current directory
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
 DIRS="$DIRS fsdev"
Signature · 1 line
1.8.5.2.229.g4448466
author
#27 lore raw

Re: [Qemu-devel] [PATCH v3.1 24/31] configure: add Linux libnuma detection

Quoted 61 lines · Michael S. Tsirkin
Quoted 60 lines · Hu Tao

From: Wanlong Gao <[email protected]>

Add detection of libnuma (mostly contained in the numactl package) to the configure script. Can be enabled or disabled on the command line, default is use if available.

---
 configure | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
configure +17 -0
index 870c939..c3df5e6 100755
@@ -324,6 +324,7 @@ tpm="no"
 libssh2=""
 vhdx=""
 quorum="no"
+numa=""
 
 # parse CC options first
 for opt do
@@ -1087,6 +1088,10 @@ for opt do
   ;;
   --enable-quorum) quorum="yes"
   ;;
+  --disable-numa) numa="no"
+  ;;
+  --enable-numa) numa="yes"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1354,6 +1359,8 @@ Advanced options (experts only):
   --enable-vhdx            enable support for the Microsoft VHDX image format
   --disable-quorum         disable quorum block filter support
   --enable-quorum          enable quorum block filter support
+  --disable-numa           disable libnuma support
+  --enable-numa            enable libnuma support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -3113,6 +3120,27 @@ if compile_prog "" "" ; then
 fi
 
 ##########################################
+# libnuma probe
+
+if test "$numa" != "no" ; then
+  numa=no
+  cat > $TMPC << EOF
+#include <numa.h>
+int main(void) { return numa_available(); }
+EOF
+
+  if compile_prog "" "-lnuma" ; then
Quoted 8 lines

configure if compile_prog "" "" ; then · line 3137

Quoted 5 lines
configure +5 -0
+    numa=yes
+    libs_softmmu="-lnuma $libs_softmmu"
+  else
+    if test "$numa" = "yes" ; then
+      feature_not_found "linux NUMA (install numactl?)"

Which distro?
At least for Fedora I think one needs numactl-devel?

Yes, on Fedora the package is called numactl-devel. But, to follow other similar messages I'll change it into 'install numactl devel'.

Regards,
Hu Tao

author
#28 lore raw

PATCH 25/31 hostmem: add properties for NUMA memory policy

In reply to Hu Tao

[Raise errors on setting properties if !CONFIG_NUMA. Add BUILD_BUG_ON
checks. - Paolo]

---
 backends/hostmem.c       | 109 ++++++++++++++++++++++++++++++++++++++++++++++-
 include/sysemu/hostmem.h |   4 ++
 qapi-schema.json         |  20 +++++++++
 3 files changed, 132 insertions(+), 1 deletion(-)
backends/hostmem.c +108 -1
index 738bb31..d3f8476 100644
@@ -10,11 +10,20 @@
  * See the COPYING file in the top-level directory.
  */
 #include "sysemu/hostmem.h"
-#include "sysemu/sysemu.h"
 #include "qapi/visitor.h"
+#include "qapi-types.h"
+#include "qapi-visit.h"
 #include "qapi/qmp/qerror.h"
 #include "qom/object_interfaces.h"
 
+#ifdef CONFIG_NUMA
+#include <numaif.h>
+QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT);
+QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED);
+QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
+QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
+#endif
+
 static void
 host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque,
                             const char *name, Error **errp)
@@ -49,6 +58,84 @@ host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque,
     backend->size = value;
 }
 
+static void
+get_host_nodes(Object *obj, Visitor *v, void *opaque, const char *name,
+               Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    uint16List *host_nodes = NULL;
+    uint16List **node = &host_nodes;
+    unsigned long value;
+
+    value = find_first_bit(backend->host_nodes, MAX_NODES);
+    if (value == MAX_NODES) {
+        return;
+    }
+
+    *node = g_malloc0(sizeof(**node));
+    (*node)->value = value;
+    node = &(*node)->next;
+
+    do {
+        value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1);
+        if (value == MAX_NODES) {
+            break;
+        }
+
+        *node = g_malloc0(sizeof(**node));
+        (*node)->value = value;
+        node = &(*node)->next;
+    } while (true);
+
+    visit_type_uint16List(v, &host_nodes, name, errp);
+}
+
+static void
+set_host_nodes(Object *obj, Visitor *v, void *opaque, const char *name,
+               Error **errp)
+{
+#ifdef CONFIG_NUMA
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    uint16List *l = NULL;
+
+    visit_type_uint16List(v, &l, name, errp);
+
+    while (l) {
+        bitmap_set(backend->host_nodes, l->value, 1);
+        l = l->next;
+    }
+#else
+    error_setg(errp, "NUMA node binding are not supported by this QEMU");
+#endif
+}
+
+static void
+get_policy(Object *obj, Visitor *v, void *opaque, const char *name,
+           Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    int policy = backend->policy;
+
+    visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
+}
+
+static void
+set_policy(Object *obj, Visitor *v, void *opaque, const char *name,
+           Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    int policy;
+
+    visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
+    backend->policy = policy;
+
+#ifndef CONFIG_NUMA
+    if (policy != HOST_MEM_POLICY_DEFAULT) {
+        error_setg(errp, "NUMA policies are not supported by this QEMU");
+    }
+#endif
+}
+
 static bool host_memory_backend_get_merge(Object *obj, Error **errp)
 {
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
@@ -159,6 +246,12 @@ static void host_memory_backend_initfn(Object *obj)
     object_property_add(obj, "size", "int",
                         host_memory_backend_get_size,
                         host_memory_backend_set_size, NULL, NULL, NULL);
+    object_property_add(obj, "host-nodes", "int",
+                        get_host_nodes,
+                        set_host_nodes, NULL, NULL, NULL);
+    object_property_add(obj, "policy", "str",
+                        get_policy,
+                        set_policy, NULL, NULL, NULL);
 }
 
 static void host_memory_backend_finalize(Object *obj)
@@ -203,6 +296,20 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (backend->prealloc) {
         os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
     }
+
+#ifdef CONFIG_NUMA
+    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+
+    /* This is a workaround for a long standing bug in Linux'
+     * mbind implementation, which cuts off the last specified
+     * node.
+     */
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");
+        return;
+    }
+#endif
 }
 
 MemoryRegion *
include/sysemu/hostmem.h +4 -0
index 819b72d..4e96298 100644
@@ -12,8 +12,10 @@
 #ifndef QEMU_HOSTMEM_H
 #define QEMU_HOSTMEM_H
 
+#include "sysemu/sysemu.h" /* for MAX_NODES */
 #include "qom/object.h"
 #include "exec/memory.h"
+#include "qemu/bitmap.h"
 
 #define TYPE_MEMORY_BACKEND "memory"
 #define MEMORY_BACKEND(obj) \
@@ -52,6 +54,8 @@ struct HostMemoryBackend {
     uint64_t size;
     bool merge, dump;
     bool prealloc, force_prealloc;
+    DECLARE_BITMAP(host_nodes, MAX_NODES);
+    HostMemPolicy policy;
 
     MemoryRegion mr;
 };
qapi-schema.json +20 -0
index 5dd30eb..bea3476 100644
@@ -4732,3 +4732,23 @@
    '*cpus':   ['uint16'],
    '*mem':    'size',
    '*memdev': 'str' }}
+
+##
+# @HostMemPolicy
+#
+# Host memory policy types
+#
+# @default: restore default policy, remove any nondefault policy
+#
+# @preferred: set the preferred host nodes for allocation
+#
+# @bind: a strict policy that restricts memory allocation to the
+#        host nodes specified
+#
+# @interleave: memory allocations are interleaved across the set
+#              of host nodes specified
+#
+# Since 2.1
+##
+{ 'enum': 'HostMemPolicy',
+  'data': [ 'default', 'preferred', 'bind', 'interleave' ] }
Signature · 1 line
1.8.5.2.229.g4448466
#29 lore raw

Re: [Qemu-devel] [PATCH v3.1 25/31] hostmem: add properties for NUMA memory policy

On Tue, May 06, 2014 at 05:27:46PM +0800, Hu Tao wrote:
[...]

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 306

Quoted 12 lines
backends/hostmem.c +8 -0
@@ -203,6 +296,20 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (backend->prealloc) {
         os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
     }
+
+#ifdef CONFIG_NUMA
+    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+
+    /* This is a workaround for a long standing bug in Linux'
+     * mbind implementation, which cuts off the last specified
+     * node.
+     */

What if the bug is fixed? mbind() documentation says "nodemask points to a bit mask of nodes containing up to maxnode bits", so we must ensure backend->host_nodes has the one extra bit.

Also, if no bit is set, we can pass nodemask=NULL or maxnode=0 as argument.

We could address both issues, and do this:

    struct HostMemoryBackend { [...]
        DECLARE_BITMAP(host_nodes, MAX_NODES + 1);
    [...]
    lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
    /* lastbit == MAX_NODES means maxnode=0 */
    maxnode = (lastbit + 1) % (MAX_NODES + 1);
    /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1
     * as argument to mbind() due to an old Linux bug (feature?) which
     * cuts off the last specified node. This means backend->host_nodes
     * must have MAX_NODES+1 bits available.
     */
    assert(sizeof(backend->host_nodes) >= BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
    assert(maxnode <= MAX_NODES);
    mbind(ptr, sz, policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags);

(I am starting to wonder if it was worth dropping the libnuma requirement and implementing our own mbind()-calling code.)

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 309

Quoted 3 lines
backends/hostmem.c +3 -0
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");

Don't we want to set flags to MPOL_MF_STRICT here? I believe we shouldn't have any pages preallocated at this point, but in case we do, I would expect them to be moved instead of ignoring the policy set by the user.

Quoted 62 lines
backends/hostmem.c +3 -0
+        return;
+    }
+#endif
 }
 
 MemoryRegion *
include/sysemu/hostmem.h +4 -0
index 819b72d..4e96298 100644
@@ -12,8 +12,10 @@
 #ifndef QEMU_HOSTMEM_H
 #define QEMU_HOSTMEM_H
 
+#include "sysemu/sysemu.h" /* for MAX_NODES */
 #include "qom/object.h"
 #include "exec/memory.h"
+#include "qemu/bitmap.h"
 
 #define TYPE_MEMORY_BACKEND "memory"
 #define MEMORY_BACKEND(obj) \
@@ -52,6 +54,8 @@ struct HostMemoryBackend {
     uint64_t size;
     bool merge, dump;
     bool prealloc, force_prealloc;
+    DECLARE_BITMAP(host_nodes, MAX_NODES);
+    HostMemPolicy policy;
 
     MemoryRegion mr;
 };
qapi-schema.json +20 -0
index 5dd30eb..bea3476 100644
@@ -4732,3 +4732,23 @@
    '*cpus':   ['uint16'],
    '*mem':    'size',
    '*memdev': 'str' }}
+
+##
+# @HostMemPolicy
+#
+# Host memory policy types
+#
+# @default: restore default policy, remove any nondefault policy
+#
+# @preferred: set the preferred host nodes for allocation
+#
+# @bind: a strict policy that restricts memory allocation to the
+#        host nodes specified
+#
+# @interleave: memory allocations are interleaved across the set
+#              of host nodes specified
+#
+# Since 2.1
+##
+{ 'enum': 'HostMemPolicy',
+  'data': [ 'default', 'preferred', 'bind', 'interleave' ] }
Signature · 1 line
1.8.5.2.229.g4448466
Signature · 1 line
Eduardo
author
#30 lore raw

Re: [Qemu-devel] [PATCH v3.1 25/31] hostmem: add properties for NUMA memory policy

Quoted 16 lines · Eduardo Habkost

On Tue, May 06, 2014 at 05:27:46PM +0800, Hu Tao wrote:
[...]

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 306

Quoted 12 lines
backends/hostmem.c +8 -0
@@ -203,6 +296,20 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (backend->prealloc) {
         os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
     }
+
+#ifdef CONFIG_NUMA
+    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+
+    /* This is a workaround for a long standing bug in Linux'
+     * mbind implementation, which cuts off the last specified
+     * node.
+     */

What if the bug is fixed? mbind() documentation says "nodemask points to

No it won't, otherwise softwares depend on mbind() will break.

Quoted 2 lines

a bit mask of nodes containing up to maxnode bits", so we must ensure backend->host_nodes has the one extra bit.

Yes.

Quoted 6 lines

Also, if no bit is set, we can pass nodemask=NULL or maxnode=0 as argument.

We could address both issues, and do this:

Quoted 13 lines
    struct HostMemoryBackend { [...]
        DECLARE_BITMAP(host_nodes, MAX_NODES + 1);
    [...]
    lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
    /* lastbit == MAX_NODES means maxnode=0 */
    maxnode = (lastbit + 1) % (MAX_NODES + 1);
    /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1
     * as argument to mbind() due to an old Linux bug (feature?) which
     * cuts off the last specified node. This means backend->host_nodes
     * must have MAX_NODES+1 bits available.
     */
    assert(sizeof(backend->host_nodes) >= BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
    assert(maxnode <= MAX_NODES);

I think we can just omit these two asserts since they are guaranteed to be true.

Quoted 6 lines
    mbind(ptr, sz, policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags);

(I am starting to wonder if it was worth dropping the libnuma requirement and implementing our own mbind()-calling code.)

Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 309

Quoted 3 lines
backends/hostmem.c +3 -0
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");

Don't we want to set flags to MPOL_MF_STRICT here? I believe we shouldn't have any pages preallocated at this point, but in case we do, I would expect them to be moved instead of ignoring the policy set by the user.

MPOL_MF_STRICT | MPOL_MF_MOVE to move. Actually in this version the preallocation happens before mbind, which is fixed in v3.2.

Quoted 66 lines
Quoted 63 lines
backends/hostmem.c +3 -0
+        return;
+    }
+#endif
 }
 
 MemoryRegion *
include/sysemu/hostmem.h +4 -0
index 819b72d..4e96298 100644
@@ -12,8 +12,10 @@
 #ifndef QEMU_HOSTMEM_H
 #define QEMU_HOSTMEM_H
 
+#include "sysemu/sysemu.h" /* for MAX_NODES */
 #include "qom/object.h"
 #include "exec/memory.h"
+#include "qemu/bitmap.h"
 
 #define TYPE_MEMORY_BACKEND "memory"
 #define MEMORY_BACKEND(obj) \
@@ -52,6 +54,8 @@ struct HostMemoryBackend {
     uint64_t size;
     bool merge, dump;
     bool prealloc, force_prealloc;
+    DECLARE_BITMAP(host_nodes, MAX_NODES);
+    HostMemPolicy policy;
 
     MemoryRegion mr;
 };
qapi-schema.json +20 -0
index 5dd30eb..bea3476 100644
@@ -4732,3 +4732,23 @@
    '*cpus':   ['uint16'],
    '*mem':    'size',
    '*memdev': 'str' }}
+
+##
+# @HostMemPolicy
+#
+# Host memory policy types
+#
+# @default: restore default policy, remove any nondefault policy
+#
+# @preferred: set the preferred host nodes for allocation
+#
+# @bind: a strict policy that restricts memory allocation to the
+#        host nodes specified
+#
+# @interleave: memory allocations are interleaved across the set
+#              of host nodes specified
+#
+# Since 2.1
+##
+{ 'enum': 'HostMemPolicy',
+  'data': [ 'default', 'preferred', 'bind', 'interleave' ] }
Signature · 1 line
1.8.5.2.229.g4448466
Signature · 1 line
Eduardo
#31 lore raw

Re: [Qemu-devel] [PATCH v3.1 25/31] hostmem: add properties for NUMA memory policy

Quoted 45 lines · Hu Tao
Quoted 16 lines · Eduardo Habkost

On Tue, May 06, 2014 at 05:27:46PM +0800, Hu Tao wrote:
[...]

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 306

Quoted 12 lines
backends/hostmem.c +8 -0
@@ -203,6 +296,20 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (backend->prealloc) {
         os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
     }
+
+#ifdef CONFIG_NUMA
+    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+
+    /* This is a workaround for a long standing bug in Linux'
+     * mbind implementation, which cuts off the last specified
+     * node.
+     */

What if the bug is fixed? mbind() documentation says "nodemask points to

No it won't, otherwise softwares depend on mbind() will break.

Quoted 2 lines

a bit mask of nodes containing up to maxnode bits", so we must ensure backend->host_nodes has the one extra bit.

Yes.

Quoted 6 lines

Also, if no bit is set, we can pass nodemask=NULL or maxnode=0 as argument.

We could address both issues, and do this:

Quoted 13 lines
    struct HostMemoryBackend { [...]
        DECLARE_BITMAP(host_nodes, MAX_NODES + 1);
    [...]
    lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
    /* lastbit == MAX_NODES means maxnode=0 */
    maxnode = (lastbit + 1) % (MAX_NODES + 1);
    /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1
     * as argument to mbind() due to an old Linux bug (feature?) which
     * cuts off the last specified node. This means backend->host_nodes
     * must have MAX_NODES+1 bits available.
     */
    assert(sizeof(backend->host_nodes) >= BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
    assert(maxnode <= MAX_NODES);
Quoted 2 lines

I think we can just omit these two asserts since they are guaranteed to be true.

asserts() must be always guaranteed to be true, that's the whole point of using them. They can detect subtle off-by-one bugs if somebody introduces them in the future.

Quoted 11 lines
Quoted 11 lines
    mbind(ptr, sz, policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags);

(I am starting to wonder if it was worth dropping the libnuma requirement and implementing our own mbind()-calling code.)

Quoted 3 lines
backends/hostmem.c +3 -0
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");
Quoted 7 lines
Quoted 4 lines

Don't we want to set flags to MPOL_MF_STRICT here? I believe we shouldn't have any pages preallocated at this point, but in case we do, I would expect them to be moved instead of ignoring the policy set by the user.

MPOL_MF_STRICT | MPOL_MF_MOVE to move. Actually in this version the preallocation happens before mbind, which is fixed in v3.2.

If memory was already allocated in a different node and has to be moved that early, that's a bug we want to detect and fix (instead of triggering useles memory moves). So I would use only MPOL_MF_STRICT.

Signature · 1 line
Eduardo
author
#32 lore raw

Re: [Qemu-devel] [PATCH v3.1 25/31] hostmem: add properties for NUMA memory policy

Quoted 69 lines · Eduardo Habkost
Quoted 45 lines · Hu Tao
Quoted 16 lines · Eduardo Habkost

On Tue, May 06, 2014 at 05:27:46PM +0800, Hu Tao wrote:
[...]

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 306

Quoted 12 lines
backends/hostmem.c +8 -0
@@ -203,6 +296,20 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (backend->prealloc) {
         os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
     }
+
+#ifdef CONFIG_NUMA
+    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+
+    /* This is a workaround for a long standing bug in Linux'
+     * mbind implementation, which cuts off the last specified
+     * node.
+     */

What if the bug is fixed? mbind() documentation says "nodemask points to

No it won't, otherwise softwares depend on mbind() will break.

Quoted 2 lines

a bit mask of nodes containing up to maxnode bits", so we must ensure backend->host_nodes has the one extra bit.

Yes.

Quoted 6 lines

Also, if no bit is set, we can pass nodemask=NULL or maxnode=0 as argument.

We could address both issues, and do this:

Quoted 13 lines
    struct HostMemoryBackend { [...]
        DECLARE_BITMAP(host_nodes, MAX_NODES + 1);
    [...]
    lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
    /* lastbit == MAX_NODES means maxnode=0 */
    maxnode = (lastbit + 1) % (MAX_NODES + 1);
    /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1
     * as argument to mbind() due to an old Linux bug (feature?) which
     * cuts off the last specified node. This means backend->host_nodes
     * must have MAX_NODES+1 bits available.
     */
    assert(sizeof(backend->host_nodes) >= BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
    assert(maxnode <= MAX_NODES);
Quoted 2 lines

I think we can just omit these two asserts since they are guaranteed to be true.

asserts() must be always guaranteed to be true, that's the whole point of using them. They can detect subtle off-by-one bugs if somebody introduces them in the future.

Quoted 16 lines
Quoted 15 lines
    mbind(ptr, sz, policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags);

(I am starting to wonder if it was worth dropping the libnuma requirement and implementing our own mbind()-calling code.)

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 309

Quoted 3 lines
backends/hostmem.c +3 -0
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");

Don't we want to set flags to MPOL_MF_STRICT here? I believe we shouldn't have any pages preallocated at this point, but in case we do, I would expect them to be moved instead of ignoring the policy set by the user.

Quoted 6 lines
Quoted 2 lines

MPOL_MF_STRICT | MPOL_MF_MOVE to move. Actually in this version the preallocation happens before mbind, which is fixed in v3.2.

If memory was already allocated in a different node and has to be moved that early, that's a bug we want to detect and fix (instead of triggering useles memory moves). So I would use only MPOL_MF_STRICT.

Fair enough. But what about huge pages? As man page says, MPOL_MF_STRICT is ignored on huge page mappings. Is leaving a comment at the place of memory preallocation to warn people against alocating memory before mbind (like it's done in v3.2) the only thing we can do?

Regards,
Hu

#33 lore raw

[Qemu-devel] MPOL_MF_STRICT & hugetlbfs (was Re: [PATCH v3.1 25/31] hostmem: add properties for NUMAS memory policy)

On Mon, Jun 09, 2014 at 10:12:07AM +0800, Hu Tao wrote:
[...]

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
Quoted 7 lines
    mbind(ptr, sz, policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags);

(I am starting to wonder if it was worth dropping the libnuma requirement and implementing our own mbind()-calling code.)

Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 309

Quoted 3 lines
backends/hostmem.c +3 -0
+    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+        error_setg_errno(errp, errno,
+                         "cannot bind memory to host NUMA nodes");

Don't we want to set flags to MPOL_MF_STRICT here? I believe we shouldn't have any pages preallocated at this point, but in case we do, I would expect them to be moved instead of ignoring the policy set by the user.

MPOL_MF_STRICT | MPOL_MF_MOVE to move. Actually in this version the preallocation happens before mbind, which is fixed in v3.2.

Quoted 8 lines
Quoted 3 lines

If memory was already allocated in a different node and has to be moved that early, that's a bug we want to detect and fix (instead of triggering useles memory moves). So I would use only MPOL_MF_STRICT.

Fair enough. But what about huge pages? As man page says, MPOL_MF_STRICT is ignored on huge page mappings. Is leaving a comment at the place of memory preallocation to warn people against alocating memory before mbind (like it's done in v3.2) the only thing we can do?

Well, maybe the kernel should be fixed to not ignore MPOL_MF_STRICT on huge page mappings, then. Does anybody know if the warning on the manpage still applies, and if this can be changed?

In the meantime, it looks like all we can do is to print a warning, or refuse to preallocate before mbind().

Signature · 1 line
Eduardo
#34 lore raw

Re: [Qemu-devel] [PATCH v3.1 25/31] hostmem: add properties for NUMA memory policy

In reply to Hu Tao

On Tue, May 06, 2014 at 05:27:46PM +0800, Hu Tao wrote:\
[...]

backends/hostmem.c host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, · line 104

Quoted 12 lines
backends/hostmem.c +12 -0
+static void
+set_host_nodes(Object *obj, Visitor *v, void *opaque, const char *name,
+               Error **errp)
+{
+#ifdef CONFIG_NUMA
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    uint16List *l = NULL;
+
+    visit_type_uint16List(v, &l, name, errp);
+
+    while (l) {
+        bitmap_set(backend->host_nodes, l->value, 1);

Shouldn't we clear the existing bits, here?

Quoted 6 lines
backends/hostmem.c +6 -0
+        l = l->next;
+    }
+#else
+    error_setg(errp, "NUMA node binding are not supported by this QEMU");
+#endif
+}
Signature · 1 line
Eduardo
author
#35 lore raw

PATCH 26/31 Introduce signed range.

In reply to Hu Tao
---
 include/qemu/range.h | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)
include/qemu/range.h +124 -0
index aae9720..8879f8a 100644
@@ -3,6 +3,7 @@
 
 #include <inttypes.h>
 #include <qemu/typedefs.h>
+#include "qemu/queue.h"
 
 /*
  * Operations on 64 bit address ranges.
@@ -60,4 +61,127 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1,
     return !(last2 < first1 || last1 < first2);
 }
 
+typedef struct SignedRangeList SignedRangeList;
+
+typedef struct SignedRange {
+    int64_t start;
+    int64_t length;
+
+    QTAILQ_ENTRY(SignedRange) entry;
+} SignedRange;
+
+QTAILQ_HEAD(SignedRangeList, SignedRange);
+
+static inline int64_t s_range_end(int64_t start, int64_t length)
+{
+    return start + length - 1;
+}
+
+/* negative length or overflow */
+static inline bool s_range_overflow(int64_t start, int64_t length)
+{
+    return s_range_end(start, length) < start;
+}
+
+static inline SignedRange *s_range_new(int64_t start, int64_t length)
+{
+    SignedRange *range = NULL;
+
+    if (s_range_overflow(start, length)) {
+        return NULL;
+    }
+
+    range = g_malloc0(sizeof(*range));
+    range->start = start;
+    range->length = length;
+
+    return range;
+}
+
+static inline void s_range_free(SignedRange *range)
+{
+    g_free(range);
+}
+
+static inline bool s_range_overlap(int64_t start1, int64_t length1,
+                                   int64_t start2, int64_t length2)
+{
+    return !((start1 + length1) < start2 || (start2 + length2) < start1);
+}
+
+static inline int s_range_join(SignedRange *range,
+                               int64_t start, int64_t length)
+{
+    if (s_range_overflow(start, length)) {
+        return -1;
+    }
+
+    if (s_range_overlap(range->start, range->length, start, length)) {
+        int64_t end = s_range_end(range->start, range->length);
+        if (end < s_range_end(start, length)) {
+            end = s_range_end(start, length);
+        }
+        if (range->start > start) {
+            range->start = start;
+        }
+        range->length = end - range->start + 1;
+        return 0;
+    }
+
+    return -1;
+}
+
+static inline int s_range_compare(int64_t start1, int64_t length1,
+                                  int64_t start2, int64_t length2)
+{
+    if (start1 == start2 && length1 == length2) {
+        return 0;
+    } else if (s_range_end(start1, length1) <
+               s_range_end(start2, length2)) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+/* Add range to list. Keep them sorted, and merge ranges whenever possible */
+static inline bool range_list_add(SignedRangeList *list,
+                                  int64_t start, int64_t length)
+{
+    SignedRange *r, *next, *new_range = NULL, *cur = NULL;
+
+    if (s_range_overflow(start, length)) {
+        return false;
+    }
+
+    QTAILQ_FOREACH_SAFE(r, list, entry, next) {
+        if (s_range_overlap(r->start, r->length, start, length)) {
+            s_range_join(r, start, length);
+            break;
+        } else if (s_range_compare(start, length, r->start, r->length) < 0) {
+            cur = r;
+            break;
+        }
+    }
+
+    if (!r) {
+        new_range = s_range_new(start, length);
+        QTAILQ_INSERT_TAIL(list, new_range, entry);
+    } else if (cur) {
+        new_range = s_range_new(start, length);
+        QTAILQ_INSERT_BEFORE(cur, new_range, entry);
+    } else {
+        SignedRange *next = QTAILQ_NEXT(r, entry);
+        while (next && s_range_overlap(r->start, r->length,
+                                       next->start, next->length)) {
+            s_range_join(r, next->start, next->length);
+            QTAILQ_REMOVE(list, next, entry);
+            s_range_free(next);
+            next = QTAILQ_NEXT(r, entry);
+        }
+    }
+
+    return true;
+}
+
 #endif
Signature · 1 line
1.8.5.2.229.g4448466
author
#36 lore raw

PATCH 27/31 qapi: make string input visitor parse int list

In reply to Hu Tao
---
 qapi/string-input-visitor.c       | 181 ++++++++++++++++++++++++++++++++++++--
 tests/test-string-input-visitor.c |  39 ++++++++
 2 files changed, 212 insertions(+), 8 deletions(-)
qapi/string-input-visitor.c +173 -8
index 793548a..0f6add7 100644
@@ -15,31 +15,182 @@
 #include "qapi/visitor-impl.h"
 #include "qapi/qmp/qerror.h"
 #include "qemu/option.h"
+#include "qemu/queue.h"
+#include "qemu/range.h"
+
 
 struct StringInputVisitor
 {
     Visitor visitor;
+
+    bool head;
+
+    SignedRangeList *ranges;
+    SignedRange *cur_range;
+    int64_t cur;
+
     const char *string;
 };
 
+static void parse_str(StringInputVisitor *siv, Error **errp)
+{
+    char *str = (char *) siv->string;
+    long long start, end;
+    SignedRange *r, *next;
+    char *endptr;
+
+    if (siv->ranges) {
+        return;
+    }
+
+    siv->ranges = g_malloc0(sizeof(*siv->ranges));
+    QTAILQ_INIT(siv->ranges);
+    errno = 0;
+    do {
+        start = strtoll(str, &endptr, 0);
+        if (errno == 0 && endptr > str && INT64_MIN <= start &&
+            start <= INT64_MAX) {
+            if (*endptr == '\0') {
+                if (!range_list_add(siv->ranges, start, 1)) {
+                    goto error;
+                }
+                str = NULL;
+            } else if (*endptr == '-') {
+                str = endptr + 1;
+                end = strtoll(str, &endptr, 0);
+                if (errno == 0 && endptr > str &&
+                    INT64_MIN <= end && end <= INT64_MAX && start <= end &&
+                    (start > INT64_MAX - 65536 ||
+                     end < start + 65536)) {
+                    if (*endptr == '\0') {
+                        if (!range_list_add(siv->ranges, start,
+                                            end - start + 1)) {
+                            goto error;
+                        }
+                        str = NULL;
+                    } else if (*endptr == ',') {
+                        str = endptr + 1;
+                        if (!range_list_add(siv->ranges, start,
+                                            end - start + 1)) {
+                            goto error;
+                        }
+                    } else {
+                        goto error;
+                    }
+                } else {
+                    goto error;
+                }
+            } else if (*endptr == ',') {
+                str = endptr + 1;
+                if (!range_list_add(siv->ranges, start, 1)) {
+                    goto error;
+                }
+            } else {
+                goto error;
+            }
+        } else {
+            goto error;
+        }
+    } while (str);
+
+    return;
+error:
+    if (siv->ranges) {
+        QTAILQ_FOREACH_SAFE(r, siv->ranges, entry, next) {
+            QTAILQ_REMOVE(siv->ranges, r, entry);
+            g_free(r);
+        }
+        g_free(siv->ranges);
+        siv->ranges = NULL;
+    }
+}
+
+static void
+start_list(Visitor *v, const char *name, Error **errp)
+{
+    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+
+    parse_str(siv, errp);
+
+    if (siv->ranges) {
+        siv->cur_range = QTAILQ_FIRST(siv->ranges);
+        if (siv->cur_range) {
+            siv->cur = siv->cur_range->start;
+        }
+    }
+}
+
+static GenericList *
+next_list(Visitor *v, GenericList **list, Error **errp)
+{
+    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    GenericList **link;
+
+    if (!siv->ranges || !siv->cur_range) {
+        return NULL;
+    }
+
+    if (siv->cur < siv->cur_range->start ||
+        siv->cur >= (siv->cur_range->start + siv->cur_range->length)) {
+        siv->cur_range = QTAILQ_NEXT(siv->cur_range, entry);
+        if (siv->cur_range) {
+            siv->cur = siv->cur_range->start;
+        } else {
+            return NULL;
+        }
+    }
+
+    if (siv->head) {
+        link = list;
+        siv->head = false;
+    } else {
+        link = &(*list)->next;
+    }
+
+    *link = g_malloc0(sizeof **link);
+    return *link;
+}
+
+static void
+end_list(Visitor *v, Error **errp)
+{
+    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    siv->head = true;
+}
+
 static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
                            Error **errp)
 {
     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
-    char *endp = (char *) siv->string;
-    long long val;
 
-    errno = 0;
-    if (siv->string) {
-        val = strtoll(siv->string, &endp, 0);
-    }
-    if (!siv->string || errno || endp == siv->string || *endp) {
+    if (!siv->string) {
         error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                   "integer");
         return;
     }
 
-    *obj = val;
+    parse_str(siv, errp);
+
+    if (!siv->ranges) {
+        goto error;
+    }
+
+    if (!siv->cur_range) {
+        siv->cur_range = QTAILQ_FIRST(siv->ranges);
+        if (siv->cur_range) {
+            siv->cur = siv->cur_range->start;
+        } else {
+            goto error;
+        }
+    }
+
+    *obj = siv->cur;
+    siv->cur++;
+    return;
+
+error:
+    error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
+              "an int64 value or range");
 }
 
 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
@@ -140,6 +291,16 @@ Visitor *string_input_get_visitor(StringInputVisitor *v)
 
 void string_input_visitor_cleanup(StringInputVisitor *v)
 {
+    SignedRange *r, *next;
+
+    if (v->ranges) {
+        QTAILQ_FOREACH_SAFE(r, v->ranges, entry, next) {
+            QTAILQ_REMOVE(v->ranges, r, entry);
+            g_free(r);
+        }
+        g_free(v->ranges);
+    }
+
     g_free(v);
 }
 
@@ -155,8 +316,12 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v->visitor.type_bool = parse_type_bool;
     v->visitor.type_str = parse_type_str;
     v->visitor.type_number = parse_type_number;
+    v->visitor.start_list = start_list;
+    v->visitor.next_list = next_list;
+    v->visitor.end_list = end_list;
     v->visitor.start_optional = parse_start_optional;
 
     v->string = str;
+    v->head = true;
     return v;
 }
tests/test-string-input-visitor.c +39 -0
index d406263..98210b4 100644
@@ -64,6 +64,35 @@ static void test_visitor_in_int(TestInputVisitorData *data,
     g_assert_cmpint(res, ==, value);
 }
 
+static void test_visitor_in_intList(TestInputVisitorData *data,
+                                    const void *unused)
+{
+    int64_t value[] = {-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20};
+    int16List *res = NULL, *tmp;
+    Error *errp = NULL;
+    Visitor *v;
+    int i = 0;
+
+    v = visitor_input_test_init(data, "1,2,-2-1,2-4,20,5-9,1-8");
+
+    visit_type_int16List(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    tmp = res;
+    while (i < sizeof(value) / sizeof(value[0])) {
+        g_assert(tmp);
+        g_assert_cmpint(tmp->value, ==, value[i++]);
+        tmp = tmp->next;
+    }
+    g_assert(!tmp);
+
+    tmp = res;
+    while (tmp) {
+        res = res->next;
+        g_free(tmp);
+        tmp = res;
+    }
+}
+
 static void test_visitor_in_bool(TestInputVisitorData *data,
                                  const void *unused)
 {
@@ -170,6 +199,7 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
                                  const void *unused)
 {
     int64_t ires;
+    intList *ilres;
     bool bres;
     double nres;
     char *sres;
@@ -193,6 +223,11 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
 
         v = visitor_input_test_init(data, buf);
         visit_type_int(v, &ires, NULL, NULL);
+        visitor_input_teardown(data, NULL);
+
+        v = visitor_input_test_init(data, buf);
+        visit_type_intList(v, &ilres, NULL, NULL);
+        visitor_input_teardown(data, NULL);
 
         v = visitor_input_test_init(data, buf);
         visit_type_bool(v, &bres, NULL, NULL);
@@ -200,11 +235,13 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
 
         v = visitor_input_test_init(data, buf);
         visit_type_number(v, &nres, NULL, NULL);
+        visitor_input_teardown(data, NULL);
 
         v = visitor_input_test_init(data, buf);
         sres = NULL;
         visit_type_str(v, &sres, NULL, NULL);
         g_free(sres);
+        visitor_input_teardown(data, NULL);
 
         v = visitor_input_test_init(data, buf);
         visit_type_EnumOne(v, &eres, NULL, NULL);
@@ -228,6 +265,8 @@ int main(int argc, char **argv)
 
     input_visitor_test_add("/string-visitor/input/int",
                            &in_visitor_data, test_visitor_in_int);
+    input_visitor_test_add("/string-visitor/input/intList",
+                           &in_visitor_data, test_visitor_in_intList);
     input_visitor_test_add("/string-visitor/input/bool",
                            &in_visitor_data, test_visitor_in_bool);
     input_visitor_test_add("/string-visitor/input/number",
Signature · 1 line
1.8.5.2.229.g4448466
author
#37 lore raw

PATCH 28/31 qapi: make string output visitor parse int list

In reply to Hu Tao
---
 qapi/string-output-visitor.c       | 230 +++++++++++++++++++++++++++++++++++--
 tests/test-string-output-visitor.c |  34 ++++++
 2 files changed, 254 insertions(+), 10 deletions(-)
qapi/string-output-visitor.c +220 -10
index fb1d2e8..ccebc7a 100644
@@ -16,32 +16,173 @@
 #include "qapi/qmp/qerror.h"
 #include "qemu/host-utils.h"
 #include <math.h>
+#include "qemu/range.h"
+
+enum ListMode {
+    LM_NONE,             /* not traversing a list of repeated options */
+    LM_STARTED,          /* start_list() succeeded */
+
+    LM_IN_PROGRESS,      /* next_list() has been called.
+                          *
+                          * Generating the next list link will consume the most
+                          * recently parsed QemuOpt instance of the repeated
+                          * option.
+                          *
+                          * Parsing a value into the list link will examine the
+                          * next QemuOpt instance of the repeated option, and
+                          * possibly enter LM_SIGNED_INTERVAL or
+                          * LM_UNSIGNED_INTERVAL.
+                          */
+
+    LM_SIGNED_INTERVAL,  /* next_list() has been called.
+                          *
+                          * Generating the next list link will consume the most
+                          * recently stored element from the signed interval,
+                          * parsed from the most recent QemuOpt instance of the
+                          * repeated option. This may consume QemuOpt itself
+                          * and return to LM_IN_PROGRESS.
+                          *
+                          * Parsing a value into the list link will store the
+                          * next element of the signed interval.
+                          */
+
+    LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
+
+    LM_END
+};
+
+typedef enum ListMode ListMode;
 
 struct StringOutputVisitor
 {
     Visitor visitor;
     bool human;
-    char *string;
+    GString *string;
+    bool head;
+    ListMode list_mode;
+    union {
+        int64_t s;
+        uint64_t u;
+    } range_start, range_end;
+    SignedRangeList *ranges;
 };
 
 static void string_output_set(StringOutputVisitor *sov, char *string)
 {
-    g_free(sov->string);
-    sov->string = string;
+    if (sov->string) {
+        g_string_free(sov->string, true);
+    }
+    sov->string = g_string_new(string);
+    g_free(string);
+}
+
+static void string_output_append(StringOutputVisitor *sov, int64_t a)
+{
+    range_list_add(sov->ranges, a, 1);
+}
+
+static void string_output_append_range(StringOutputVisitor *sov,
+                                       int64_t s, int64_t e)
+{
+    range_list_add(sov->ranges, s, e);
 }
 
 static void print_type_int(Visitor *v, int64_t *obj, const char *name,
                            Error **errp)
 {
     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
-    char *out;
+    SignedRange *r;
+
+    if (!sov->ranges) {
+        sov->ranges = g_malloc0(sizeof(*sov->ranges));
+        QTAILQ_INIT(sov->ranges);
+    }
+
+    switch (sov->list_mode) {
+    case LM_NONE:
+        string_output_append(sov, *obj);
+        break;
+
+    case LM_STARTED:
+        sov->range_start.s = *obj;
+        sov->range_end.s = *obj;
+        sov->list_mode = LM_IN_PROGRESS;
+        return;
+
+    case LM_IN_PROGRESS:
+        if (sov->range_end.s + 1 == *obj) {
+            sov->range_end.s++;
+        } else {
+            if (sov->range_start.s == sov->range_end.s) {
+                string_output_append(sov, sov->range_end.s);
+            } else {
+                assert(sov->range_start.s < sov->range_end.s);
+                string_output_append_range(sov, sov->range_start.s,
+                                           sov->range_end.s -
+                                           sov->range_start.s + 1);
+            }
+
+            sov->range_start.s = *obj;
+            sov->range_end.s = *obj;
+        }
+        return;
+
+    case LM_END:
+        if (sov->range_end.s + 1 == *obj) {
+            sov->range_end.s++;
+            assert(sov->range_start.s < sov->range_end.s);
+            string_output_append_range(sov, sov->range_start.s,
+                                       sov->range_end.s -
+                                       sov->range_start.s + 1);
+        } else {
+            if (sov->range_start.s == sov->range_end.s) {
+                string_output_append(sov, sov->range_end.s);
+            } else {
+                assert(sov->range_start.s < sov->range_end.s);
+
+                string_output_append_range(sov, sov->range_start.s,
+                                           sov->range_end.s -
+                                           sov->range_start.s + 1);
+            }
+            string_output_append(sov, *obj);
+        }
+        break;
+
+    default:
+        abort();
+    }
+
+    QTAILQ_FOREACH(r, sov->ranges, entry) {
+        if (r->length > 1) {
+            g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
+                                   r->start,
+                                   s_range_end(r->start, r->length));
+        } else {
+            g_string_append_printf(sov->string, "%" PRId64,
+                                   r->start);
+        }
+        if (r->entry.tqe_next) {
+            g_string_append(sov->string, ",");
+        }
+    }
 
     if (sov->human) {
-        out = g_strdup_printf("%lld (%#llx)", (long long) *obj, (long long) *obj);
-    } else {
-        out = g_strdup_printf("%lld", (long long) *obj);
+        g_string_append(sov->string, " (");
+        QTAILQ_FOREACH(r, sov->ranges, entry) {
+            if (r->length > 1) {
+                g_string_append_printf(sov->string, "%" PRIx64 "-%" PRIx64,
+                                       r->start,
+                                       s_range_end(r->start, r->length));
+            } else {
+                g_string_append_printf(sov->string, "%" PRIx64,
+                                       r->start);
+            }
+            if (r->entry.tqe_next) {
+                g_string_append(sov->string, ",");
+            }
+        }
+        g_string_append(sov->string, ")");
     }
-    string_output_set(sov, out);
 }
 
 static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
@@ -103,9 +244,61 @@ static void print_type_number(Visitor *v, double *obj, const char *name,
     string_output_set(sov, g_strdup_printf("%f", *obj));
 }
 
+static void
+start_list(Visitor *v, const char *name, Error **errp)
+{
+    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+
+    /* we can't traverse a list in a list */
+    assert(sov->list_mode == LM_NONE);
+    sov->list_mode = LM_STARTED;
+    sov->head = true;
+}
+
+static GenericList *
+next_list(Visitor *v, GenericList **list, Error **errp)
+{
+    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    GenericList *ret = NULL;
+    if (*list) {
+        if (sov->head) {
+            ret = *list;
+        } else {
+            ret = (*list)->next;
+        }
+
+        if (sov->head) {
+            if (ret && ret->next == NULL) {
+                sov->list_mode = LM_NONE;
+            }
+            sov->head = false;
+        } else {
+            if (ret && ret->next == NULL) {
+                sov->list_mode = LM_END;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static void
+end_list(Visitor *v, Error **errp)
+{
+    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+
+    assert(sov->list_mode == LM_STARTED ||
+           sov->list_mode == LM_END ||
+           sov->list_mode == LM_NONE ||
+           sov->list_mode == LM_IN_PROGRESS);
+    sov->list_mode = LM_NONE;
+    sov->head = true;
+
+}
+
 char *string_output_get_string(StringOutputVisitor *sov)
 {
-    char *string = sov->string;
+    char *string = g_string_free(sov->string, false);
     sov->string = NULL;
     return string;
 }
@@ -117,7 +310,20 @@ Visitor *string_output_get_visitor(StringOutputVisitor *sov)
 
 void string_output_visitor_cleanup(StringOutputVisitor *sov)
 {
-    g_free(sov->string);
+    SignedRange *r, *next;
+
+    if (sov->string) {
+        g_string_free(sov->string, true);
+    }
+
+    if (sov->ranges) {
+        QTAILQ_FOREACH_SAFE(r, sov->ranges, entry, next) {
+            QTAILQ_REMOVE(sov->ranges, r, entry);
+            s_range_free(r);
+        }
+        g_free(sov->ranges);
+    }
+
     g_free(sov);
 }
 
@@ -127,6 +333,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
 
     v = g_malloc0(sizeof(*v));
 
+    v->string = g_string_new(NULL);
     v->human = human;
     v->visitor.type_enum = output_type_enum;
     v->visitor.type_int = print_type_int;
@@ -134,6 +341,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->visitor.type_bool = print_type_bool;
     v->visitor.type_str = print_type_str;
     v->visitor.type_number = print_type_number;
+    v->visitor.start_list = start_list;
+    v->visitor.next_list = next_list;
+    v->visitor.end_list = end_list;
 
     return v;
 }
tests/test-string-output-visitor.c +34 -0
index 22363d1..d4b47fe 100644
@@ -57,6 +57,38 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
     g_free(str);
 }
 
+static void test_visitor_out_intList(TestOutputVisitorData *data,
+                                     const void *unused)
+{
+    int64_t value[] = {-10, -7, -2, -1, 0, 1, 9, 10, 16, 15, 14,
+        3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX};
+    intList *list = NULL, **tmp = &list;
+    int i;
+    Error *errp = NULL;
+    char *str;
+
+    for (i = 0; i < sizeof(value) / sizeof(value[0]); i++) {
+        *tmp = g_malloc0(sizeof(**tmp));
+        (*tmp)->value = value[i];
+        tmp = &(*tmp)->next;
+    }
+
+    visit_type_intList(data->ov, &list, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==,
+        "-10,-7,-2-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807");
+    g_free(str);
+    while (list) {
+        intList *tmp2;
+        tmp2 = list->next;
+        g_free(list);
+        list = tmp2;
+    }
+}
+
 static void test_visitor_out_bool(TestOutputVisitorData *data,
                                   const void *unused)
 {
@@ -182,6 +214,8 @@ int main(int argc, char **argv)
                             &out_visitor_data, test_visitor_out_enum);
     output_visitor_test_add("/string-visitor/output/enum-errors",
                             &out_visitor_data, test_visitor_out_enum_errors);
+    output_visitor_test_add("/string-visitor/output/intList",
+                            &out_visitor_data, test_visitor_out_intList);
 
     g_test_run();
 
Signature · 1 line
1.8.5.2.229.g4448466
author
#38 lore raw

PATCH 29/31 qom: introduce object_property_get_enum and object_property_get_uint16List

In reply to Hu Tao
---
 include/qom/object.h | 28 ++++++++++++++++++++++++++++
 qom/object.c         | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)
include/qom/object.h +28 -0
index a641dcd..b882ccc 100644
@@ -917,6 +917,34 @@ int64_t object_property_get_int(Object *obj, const char *name,
                                 Error **errp);
 
 /**
+ * object_property_get_enum:
+ * @obj: the object
+ * @name: the name of the property
+ * @strings: strings corresponding to enums
+ * @errp: returns an error if this function fails
+ *
+ * Returns: the value of the property, converted to an integer, or
+ * undefined if an error occurs (including when the property value is not
+ * an enum).
+ */
+int object_property_get_enum(Object *obj, const char *name,
+                             const char *strings[], Error **errp);
+
+/**
+ * object_property_get_uint16List:
+ * @obj: the object
+ * @name: the name of the property
+ * @list: the returned int list
+ * @errp: returns an error if this function fails
+ *
+ * Returns: the value of the property, converted to integers, or
+ * undefined if an error occurs (including when the property value is not
+ * an list of integers).
+ */
+void object_property_get_uint16List(Object *obj, const char *name,
+                                    uint16List **list, Error **errp);
+
+/**
  * object_property_set:
  * @obj: the object
  * @v: the visitor that will be used to write the property value.  This should
qom/object.c +35 -0
index e42b254..3876618 100644
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 #include "qemu-common.h"
 #include "qapi/visitor.h"
+#include "qapi-visit.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/string-output-visitor.h"
 #include "qapi/qmp/qerror.h"
@@ -938,6 +939,40 @@ int64_t object_property_get_int(Object *obj, const char *name,
     return retval;
 }
 
+int object_property_get_enum(Object *obj, const char *name,
+                             const char *strings[], Error **errp)
+{
+    StringOutputVisitor *sov;
+    StringInputVisitor *siv;
+    int ret;
+
+    sov = string_output_visitor_new(false);
+    object_property_get(obj, string_output_get_visitor(sov), name, errp);
+    siv = string_input_visitor_new(string_output_get_string(sov));
+    string_output_visitor_cleanup(sov);
+    visit_type_enum(string_input_get_visitor(siv),
+                    &ret, strings, NULL, name, errp);
+    string_input_visitor_cleanup(siv);
+
+    return ret;
+}
+
+void object_property_get_uint16List(Object *obj, const char *name,
+                                    uint16List **list, Error **errp)
+{
+    StringOutputVisitor *ov;
+    StringInputVisitor *iv;
+
+    ov = string_output_visitor_new(false);
+    object_property_get(obj, string_output_get_visitor(ov),
+                        name, errp);
+    iv = string_input_visitor_new(string_output_get_string(ov));
+    visit_type_uint16List(string_input_get_visitor(iv),
+                          list, NULL, errp);
+    string_output_visitor_cleanup(ov);
+    string_input_visitor_cleanup(iv);
+}
+
 void object_property_parse(Object *obj, const char *string,
                            const char *name, Error **errp)
 {
Signature · 1 line
1.8.5.2.229.g4448466
author
#39 lore raw

PATCH 30/31 qmp: add query-memdev

In reply to Hu Tao

Add qmp command query-memdev to query for information of memory devices

---
 numa.c           | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json | 34 ++++++++++++++++++++++++++
 qmp-commands.hx  | 32 +++++++++++++++++++++++++
 3 files changed, 138 insertions(+)
numa.c +72 -0
index fa117f0..77b6ca1 100644
@@ -32,9 +32,14 @@
 #include "qapi-visit.h"
 #include "qapi/opts-visitor.h"
 #include "qapi/dealloc-visitor.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/string-output-visitor.h"
+#include "qapi/string-input-visitor.h"
 #include "qapi/qmp/qerror.h"
 #include "hw/boards.h"
 #include "sysemu/hostmem.h"
+#include "qmp-commands.h"
 
 QemuOptsList qemu_numa_opts = {
     .name = "numa",
@@ -281,3 +286,70 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
         addr += size;
     }
 }
+
+MemdevList *qmp_query_memdev(Error **errp)
+{
+    MemdevList *list = NULL, *m;
+    HostMemoryBackend *backend;
+    Error *err = NULL;
+    int i;
+
+    for (i = 0; i < nb_numa_nodes; i++) {
+        backend = numa_info[i].node_memdev;
+
+        m = g_malloc0(sizeof(*m));
+        m->value = g_malloc0(sizeof(*m->value));
+        m->value->size = object_property_get_int(OBJECT(backend), "size",
+                                                 &err);
+        if (err) {
+            goto error;
+        }
+
+        m->value->merge = object_property_get_bool(OBJECT(backend), "merge",
+                                                   &err);
+        if (err) {
+            goto error;
+        }
+
+        m->value->dump = object_property_get_bool(OBJECT(backend), "dump",
+                                                  &err);
+        if (err) {
+            goto error;
+        }
+
+        m->value->prealloc = object_property_get_bool(OBJECT(backend),
+                                                      "prealloc", &err);
+        if (err) {
+            goto error;
+        }
+
+        m->value->policy = object_property_get_enum(OBJECT(backend),
+                                                    "policy",
+                                                    HostMemPolicy_lookup,
+                                                    &err);
+        if (err) {
+            goto error;
+        }
+
+        object_property_get_uint16List(OBJECT(backend), "host-nodes",
+                                       &m->value->host_nodes, &err);
+        if (err) {
+            goto error;
+        }
+
+        m->next = list;
+        list = m;
+    }
+
+    return list;
+
+error:
+    while (list) {
+        m = list;
+        list = list->next;
+        g_free(m->value);
+        g_free(m);
+    }
+    qerror_report_err(err);
+    return NULL;
+}
qapi-schema.json +34 -0
index bea3476..bd0d6c1 100644
@@ -4752,3 +4752,37 @@
 ##
 { 'enum': 'HostMemPolicy',
   'data': [ 'default', 'preferred', 'bind', 'interleave' ] }
+
+##
+# @Memdev:
+#
+# Information of memory device
+#
+# @size: memory device size
+#
+# @host-nodes: host nodes for its memory policy
+#
+# @policy: memory policy of memory device
+#
+# Since: 2.1
+##
+
+{ 'type': 'Memdev',
+  'data': {
+    'size':       'size',
+    'merge':      'bool',
+    'dump':       'bool',
+    'prealloc':   'bool',
+    'host-nodes': ['uint16'],
+    'policy':     'HostMemPolicy' }}
+
+##
+# @query-memdev:
+#
+# Returns information for all memory devices.
+#
+# Returns: a list of @Memdev.
+#
+# Since: 2.1
+##
+{ 'command': 'query-memdev', 'returns': ['Memdev'] }
qmp-commands.hx +32 -0
index ed3ab92..5a5b37f 100644
@@ -3562,3 +3562,35 @@ Example:
                    } } ] }
 
 EQMP
+
+    {
+        .name       = "query-memdev",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_memdev,
+    },
+
+SQMP
+query-memdev
+------------
+
+Show memory devices information.
+
+
+Example (1):
+
+-> { "execute": "query-memdev" }
+<- { "return": [
+       {
+         "size": 536870912,
+         "host-nodes": [0, 1],
+         "policy": "bind"
+       },
+       {
+         "size": 536870912,
+         "host-nodes": [2, 3],
+         "policy": "preferred"
+       }
+     ]
+   }
+
+EQMP
Signature · 1 line
1.8.5.2.229.g4448466
author
#40 lore raw

PATCH 31/31 hmp: add info memdev

In reply to Hu Tao

This is the hmp counterpart of qmp query-memdev.

---
 hmp.c     | 36 ++++++++++++++++++++++++++++++++++++
 hmp.h     |  1 +
 monitor.c |  7 +++++++
 3 files changed, 44 insertions(+)
hmp.c +36 -0
index ca869ba..bde0df9 100644
@@ -22,6 +22,8 @@
 #include "qemu/sockets.h"
 #include "monitor/monitor.h"
 #include "qapi/opts-visitor.h"
+#include "qapi/string-output-visitor.h"
+#include "qapi-visit.h"
 #include "ui/console.h"
 #include "block/qapi.h"
 #include "qemu-io.h"
@@ -1666,3 +1668,37 @@ void hmp_object_del(Monitor *mon, const QDict *qdict)
     qmp_object_del(id, &err);
     hmp_handle_error(mon, &err);
 }
+
+void hmp_info_memdev(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    MemdevList *memdev_list = qmp_query_memdev(&err);
+    MemdevList *m = memdev_list;
+    StringOutputVisitor *ov;
+    int i = 0;
+
+
+    while (m) {
+        ov = string_output_visitor_new(false);
+        visit_type_uint16List(string_output_get_visitor(ov),
+                              &m->value->host_nodes, NULL, NULL);
+        monitor_printf(mon, "memory device %d\n", i);
+        monitor_printf(mon, "  size: %ld\n", m->value->size);
+        monitor_printf(mon, "  merge: %s\n",
+                       m->value->merge ? "true" : "false");
+        monitor_printf(mon, "  dump: %s\n",
+                       m->value->dump ? "true" : "false");
+        monitor_printf(mon, "  prealloc: %s\n",
+                       m->value->prealloc ? "true" : "false");
+        monitor_printf(mon, "  policy: %s\n",
+                       HostMemPolicy_lookup[m->value->policy]);
+        monitor_printf(mon, "  host nodes: %s\n",
+                       string_output_get_string(ov));
+
+        string_output_visitor_cleanup(ov);
+        m = m->next;
+        i++;
+    }
+
+    monitor_printf(mon, "\n");
+}
hmp.h +1 -0
index 20ef454..bc13aae 100644
@@ -97,5 +97,6 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_del_completion(ReadLineState *rs, int nb_args, const char *str);
+void hmp_info_memdev(Monitor *mon, const QDict *qdict);
 
 #endif
monitor.c +7 -0
index db17a31..d9cc6b9 100644
@@ -2941,6 +2941,13 @@ static mon_cmd_t info_cmds[] = {
         .mhandler.cmd = hmp_info_tpm,
     },
     {
+        .name       = "memdev",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the memory device",
+        .mhandler.cmd = hmp_info_memdev,
+    },
+    {
         .name       = NULL,
     },
 };
Signature · 1 line
1.8.5.2.229.g4448466
#41 lore raw
In reply to Hu Tao

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +12 -1
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.
+     */
+    if (find_first_bit(backend->host_nodes, MAX_NODES) != MAX_NODES &&
+        policy == MPOL_DEFAULT) {
+        policy = MPOL_BIND;
+    }
 
     /* This is a workaround for a long standing bug in Linux'
      * mbind implementation, which cuts off the last specified
      * node.
      */
-    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+    if (mbind(ptr, sz, policy, backend->host_nodes, maxnode + 2, 0)) {
         error_setg_errno(errp, errno,
                          "cannot bind memory to host NUMA nodes");
author
#42 lore raw
Quoted 27 lines · Paolo Bonzini

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +1 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 312

Quoted 10 lines
backends/hostmem.c +10 -0
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.
+     */
+    if (find_first_bit(backend->host_nodes, MAX_NODES) != MAX_NODES &&
+        policy == MPOL_DEFAULT) {
+        policy = MPOL_BIND;
+    }

Looks reasonable to me. Thanks!

Quoted 9 lines
backends/hostmem.c +1 -1
     /* This is a workaround for a long standing bug in Linux'
      * mbind implementation, which cuts off the last specified
      * node.
      */
-    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+    if (mbind(ptr, sz, policy, backend->host_nodes, maxnode + 2, 0)) {
         error_setg_errno(errp, errno,
                          "cannot bind memory to host NUMA nodes");
author
#43 lore raw
In reply to Paolo Bonzini
Quoted 27 lines · Paolo Bonzini

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +1 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 312

Quoted 10 lines
backends/hostmem.c +10 -0
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.
+     */
+    if (find_first_bit(backend->host_nodes, MAX_NODES) != MAX_NODES &&
+        policy == MPOL_DEFAULT) {
+        policy = MPOL_BIND;
+    }

This only changes the policy applied to memory. But info memdev still shows the policy as 'default'(the 'default' here is interpreted as 'MPOL_DEFAULT', right?).

So this is related to how we deal with cases where 'policy' is not specified in qemu:

1. if possible, the policy is set to MPOL_DEFAULT.
2. if host-nodes is not zero, the policy is set to MPOL_BIND.(set backend->policy too, other than just apply it)

Opinions?

Quoted 9 lines
backends/hostmem.c +1 -1
     /* This is a workaround for a long standing bug in Linux'
      * mbind implementation, which cuts off the last specified
      * node.
      */
-    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+    if (mbind(ptr, sz, policy, backend->host_nodes, maxnode + 2, 0)) {
         error_setg_errno(errp, errno,
                          "cannot bind memory to host NUMA nodes");
#44 lore raw
Quoted 25 lines · Hu Tao
Quoted 24 lines · Paolo Bonzini

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +0 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 307

Quoted 8 lines
backends/hostmem.c +6 -0
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.

Are we sure MPOL_BIND is a better default than MPOL_INTERLEAVE or MPOL_PREFERRED?

Quoted 9 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 312

Quoted 5 lines
backends/hostmem.c +5 -0
+     */
+    if (find_first_bit(backend->host_nodes, MAX_NODES) != MAX_NODES &&
+        policy == MPOL_DEFAULT) {
+        policy = MPOL_BIND;
+    }

This only changes the policy applied to memory. But info memdev still shows the policy as 'default'(the 'default' here is interpreted as 'MPOL_DEFAULT', right?).

That's what I would expect. With this, the user can't even check what is the actual result of the command-line they used.

Quoted 7 lines

So this is related to how we deal with cases where 'policy' is not specified in qemu:

1. if possible, the policy is set to MPOL_DEFAULT.
2. if host-nodes is not zero, the policy is set to MPOL_BIND.(set backend->policy too, other than just apply it)

You mean, only if policy is omitted, right? host-nodes may be set if the user wants policy=preferred or policy=interleave, too.

Quoted 2 lines

Opinions?

I find it confusing that an explicit policy=default option won't always mean MPOL_DEFAULT.

So, what about:

* If policy=default is set, it is always going to be MPOL_DEFAULT.
* If policy=bind is set, it is always going to be MPOL_BIND.
* if policy=preferred is set, it is always going to be MPOL_PREFERRED.
* If policy is omitted, it will be be MPOL_DEFAULT is host-nodes is unset, and MPOL_BIND if host_nodes is set.

Quoted 11 lines
Quoted 10 lines
backends/hostmem.c +1 -1
     /* This is a workaround for a long standing bug in Linux'
      * mbind implementation, which cuts off the last specified
      * node.
      */
-    if (mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 2, 0)) {
+    if (mbind(ptr, sz, policy, backend->host_nodes, maxnode + 2, 0)) {
         error_setg_errno(errp, errno,
                          "cannot bind memory to host NUMA nodes");
Signature · 1 line
Eduardo
#45 lore raw

Il 09/05/2014 19:54, Eduardo Habkost ha scritto:

Quoted 26 lines · Hu Tao
Quoted 25 lines · Paolo Bonzini
Quoted 24 lines

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +0 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
Quoted 11 lines
Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 307

Quoted 8 lines
backends/hostmem.c +6 -0
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.

Are we sure MPOL_BIND is a better default than MPOL_INTERLEAVE or MPOL_PREFERRED?

Better than MPOL_PREFERRED, yes. Better than MPOL_INTERLEAVE, I am not sure.

Quoted 5 lines

* If policy=default is set, it is always going to be MPOL_DEFAULT.
* If policy=bind is set, it is always going to be MPOL_BIND.
* if policy=preferred is set, it is always going to be MPOL_PREFERRED.
* If policy is omitted, it will be be MPOL_DEFAULT is host-nodes is unset, and MPOL_BIND if host_nodes is set.

That's possible. Or we can just detect the case in host_memory_backend_memory_init and provide a better error message than just "Invalid argument" (aka EINVAL). We're always free to take an error situation and make it non-error in the future, but the reverse would be hard to do.

Paolo

author
#46 lore raw
Quoted 48 lines · Paolo Bonzini

Il 09/05/2014 19:54, Eduardo Habkost ha scritto:

Quoted 26 lines · Hu Tao
Quoted 25 lines · Paolo Bonzini
Quoted 24 lines

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +0 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
Quoted 11 lines
Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 307

Quoted 8 lines
backends/hostmem.c +6 -0
 #ifdef CONFIG_NUMA
    unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.

Are we sure MPOL_BIND is a better default than MPOL_INTERLEAVE or MPOL_PREFERRED?

Better than MPOL_PREFERRED, yes. Better than MPOL_INTERLEAVE, I am not sure.

Quoted 5 lines

* If policy=default is set, it is always going to be MPOL_DEFAULT.
* If policy=bind is set, it is always going to be MPOL_BIND.
* if policy=preferred is set, it is always going to be MPOL_PREFERRED.
* If policy is omitted, it will be be MPOL_DEFAULT is host-nodes is unset, and MPOL_BIND if host_nodes is set.

Quoted 5 lines

That's possible. Or we can just detect the case in host_memory_backend_memory_init and provide a better error message than just "Invalid argument" (aka EINVAL). We're always free to take an error situation and make it non-error in the future, but the reverse would be hard to do.

This one looks better. If we decide to turn implict MPOL_BIND into error in the future, it may cause a long-run guest with policy ommitted fail to start, which sounds like a compatibility problem.

Regards,
Hu Tao

author
#47 lore raw
In reply to Eduardo Habkost
Quoted 56 lines · Eduardo Habkost
Quoted 25 lines · Hu Tao
Quoted 24 lines · Paolo Bonzini

Il 06/05/2014 11:27, Hu Tao ha scritto:

Quoted 6 lines

This series includes work on QOMifying the memory backends. the idea is to delegate all properties of the memory backend to a new QOM class hierarchy, in which the concrete classes are hostmem-ram and hostmem-file. The backend is passed to the machine via "-numa node,memdev=foo" where "foo" is the id of the backend object.

Hello,

I noticed now that if you have the host-nodes property set Linux requires you to set a policy other than "default" too. If you don't, the mbind system call fails.

What about squashing something like this?

Paolo

backends/hostmem.c +0 -0
index d3f8476..a0a3111 100644
@@ -299,12 +299,23 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
 
Quoted 8 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 307

Quoted 8 lines
backends/hostmem.c +6 -0
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
+    unsigned policy = backend->policy;
+
+    /* Linux does not accept MPOL_DEFAULT with nonzero bitmap, but
+     * "-object memory-ram,size=128M,hostnodes=0,policy=bind" is a
+     * bit of a mouthful.  So if the host_nodes bitmap is nonzero,
+     * pick the BIND policy.

Are we sure MPOL_BIND is a better default than MPOL_INTERLEAVE or MPOL_PREFERRED?

Quoted 9 lines

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 312

Quoted 5 lines
backends/hostmem.c +5 -0
+     */
+    if (find_first_bit(backend->host_nodes, MAX_NODES) != MAX_NODES &&
+        policy == MPOL_DEFAULT) {
+        policy = MPOL_BIND;
+    }

This only changes the policy applied to memory. But info memdev still shows the policy as 'default'(the 'default' here is interpreted as 'MPOL_DEFAULT', right?).

That's what I would expect. With this, the user can't even check what is the actual result of the command-line they used.

Quoted 4 lines

So this is related to how we deal with cases where 'policy' is not specified in qemu:

Quoted 6 lines
Quoted 3 lines

1. if possible, the policy is set to MPOL_DEFAULT.
2. if host-nodes is not zero, the policy is set to MPOL_BIND.(set backend->policy too, other than just apply it)

You mean, only if policy is omitted, right? host-nodes may be set if the user wants policy=preferred or policy=interleave, too.

Yes.

Quoted 7 lines
Quoted 3 lines

Opinions?

I find it confusing that an explicit policy=default option won't always mean MPOL_DEFAULT.

Quoted 7 lines

So, what about:

* If policy=default is set, it is always going to be MPOL_DEFAULT.
* If policy=bind is set, it is always going to be MPOL_BIND.
* if policy=preferred is set, it is always going to be MPOL_PREFERRED.
* If policy is omitted, it will be be MPOL_DEFAULT is host-nodes is unset, and MPOL_BIND if host_nodes is set.

Agreed.

Regards,
Hu Tao

#48 lore raw

PATCH 1/1 preallocate memory after NUMA policy configuration

In reply to Hu Tao

(note: applies on top of patchset of current thread)

Preallocate memory after the NUMA policy has been instantiated. This is necessary to guarantee memory is allocated with specified NUMA policy in place.

backends/hostmem.c +3 -3
index d3f8476..3cfa8a0 100644
@@ -293,9 +293,6 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (!backend->dump) {
         qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP);
     }
-    if (backend->prealloc) {
-        os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
-    }
 
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);
@@ -310,6 +307,9 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
         return;
     }
 #endif
+    if (backend->prealloc) {
+        os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
+    }
 }
 
 MemoryRegion *
author
#49 lore raw

Re: [Qemu-devel] [PATCH] preallocate memory after NUMA policy configuration

Quoted 24 lines · Marcelo Tosatti

(note: applies on top of patchset of current thread)

Preallocate memory after the NUMA policy has been instantiated. This is necessary to guarantee memory is allocated with specified NUMA policy in place.

backends/hostmem.c +0 -3
index d3f8476..3cfa8a0 100644
@@ -293,9 +293,6 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
     if (!backend->dump) {
         qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP);
     }
-    if (backend->prealloc) {
-        os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
-    }
 
 #ifdef CONFIG_NUMA
     unsigned long maxnode = find_last_bit(backend->host_nodes, MAX_NODES);

backends/hostmem.c host_memory_backend_memory_init(UserCreatable *uc, Error **errp) · line 315

Quoted 9 lines
backends/hostmem.c +3 -0
@@ -310,6 +307,9 @@ host_memory_backend_memory_init(UserCreatable *uc, Error **errp)
         return;
     }
 #endif
+    if (backend->prealloc) {
+        os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
+    }
 }
 
 MemoryRegion *

Thanks, I'll squash it in patch 25.

Regards,
Hu Tao