[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 3/4] hvm: NUMA guest: allocate memory and pin cpus according to guestnodes number



This patch introduces a new config file option called guestnodes.
Depending on the specified number (which can be 0 (the default) to
return to current behavior) a set of suitable nodes (which have enough
memory and are the least used ones) is selected and memory allocation is
split evenly across these host nodes. CPU affinity is set accordingly.

Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>

Regards,
Andre

--
Andre Przywara
AMD-Operating System Research Center (OSRC), Dresden, Germany
Tel: +49 351 277-84917
----to satisfy European Law for business letters:
AMD Saxony Limited Liability Company & Co. KG,
Wilschdorfer Landstr. 101, 01109 Dresden, Germany
Register Court Dresden: HRA 4896, General Partner authorized
to represent: AMD Saxony LLC (Wilmington, Delaware, US)
General Manager of AMD Saxony LLC: Dr. Hans-R. Deppe, Thomas McCoy
# HG changeset patch
# User Andre Przywara <andre.przywara@xxxxxxx>
# Date 1215083831 -7200
# Node ID b84c5f2fe83bd7c94ed956ba412689e614177f5c
# Parent  a0dccef499b005ba13eb70bf6cac856af44a10a0
make guest memory allocation NUMA aware

diff -r a0dccef499b0 -r b84c5f2fe83b tools/libxc/xc_hvm_build.c
--- a/tools/libxc/xc_hvm_build.c        Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/libxc/xc_hvm_build.c        Thu Jul 03 13:17:11 2008 +0200
@@ -18,6 +18,8 @@
 #include "xc_e820.h"
 
 #include <xen/libelf.h>
+
+#include <asm/bitops.h>
 
 #define SUPERPAGE_PFN_SHIFT  9
 #define SUPERPAGE_NR_PFNS    (1UL << SUPERPAGE_PFN_SHIFT)
@@ -155,8 +157,171 @@
     return rc;
 }
 
