mem: move virtual area function in common directory
[dpdk.git] / lib / librte_eal / common / eal_common_memory.c
index 852f3bb..5b8ced4 100644 (file)
@@ -2,10 +2,12 @@
  * Copyright(c) 2010-2014 Intel Corporation
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 #include <unistd.h>
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <rte_memory.h>
 #include <rte_eal.h>
 #include <rte_eal_memconfig.h>
+#include <rte_errno.h>
 #include <rte_log.h>
 
 #include "eal_private.h"
 #include "eal_internal_cfg.h"
 
+/*
+ * Try to mmap *size bytes in /dev/zero. If it is successful, return the
+ * pointer to the mmap'd area and keep *size unmodified. Else, retry
+ * with a smaller zone: decrease *size by hugepage_sz until it reaches
+ * 0. In this case, return NULL. Note: this function returns an address
+ * which is a multiple of hugepage size.
+ */
+
+static uint64_t baseaddr_offset;
+static uint64_t system_page_sz;
+
+void *
+eal_get_virtual_area(void *requested_addr, size_t *size,
+               size_t page_sz, int flags, int mmap_flags)
+{
+       bool addr_is_hint, allow_shrink, unmap, no_align;
+       uint64_t map_sz;
+       void *mapped_addr, *aligned_addr;
+
+       if (system_page_sz == 0)
+               system_page_sz = sysconf(_SC_PAGESIZE);
+
+       mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
+
+       RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zx bytes\n", *size);
+
+       addr_is_hint = (flags & EAL_VIRTUAL_AREA_ADDR_IS_HINT) > 0;
+       allow_shrink = (flags & EAL_VIRTUAL_AREA_ALLOW_SHRINK) > 0;
+       unmap = (flags & EAL_VIRTUAL_AREA_UNMAP) > 0;
+
+       if (requested_addr == NULL && internal_config.base_virtaddr != 0) {
+               requested_addr = (void *) (internal_config.base_virtaddr +
+                               (size_t)baseaddr_offset);
+               requested_addr = RTE_PTR_ALIGN(requested_addr, page_sz);
+               addr_is_hint = true;
+       }
+
+       /* if requested address is not aligned by page size, or if requested
+        * address is NULL, add page size to requested length as we may get an
+        * address that's aligned by system page size, which can be smaller than
+        * our requested page size. additionally, we shouldn't try to align if
+        * system page size is the same as requested page size.
+        */
+       no_align = (requested_addr != NULL &&
+               ((uintptr_t)requested_addr & (page_sz - 1)) == 0) ||
+               page_sz == system_page_sz;
+
+       do {
+               map_sz = no_align ? *size : *size + page_sz;
+
+               mapped_addr = mmap(requested_addr, map_sz, PROT_READ,
+                               mmap_flags, -1, 0);
+               if (mapped_addr == MAP_FAILED && allow_shrink)
+                       *size -= page_sz;
+       } while (allow_shrink && mapped_addr == MAP_FAILED && *size > 0);
+
+       /* align resulting address - if map failed, we will ignore the value
+        * anyway, so no need to add additional checks.
+        */
+       aligned_addr = no_align ? mapped_addr :
+                       RTE_PTR_ALIGN(mapped_addr, page_sz);
+
+       if (*size == 0) {
+               RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
+                       strerror(errno));
+               rte_errno = errno;
+               return NULL;
+       } else if (mapped_addr == MAP_FAILED) {
+               RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
+                       strerror(errno));
+               /* pass errno up the call chain */
+               rte_errno = errno;
+               return NULL;
+       } else if (requested_addr != NULL && !addr_is_hint &&
+                       aligned_addr != requested_addr) {
+               RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
+                       requested_addr, aligned_addr);
+               munmap(mapped_addr, map_sz);
+               rte_errno = EADDRNOTAVAIL;
+               return NULL;
+       } else if (requested_addr != NULL && addr_is_hint &&
+                       aligned_addr != requested_addr) {
+               RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
+                       requested_addr, aligned_addr);
+               RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
+       }
+
+       if (unmap)
+               munmap(mapped_addr, map_sz);
+
+       RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
+               aligned_addr, *size);
+
+       baseaddr_offset += *size;
+
+       return aligned_addr;
+}
+
 /*
  * Return a pointer to a read-only table of struct rte_physmem_desc
  * elements, containing the layout of all addressable physical