]> git.droids-corp.org - dpdk.git/commitdiff
mem: balanced allocation of hugepages
authorIlya Maximets <i.maximets@samsung.com>
Thu, 29 Jun 2017 05:59:19 +0000 (08:59 +0300)
committerThomas Monjalon <thomas@monjalon.net>
Fri, 30 Jun 2017 15:54:32 +0000 (17:54 +0200)
Currently EAL allocates hugepages one by one not paying attention
from which NUMA node allocation was done.

Such behaviour leads to allocation failure if number of available
hugepages for application limited by cgroups or hugetlbfs and
memory requested not only from the first socket.

Example:
# 90 x 1GB hugepages availavle in a system

cgcreate -g hugetlb:/test
# Limit to 32GB of hugepages
cgset -r hugetlb.1GB.limit_in_bytes=34359738368 test
# Request 4GB from each of 2 sockets
cgexec -g hugetlb:test testpmd --socket-mem=4096,4096 ...

EAL: SIGBUS: Cannot mmap more hugepages of size 1024 MB
EAL: 32 not 90 hugepages of size 1024 MB allocated
EAL: Not enough memory available on socket 1!
     Requested: 4096MB, available: 0MB
PANIC in rte_eal_init():
Cannot init memory

This happens beacause all allocated pages are
on socket 0.

Fix this issue by setting mempolicy MPOL_PREFERRED for each hugepage
to one of requested nodes using following schema:

1) Allocate essential hugepages:
1.1) Allocate as many hugepages from numa N to
     only fit requested memory for this numa.
1.2) repeat 1.1 for all numa nodes.
2) Try to map all remaining free hugepages in a round-robin
   fashion.
3) Sort pages and choose the most suitable.

In this case all essential memory will be allocated and all remaining
pages will be fairly distributed between all requested nodes.

New config option RTE_EAL_NUMA_AWARE_HUGEPAGES introduced and
enabled by default for linuxapp except armv7 and dpaa2.
Enabling of this option adds libnuma as a dependency for EAL.

Fixes: 77988fc08dc5 ("mem: fix allocating all free hugepages")
Signed-off-by: Ilya Maximets <i.maximets@samsung.com>
Acked-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
Acked-by: Hemant Agrawal <hemant.agrawal@nxp.com>
Acked-by: Jerin Jacob <jerin.jacob@caviumnetworks.com>
Tested-by: Jerin Jacob <jerin.jacob@caviumnetworks.com>
config/common_base
config/common_linuxapp
config/defconfig_arm-armv7a-linuxapp-gcc
config/defconfig_arm64-dpaa2-linuxapp-gcc
devtools/test-build.sh
lib/librte_eal/linuxapp/eal/Makefile
lib/librte_eal/linuxapp/eal/eal_memory.c
mk/rte.app.mk

index f6aafd17d4eb9a725f03a40db62611ae81359cc0..660588a3df0673e79d1c71d28e48c77377eccd16 100644 (file)
@@ -103,6 +103,7 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
 CONFIG_RTE_EAL_IGB_UIO=n
 CONFIG_RTE_EAL_VFIO=n
 CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=n
 
 #
 # Recognize/ignore the AVX/AVX512 CPU flags for performance/power testing.
index b3cf41b0164c7cefdd7930577e299b3de1ab3550..64bef87af8e6771ddfa9f73959df875d3e67b23a 100644 (file)
@@ -35,6 +35,7 @@
 CONFIG_RTE_EXEC_ENV="linuxapp"
 CONFIG_RTE_EXEC_ENV_LINUXAPP=y
 
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
 CONFIG_RTE_EAL_IGB_UIO=y
 CONFIG_RTE_EAL_VFIO=y
 CONFIG_RTE_KNI_KMOD=y
index 19607eb67e6b1784fc16077696c6406942a5e8ab..e06b1d44138f433ed3a6ba97028b53ddc47bfc93 100644 (file)
@@ -47,6 +47,9 @@ CONFIG_RTE_ARCH_STRICT_ALIGN=y
 CONFIG_RTE_TOOLCHAIN="gcc"
 CONFIG_RTE_TOOLCHAIN_GCC=y
 
+# NUMA is not supported on ARM
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=n
+
 # ARM doesn't have support for vmware TSC map
 CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=n
 
index 2304ab607991c4d4e3ebdefc698a172474b75de6..d17201b1ef96eeb9862f02f3a23703b9b55718b2 100644 (file)
@@ -45,6 +45,9 @@ CONFIG_RTE_CACHE_LINE_SIZE=64
 
 CONFIG_RTE_PKTMBUF_HEADROOM=256
 
+# Doesn't support NUMA
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=n
+
 #
 # Compile Support Libraries for DPAA2
 #