+static int hweight_long (unsigned long value)
+{
+int ret=0;
+
+    while (value>0)
+    {
+        if (value&1) ++ret;
+        value>>=1;
+    }
+    return ret;
+}
+
+static int get_nodemasks (int xc_handle, uint64_t **nodemasks)
+{
+#define MAX_CPU_ID 255
+    xc_physinfo_t physinfo;
+    xc_cpu_to_node_t *cpumap;
+    int nrcpus, i;
+
+    cpumap=(xc_cpu_to_node_t *)malloc(sizeof(xc_cpu_to_node_t)*MAX_CPU_ID);
+    set_xen_guest_handle(physinfo.cpu_to_node, cpumap);
+
+    xc_physinfo (xc_handle,&physinfo);
+    nrcpus = physinfo.threads_per_core * physinfo.cores_per_socket *
+             physinfo.nr_nodes;
+
+    *nodemasks=malloc(sizeof(uint64_t)*physinfo.nr_nodes);
+    memset (*nodemasks,0,sizeof(uint64_t)*physinfo.nr_nodes);
+    for ( i = 0; i < nrcpus; i++ )
+    {
+        (*nodemasks)[cpumap[i]] |= 1 << i;
+    }
+    return nrcpus;
+}
+
+/* Distribute the VCPUs to the given NUMA nodes.
+ * Use xc_vcpu_setaffinity to pin physical CPUs to the VCPUs.
+ */
+static int setup_numa_affinity (int xc_handle, uint32_t dom,
+                                unsigned long nodemask)
+{
+    uint64_t *nodemasks, usemask;
+
+    int nrcpus, i;
+    xc_dominfo_t dominfo;
+    int nrnodes,curnode,vcpusleft;
+
+    nrnodes = hweight_long (nodemask);
+
+    nrcpus = get_nodemasks (xc_handle, &nodemasks);
+
+    if (xc_domain_getinfo (xc_handle, dom, 1, &dominfo) != 1)
+    {
+        ERROR("Unable to get platform info.");
+        return -1;
+    }
+    curnode = -1;
+    vcpusleft = 0;
+    for ( i = 0; i <= dominfo.max_vcpu_id; i++ )
+    {
+        if ( vcpusleft == 0 )
+        {
+            vcpusleft = ( dominfo.max_vcpu_id + 1 ) / nrnodes;
+            if ( ++curnode < ( ( dominfo.max_vcpu_id + 1 ) % nrnodes ) )
+                vcpusleft++;
+            usemask = nodemasks[__ffs(nodemask)];
+                   nodemask &= ~(1ULL<<__ffs(nodemask));
+        }
+        xc_vcpu_setaffinity (xc_handle, dom, i, usemask);
+        vcpusleft--;
+    }
+
+    return 0;
+}
+
+static int populate_on_node ( int xc_handle, uint32_t dom,
+                              unsigned long *cur_pages,
+                              unsigned long nr_pages,
+                              int node, xen_pfn_t* page_array)
+{
+int rc=0;
+unsigned long i;
+
+    while ( (rc == 0) && (nr_pages > 0 ) )
+    {
+        /* Clip count to maximum 8MB extent. */
+        unsigned long count = nr_pages;
+        if ( count > 2048 )
+            count = 2048;
+
+        /* Clip partial superpage extents to superpage boundaries. */
+        if ( ((*cur_pages & (SUPERPAGE_NR_PFNS-1)) != 0) &&
+             (count > (-*cur_pages & (SUPERPAGE_NR_PFNS-1))) )
+            count = -*cur_pages & (SUPERPAGE_NR_PFNS-1); /* clip s.p. tail */
+        else if ( ((count & (SUPERPAGE_NR_PFNS-1)) != 0) &&
+                  (count > SUPERPAGE_NR_PFNS) )
+            count &= ~(SUPERPAGE_NR_PFNS - 1); /* clip non-s.p. tail */
+
+        /* Attempt to allocate superpage extents. */
+        if ( ((count | *cur_pages) & (SUPERPAGE_NR_PFNS - 1)) == 0 )
+        {
+            long done;
+            xen_pfn_t sp_extents[2048 >> SUPERPAGE_PFN_SHIFT];
+            struct xen_memory_reservation sp_req = {
+                .nr_extents   = count >> SUPERPAGE_PFN_SHIFT,
+                .extent_order = SUPERPAGE_PFN_SHIFT,
+                .mem_flags    = XENMEM_set_node(node),
+                .domid        = dom
+            };
+            set_xen_guest_handle(sp_req.extent_start, sp_extents);
+            for ( i = 0; i < sp_req.nr_extents; i++ )
+                sp_extents[i] = 
page_array[*cur_pages+(i<<SUPERPAGE_PFN_SHIFT)];
+            done = xc_memory_op(xc_handle, XENMEM_populate_physmap, &sp_req);
+            if ( done > 0 )
+            {
+                done <<= SUPERPAGE_PFN_SHIFT;
+                *cur_pages += done;
+                count -= done;
+                nr_pages -= done;
+            }
+        }
+
+        /* Fall back to 4kB extents. */
+        if ( count != 0 )
+        {
+            rc = xc_domain_memory_populate_physmap(
+                xc_handle, dom, count, 0, 0, node, &page_array[*cur_pages]);
+            *cur_pages += count;
+            nr_pages -= count;
+        }
+    }
+    return rc;
+}
+
+static int setup_numa_mem ( int xc_handle, uint32_t dom,
+                            unsigned long *cur_pages, unsigned long nr_pages,
+                            unsigned nodemask, xen_pfn_t *page_array)
+{
+    int i, rc;
+    unsigned long cur_node_pages;
+    unsigned long pages_per_node;
+    int numanodes;
+
+    numanodes = hweight_long (nodemask);
+
+    pages_per_node = ((nr_pages+0xFF)&(~0xFFUL))/numanodes;
+
+    for ( i = 0 ; i < numanodes ; i++ )
+    {
+        if ( i == numanodes - 1 )
+            cur_node_pages = nr_pages - i * pages_per_node;
+        else cur_node_pages = pages_per_node;
+        if ( i == 0 ) cur_node_pages -= *cur_pages;
+
+        rc = populate_on_node (xc_handle, dom, cur_pages, cur_node_pages,
+                               __ffs(nodemask), page_array);
+        if ( rc != 0 ) return rc;
+
+        nodemask &= ~(1<<__ffs(nodemask));
+    }
+    return 0;
+}
+
 static int setup_guest(int xc_handle,
-                       uint32_t dom, int memsize,
+                       uint32_t dom, int memsize, unsigned long nodemask,
                        char *image, unsigned long image_size)
 {
     xen_pfn_t *page_array = NULL;
@@ -169,6 +334,7 @@
     struct elf_binary elf;
     uint64_t v_start, v_end;
     int rc;
+    int node;
     xen_capabilities_info_t caps;
 
     /* An HVM guest must be initialised with at least 2MB memory. */
@@ -217,60 +383,30 @@
      * We allocate pages in batches of no more than 8MB to ensure that
      * we can be preempted and hence dom0 remains responsive.
      */
+
+    if ( nodemask == 0 ) node = XENMEM_DEFAULT_NODE;
+        else node = __ffs (nodemask);
+
     rc = xc_domain_memory_populate_physmap(
-        xc_handle, dom, 0xa0, 0, 0, XENMEM_DEFAULT_NODE, &page_array[0x00]);
+        xc_handle, dom, 0xa0, 0, 0, node, &page_array[0x00]);
     cur_pages = 0xc0;