index 61bdce7cc993c56de64e4fabc8dd3824d6ddd184..079c8b8f853c69314656e125b61f602d354e0b07 100755 (executable)
@@ -41,7 +41,7 @@ default_path=$PATH
 # - DPDK_DEP_ISAL_CRYPTO (y/[n])
 # - DPDK_DEP_LDFLAGS
 # - DPDK_DEP_MOFED (y/[n])
-# - DPDK_DEP_NUMA (y/[n])
+# - DPDK_DEP_NUMA ([y]/n)
 # - DPDK_DEP_PCAP (y/[n])
 # - DPDK_DEP_SSL (y/[n])
 # - DPDK_DEP_SZE (y/[n])
@@ -163,8 +163,8 @@ config () # <directory> <target> <options>
                sed -ri 's,(TEST_PMD_RECORD_.*=)n,\1y,' $1/.config )
 
                # Automatic configuration
-               test "$DPDK_DEP_NUMA" != y || \
-               sed -ri               's,(NUMA=)n,\1y,' $1/.config
+               test "$DPDK_DEP_NUMA" != n || \
+               sed -ri             's,(NUMA.*=)y,\1n,' $1/.config
                sed -ri    's,(LIBRTE_IEEE1588=)n,\1y,' $1/.config
                sed -ri             's,(BYPASS=)n,\1y,' $1/.config
                test "$DPDK_DEP_ARCHIVE" != y || \
index 640afd0887de24feb7c1e67a81fd31deee53d20a..8651e2783c27539bf6da6ac5fbd520fd335d983a 100644 (file)
@@ -50,6 +50,9 @@ LDLIBS += -ldl
 LDLIBS += -lpthread
 LDLIBS += -lgcc_s
 LDLIBS += -lrt
+ifeq ($(CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES),y)
+LDLIBS += -lnuma
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
index e17c9cb5daa4d26aecbc311a015fdb84fc9f4713..647d89c58d20586bd3fca7053fd386e79565effb 100644 (file)
 #include <sys/time.h>
 #include <signal.h>
 #include <setjmp.h>
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+#include <numa.h>
+#include <numaif.h>
+#endif
 
 #include <rte_log.h>
 #include <rte_memory.h>
@@ -348,6 +352,14 @@ static int huge_wrap_sigsetjmp(void)
        return sigsetjmp(huge_jmpenv, 1);
 }
 
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+/* Callback for numa library. */
+void numa_error(char *where)
+{
+       RTE_LOG(ERR, EAL, "%s failed: %s\n", where, strerror(errno));
+}
+#endif
+
 /*
  * Mmap all hugepages of hugepage table: it first open a file in
  * hugetlbfs, then mmap() hugepage_sz data in it. If orig is set, the
@@ -356,18 +368,78 @@ static int huge_wrap_sigsetjmp(void)
  * map continguous physical blocks in contiguous virtual blocks.
  */
 static unsigned