-    while ( (rc == 0) && (nr_pages > cur_pages) )
-    {
-        /* Clip count to maximum 8MB extent. */
-        unsigned long count = nr_pages - cur_pages;
-        if ( count > 2048 )
-            count = 2048;
 
-        /* Clip partial superpage extents to superpage boundaries. */
-        if ( ((cur_pages & (SUPERPAGE_NR_PFNS-1)) != 0) &&
-             (count > (-cur_pages & (SUPERPAGE_NR_PFNS-1))) )
-            count = -cur_pages & (SUPERPAGE_NR_PFNS-1); /* clip s.p. tail */
-        else if ( ((count & (SUPERPAGE_NR_PFNS-1)) != 0) &&
-                  (count > SUPERPAGE_NR_PFNS) )
-            count &= ~(SUPERPAGE_NR_PFNS - 1); /* clip non-s.p. tail */
-
-        /* Attempt to allocate superpage extents. */
-        if ( ((count | cur_pages) & (SUPERPAGE_NR_PFNS - 1)) == 0 )
-        {
-            long done;
-            xen_pfn_t sp_extents[2048 >> SUPERPAGE_PFN_SHIFT];
-            struct xen_memory_reservation sp_req = {
-                .nr_extents   = count >> SUPERPAGE_PFN_SHIFT,
-                .extent_order = SUPERPAGE_PFN_SHIFT,
-                .mem_flags     = XENMEM_set_node(XENMEM_DEFAULT_NODE),
-                .domid        = dom
-            };
-            set_xen_guest_handle(sp_req.extent_start, sp_extents);
-            for ( i = 0; i < sp_req.nr_extents; i++ )
-                sp_extents[i] = page_array[cur_pages+(i<<SUPERPAGE_PFN_SHIFT)];
-            done = xc_memory_op(xc_handle, XENMEM_populate_physmap, &sp_req);
-            if ( done > 0 )
-            {
-                done <<= SUPERPAGE_PFN_SHIFT;
-                cur_pages += done;
-                count -= done;
-            }
-        }
-
-        /* Fall back to 4kB extents. */
-        if ( count != 0 )
-        {
-            rc = xc_domain_memory_populate_physmap(
-                xc_handle, dom, count, 0, 0, XENMEM_DEFAULT_NODE, 
&page_array[cur_pages]);
-            cur_pages += count;
-        }
-    }
+    if ( hweight_long (nodemask) > 1 )
+        rc = setup_numa_mem (xc_handle, dom, &cur_pages, nr_pages,
+                             nodemask, page_array);
+    else
+        rc = populate_on_node (xc_handle, dom, &cur_pages, nr_pages - 
cur_pages,
+                               node, page_array);
 
     if ( rc != 0 )
     {
         PERROR("Could not allocate memory for HVM guest.\n");
         goto error_out;
+    }
+
+    if ( hweight_long (nodemask) > 1 )
+    {
+        setup_numa_affinity (xc_handle, dom, nodemask);
     }
 
     if ( loadelfimage(&elf, xc_handle, dom, page_array) != 0 )
@@ -365,6 +501,7 @@
 static int xc_hvm_build_internal(int xc_handle,
                                  uint32_t domid,
                                  int memsize,
+                                 unsigned long nodemask,
                                  char *image,
                                  unsigned long image_size)
 {
@@ -374,7 +511,7 @@
         return -1;
     }
 
-    return setup_guest(xc_handle, domid, memsize, image, image_size);
+    return setup_guest(xc_handle, domid, memsize, nodemask, image, image_size);
 }
 
 static inline int is_loadable_phdr(Elf32_Phdr *phdr)
@@ -389,6 +526,7 @@
 int xc_hvm_build(int xc_handle,
                  uint32_t domid,
                  int memsize,
+                 unsigned long nodemask,
                  const char *image_name)
 {
     char *image;
@@ -399,7 +537,8 @@
          ((image = xc_read_image(image_name, &image_size)) == NULL) )
         return -1;
 
-    sts = xc_hvm_build_internal(xc_handle, domid, memsize, image, image_size);
+    sts = xc_hvm_build_internal(xc_handle, domid, memsize, nodemask,
+                                image, image_size);
 
     free(image);
 
@@ -412,6 +551,7 @@
 int xc_hvm_build_mem(int xc_handle,
                      uint32_t domid,
                      int memsize,
+                     unsigned long nodemask,
                      const char *image_buffer,
                      unsigned long image_size)
 {
@@ -434,7 +574,7 @@
         return -1;
     }
 
-    sts = xc_hvm_build_internal(xc_handle, domid, memsize,
+    sts = xc_hvm_build_internal(xc_handle, domid, memsize, nodemask,
                                 img, img_len);
 
     /* xc_inflate_buffer may return the original buffer pointer (for
diff -r a0dccef499b0 -r b84c5f2fe83b tools/libxc/xenguest.h
--- a/tools/libxc/xenguest.h    Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/libxc/xenguest.h    Thu Jul 03 13:17:11 2008 +0200
@@ -128,11 +128,13 @@
 int xc_hvm_build(int xc_handle,
                  uint32_t domid,
                  int memsize,
+                 unsigned long nodemask,
                  const char *image_name);
 
 int xc_hvm_build_mem(int xc_handle,
                      uint32_t domid,
                      int memsize,
+                     unsigned long nodemask,
                      const char *image_buffer,
                      unsigned long image_size);
 
diff -r a0dccef499b0 -r b84c5f2fe83b tools/libxc/xg_private.c
--- a/tools/libxc/xg_private.c  Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/libxc/xg_private.c  Thu Jul 03 13:17:11 2008 +0200
@@ -177,6 +177,7 @@
     int xc_hvm_build(int xc_handle,
                      uint32_t domid,
                      int memsize,
+                     unsigned long nodemask,
                      const char *image_name)
 {
     errno = ENOSYS;
diff -r a0dccef499b0 -r b84c5f2fe83b tools/python/xen/lowlevel/xc/xc.c
--- a/tools/python/xen/lowlevel/xc/xc.c Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/python/xen/lowlevel/xc/xc.c Thu Jul 03 13:17:11 2008 +0200
@@ -857,16 +857,17 @@
 #endif
     char *image;
     int memsize, vcpus = 1, acpi = 0, apic = 1;
+    unsigned long nodemask;
 
     static char *kwd_list[] = { "domid",
                                 "memsize", "image", "vcpus", "acpi",
-                                "apic", NULL };
-    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|iii", kwd_list,
-                                      &dom, &memsize,
-                                      &image, &vcpus, &acpi, &apic) )
+                                "apic", "nodemask", NULL };
+    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|iiil", kwd_list,
+                                      &dom, &memsize, &image,
+                                      &vcpus, &acpi, &apic, &nodemask) )
         return NULL;
 
-    if ( xc_hvm_build(self->xc_handle, dom, memsize, image) != 0 )
+    if ( xc_hvm_build(self->xc_handle, dom, memsize, nodemask, image) != 0 )
         return pyxc_error_to_exception();
 
 #if !defined(__ia64__)
diff -r a0dccef499b0 -r b84c5f2fe83b tools/python/xen/xend/XendConfig.py
--- a/tools/python/xen/xend/XendConfig.py       Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/python/xen/xend/XendConfig.py       Thu Jul 03 13:17:11 2008 +0200
@@ -162,6 +162,7 @@
     'vhpt': int,
     'guest_os_type': str,
     'hap': int,
+    'guestnodes': int,
 }
 
 # Xen API console 'other_config' keys.
@@ -374,6 +375,7 @@
             'other_config': {},
             'platform': {},
             'target': 0,
+            'guestnodes': 0,
         }
         
         return defaults
@@ -569,7 +571,10 @@
             cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
         if sxp.child_value(sxp_cfg, "maxmem") != None:
             cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
-            
+
+        if sxp.child_value(sxp_cfg, "guestnodes") != None:
+            cfg["guestnodes"] = int(sxp.child_value(sxp_cfg, "guestnodes"))
+
         # Convert scheduling parameters to vcpus_params
         if 'vcpus_params' not in cfg:
             cfg['vcpus_params'] = {}
diff -r a0dccef499b0 -r b84c5f2fe83b tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py   Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/python/xen/xend/XendDomainInfo.py   Thu Jul 03 13:17:11 2008 +0200
@@ -2053,7 +2053,7 @@
                     if self.info['cpus'][v]:
                         xc.vcpu_setaffinity(self.domid, v, 
self.info['cpus'][v])
             else:
-                def find_relaxed_node(node_list):
+                def find_relaxed_node(node_list, numnodes):
                     import sys
                     nr_nodes = info['nr_nodes']
                     if node_list is None:
@@ -2078,21 +2078,36 @@
                             nodeload[i] = int(nodeload[i] * 16 / 
len(info['node_to_cpu'][i]))
                         else:
                             nodeload[i] = sys.maxint
-                    index = nodeload.index( min(nodeload) )    
-                    return index
+
+                    if numnodes == 0:
+                       return nodeload.index( min(nodeload) )
+                    else:
+                        nodemask = 0
+                        for i in range (0,numnodes):
+                            index = min((n, i) for i, n in 
enumerate(nodeload))[1]
+                            nodemask = nodemask | (1 << index)
+                            nodeload[index] = sys.maxint
+                        return nodemask
 
                 info = xc.physinfo()
+                nodemask = 0
                 if info['nr_nodes'] > 1:
                     node_memory_list = info['node_to_memory']
                     needmem = 
self.image.getRequiredAvailableMemory(self.info['memory_dynamic_max']) / 1024
+                    if self.image.guestnodes > 1:
+                       needmem = needmem / self.image.guestnodes
                     candidate_node_list = []
                     for i in range(0, info['nr_nodes']):
                         if node_memory_list[i] >= needmem and 
len(info['node_to_cpu'][i]) > 0:
                             candidate_node_list.append(i)
-                    index = find_relaxed_node(candidate_node_list)
-                    cpumask = info['node_to_cpu'][index]
-                    for v in range(0, self.info['VCPUs_max']):
-                        xc.vcpu_setaffinity(self.domid, v, cpumask)
+                    nodemask = find_relaxed_node(candidate_node_list, 
+                                                 self.image.guestnodes)
+                    if self.image.guestnodes < 1:
+                        cpumask = info['node_to_cpu'][nodemask]
+                        for v in range(0, self.info['VCPUs_max']):
+                            xc.vcpu_setaffinity(self.domid, v, cpumask)
+                    else:
+                        self.image.nodemask = nodemask
 
             # Use architecture- and image-specific calculations to determine
             # the various headrooms necessary, given the raw configured
diff -r a0dccef499b0 -r b84c5f2fe83b tools/python/xen/xend/image.py
--- a/tools/python/xen/xend/image.py    Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/python/xen/xend/image.py    Thu Jul 03 13:17:11 2008 +0200
@@ -127,6 +127,9 @@
             self.cpuid = vmConfig['cpuid'];
         if 'cpuid_check' in vmConfig:
             self.cpuid_check = vmConfig['cpuid_check']
+
+        self.guestnodes = int(vmConfig['platform'].get('guestnodes',0))
+        self.nodemask   = 0
 
     def cleanupBootloading(self):
         if self.bootloader:
@@ -696,6 +699,7 @@
         self.apic = int(vmConfig['platform'].get('apic', 0))
         self.acpi = int(vmConfig['platform'].get('acpi', 0))
         self.guest_os_type = vmConfig['platform'].get('guest_os_type')
+        self.guestnodes = int(vmConfig['platform'].get('guestnodes', 0))
            
 
     # Return a list of cmd line args to the device models based on the
@@ -797,13 +801,16 @@
         log.debug("vcpus          = %d", self.vm.getVCpuCount())
         log.debug("acpi           = %d", self.acpi)
         log.debug("apic           = %d", self.apic)
+        log.debug("guestnodes     = %d", self.guestnodes)
+        log.debug("nodemask       = %d", self.nodemask)
 
         rc = xc.hvm_build(domid          = self.vm.getDomid(),
                           image          = self.loader,
                           memsize        = mem_mb,
                           vcpus          = self.vm.getVCpuCount(),
                           acpi           = self.acpi,
-                          apic           = self.apic)
+                          apic           = self.apic,
+                          nodemask       = self.nodemask)
         rc['notes'] = { 'SUSPEND_CANCEL': 1 }
 
         rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
diff -r a0dccef499b0 -r b84c5f2fe83b tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py     Thu Jul 03 13:04:01 2008 +0200
+++ b/tools/python/xen/xm/create.py     Thu Jul 03 13:17:11 2008 +0200
@@ -557,6 +557,10 @@
           fn=append_value, default=[],
           use="""Cpuid check description.""")
 
+gopts.var('guestnodes', val="GUESTNODES",
+          fn=set_int, default=0,
+          use="""Number of NUMA nodes to appear in the guest.""")
+
 def err(msg):
     """Print an error to stderr and exit.
     """
@@ -765,7 +769,8 @@
              'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
              'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
              'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci', 'hpet',
-             'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check']
+             'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check',
+             'guestnodes' ]
 
     for a in args:
         if a in vals.__dict__ and vals.__dict__[a] is not None:
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.