-map_all_hugepages(struct hugepage_file *hugepg_tbl,
-               struct hugepage_info *hpi, int orig)
+map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi,
+                 uint64_t *essential_memory __rte_unused, int orig)
 {
        int fd;
        unsigned i;
        void *virtaddr;
        void *vma_addr = NULL;
        size_t vma_len = 0;
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+       int node_id = -1;
+       int essential_prev = 0;
+       int oldpolicy;
+       struct bitmask *oldmask = numa_allocate_nodemask();
+       bool have_numa = true;
+       unsigned long maxnode = 0;
+
+       /* Check if kernel supports NUMA. */
+       if (numa_available() != 0) {
+               RTE_LOG(DEBUG, EAL, "NUMA is not supported.\n");
+               have_numa = false;
+       }
+
+       if (orig && have_numa) {
+               RTE_LOG(DEBUG, EAL, "Trying to obtain current memory policy.\n");
+               if (get_mempolicy(&oldpolicy, oldmask->maskp,
+                                 oldmask->size + 1, 0, 0) < 0) {
+                       RTE_LOG(ERR, EAL,
+                               "Failed to get current mempolicy: %s. "
+                               "Assuming MPOL_DEFAULT.\n", strerror(errno));
+                       oldpolicy = MPOL_DEFAULT;
+               }
+               for (i = 0; i < RTE_MAX_NUMA_NODES; i++)
+                       if (internal_config.socket_mem[i])
+                               maxnode = i + 1;
+       }
+#endif
 
        for (i = 0; i < hpi->num_pages[0]; i++) {
                uint64_t hugepage_sz = hpi->hugepage_sz;
 
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+               if (maxnode) {
+                       unsigned int j;
+
+                       for (j = 0; j < maxnode; j++)
+                               if (essential_memory[j])
+                                       break;
+
+                       if (j == maxnode) {
+                               node_id = (node_id + 1) % maxnode;
+                               while (!internal_config.socket_mem[node_id]) {
+                                       node_id++;
+                                       node_id %= maxnode;
+                               }
+                               essential_prev = 0;
+                       } else {
+                               node_id = j;
+                               essential_prev = essential_memory[j];
+
+                               if (essential_memory[j] < hugepage_sz)
+                                       essential_memory[j] = 0;
+                               else
+                                       essential_memory[j] -= hugepage_sz;
+                       }
+
+                       RTE_LOG(DEBUG, EAL,
+                               "Setting policy MPOL_PREFERRED for socket %d\n",
+                               node_id);
+                       numa_set_preferred(node_id);
+               }
+#endif
+
                if (orig) {
                        hugepg_tbl[i].file_id = i;
                        hugepg_tbl[i].size = hugepage_sz;
@@ -422,7 +494,7 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl,
                if (fd < 0) {
                        RTE_LOG(DEBUG, EAL, "%s(): open failed: %s\n", __func__,
                                        strerror(errno));
-                       return i;
+                       goto out;
                }
 
                /* map the segment, and populate page tables,
@@ -433,7 +505,7 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl,
                        RTE_LOG(DEBUG, EAL, "%s(): mmap failed: %s\n", __func__,
                                        strerror(errno));
                        close(fd);
-                       return i;
+                       goto out;
                }
 
                if (orig) {
@@ -458,7 +530,12 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl,
                                munmap(virtaddr, hugepage_sz);
                                close(fd);
                                unlink(hugepg_tbl[i].filepath);
-                               return i;
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+                               if (maxnode)
+                                       essential_memory[node_id] =
+                                               essential_prev;
+#endif
+                               goto out;
                        }
                        *(int *)virtaddr = 0;
                }
@@ -469,7 +546,7 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl,
                        RTE_LOG(DEBUG, EAL, "%s(): Locking file failed:%s \n",
                                __func__, strerror(errno));
                        close(fd);
-                       return i;
+                       goto out;
                }
 
                close(fd);
@@ -478,6 +555,22 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl,
                vma_len -= hugepage_sz;
        }
 
+out:
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+       if (maxnode) {
+               RTE_LOG(DEBUG, EAL,
+                       "Restoring previous memory policy: %d\n", oldpolicy);
+               if (oldpolicy == MPOL_DEFAULT) {
+                       numa_set_localalloc();
+               } else if (set_mempolicy(oldpolicy, oldmask->maskp,
+                                        oldmask->size + 1) < 0) {
+                       RTE_LOG(ERR, EAL, "Failed to restore mempolicy: %s\n",
+                               strerror(errno));
+                       numa_set_localalloc();
+               }
+       }
+       numa_free_cpumask(oldmask);
+#endif
        return i;
 }
 
@@ -562,6 +655,11 @@ find_numasocket(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi)
                        if (hugepg_tbl[i].orig_va == va) {
                                hugepg_tbl[i].socket_id = socket_id;
                                hp_count++;
+#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
+                               RTE_LOG(DEBUG, EAL,
+                                       "Hugepage %s is on socket %d\n",
+                                       hugepg_tbl[i].filepath, socket_id);
+#endif
                        }
                }
        }
@@ -1000,6 +1098,11 @@ rte_eal_hugepage_init(void)
 
        huge_register_sigbus();
 
+       /* make a copy of socket_mem, needed for balanced allocation. */
+       for (i = 0; i < RTE_MAX_NUMA_NODES; i++)
+               memory[i] = internal_config.socket_mem[i];
+
+
        /* map all hugepages and sort them */
        for (i = 0; i < (int)internal_config.num_hugepage_sizes; i ++){
                unsigned pages_old, pages_new;
@@ -1017,7 +1120,8 @@ rte_eal_hugepage_init(void)
 
                /* map all hugepages available */
                pages_old = hpi->num_pages[0];
-               pages_new = map_all_hugepages(&tmp_hp[hp_offset], hpi, 1);
+               pages_new = map_all_hugepages(&tmp_hp[hp_offset], hpi,
+                                             memory, 1);
                if (pages_new < pages_old) {
                        RTE_LOG(DEBUG, EAL,
                                "%d not %d hugepages of size %u MB allocated\n",
@@ -1060,7 +1164,7 @@ rte_eal_hugepage_init(void)
                      sizeof(struct hugepage_file), cmp_physaddr);
 
                /* remap all hugepages */
-               if (map_all_hugepages(&tmp_hp[hp_offset], hpi, 0) !=
+               if (map_all_hugepages(&tmp_hp[hp_offset], hpi, NULL, 0) !=
                    hpi->num_pages[0]) {
                        RTE_LOG(ERR, EAL, "Failed to remap %u MB pages\n",
                                        (unsigned)(hpi->hugepage_sz / 0x100000));
index bcaf1b38205dfc9211974ec505561b85f412ee4e..4fe22d1fe3a22d1692b0a07613c270123f98c807 100644 (file)
@@ -186,6 +186,9 @@ ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),n)
 # The static libraries do not know their dependencies.
 # So linking with static library requires explicit dependencies.
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrt
+ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP)$(CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES),yy)
+_LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lnuma
+endif
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lm
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrt
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lm