mem: move virtual area function in common directory
[dpdk.git] / lib / librte_eal / common / eal_common_memory.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <inttypes.h>
13 #include <sys/mman.h>
14 #include <sys/queue.h>
15
16 #include <rte_memory.h>
17 #include <rte_eal.h>
18 #include <rte_eal_memconfig.h>
19 #include <rte_errno.h>
20 #include <rte_log.h>
21
22 #include "eal_private.h"
23 #include "eal_internal_cfg.h"
24
25 /*
26  * Try to mmap *size bytes in /dev/zero. If it is successful, return the
27  * pointer to the mmap'd area and keep *size unmodified. Else, retry
28  * with a smaller zone: decrease *size by hugepage_sz until it reaches
29  * 0. In this case, return NULL. Note: this function returns an address
30  * which is a multiple of hugepage size.
31  */
32
33 static uint64_t baseaddr_offset;
34 static uint64_t system_page_sz;
35
36 void *
37 eal_get_virtual_area(void *requested_addr, size_t *size,
38                 size_t page_sz, int flags, int mmap_flags)
39 {
40         bool addr_is_hint, allow_shrink, unmap, no_align;
41         uint64_t map_sz;
42         void *mapped_addr, *aligned_addr;
43
44         if (system_page_sz == 0)
45                 system_page_sz = sysconf(_SC_PAGESIZE);
46
47         mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
48
49         RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zx bytes\n", *size);
50
51         addr_is_hint = (flags & EAL_VIRTUAL_AREA_ADDR_IS_HINT) > 0;
52         allow_shrink = (flags & EAL_VIRTUAL_AREA_ALLOW_SHRINK) > 0;
53         unmap = (flags & EAL_VIRTUAL_AREA_UNMAP) > 0;
54
55         if (requested_addr == NULL && internal_config.base_virtaddr != 0) {
56                 requested_addr = (void *) (internal_config.base_virtaddr +
57                                 (size_t)baseaddr_offset);
58                 requested_addr = RTE_PTR_ALIGN(requested_addr, page_sz);
59                 addr_is_hint = true;
60         }
61
62         /* if requested address is not aligned by page size, or if requested
63          * address is NULL, add page size to requested length as we may get an
64          * address that's aligned by system page size, which can be smaller than
65          * our requested page size. additionally, we shouldn't try to align if
66          * system page size is the same as requested page size.
67          */
68         no_align = (requested_addr != NULL &&
69                 ((uintptr_t)requested_addr & (page_sz - 1)) == 0) ||
70                 page_sz == system_page_sz;
71
72         do {
73                 map_sz = no_align ? *size : *size + page_sz;
74
75                 mapped_addr = mmap(requested_addr, map_sz, PROT_READ,
76                                 mmap_flags, -1, 0);
77                 if (mapped_addr == MAP_FAILED && allow_shrink)
78                         *size -= page_sz;
79         } while (allow_shrink && mapped_addr == MAP_FAILED && *size > 0);
80
81         /* align resulting address - if map failed, we will ignore the value
82          * anyway, so no need to add additional checks.
83          */
84         aligned_addr = no_align ? mapped_addr :
85                         RTE_PTR_ALIGN(mapped_addr, page_sz);
86
87         if (*size == 0) {
88                 RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
89                         strerror(errno));
90                 rte_errno = errno;
91                 return NULL;
92         } else if (mapped_addr == MAP_FAILED) {
93                 RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
94                         strerror(errno));
95                 /* pass errno up the call chain */
96                 rte_errno = errno;
97                 return NULL;
98         } else if (requested_addr != NULL && !addr_is_hint &&
99                         aligned_addr != requested_addr) {
100                 RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
101                         requested_addr, aligned_addr);
102                 munmap(mapped_addr, map_sz);
103                 rte_errno = EADDRNOTAVAIL;
104                 return NULL;
105         } else if (requested_addr != NULL && addr_is_hint &&
106                         aligned_addr != requested_addr) {
107                 RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
108                         requested_addr, aligned_addr);
109                 RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
110         }
111
112         if (unmap)
113                 munmap(mapped_addr, map_sz);
114
115         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
116                 aligned_addr, *size);
117
118         baseaddr_offset += *size;
119
120         return aligned_addr;
121 }
122
123 /*
124  * Return a pointer to a read-only table of struct rte_physmem_desc
125  * elements, containing the layout of all addressable physical
126  * memory. The last element of the table contains a NULL address.
127  */
128 const struct rte_memseg *
129 rte_eal_get_physmem_layout(void)
130 {
131         return rte_eal_get_configuration()->mem_config->memseg;
132 }
133
134
135 /* get the total size of memory */
136 uint64_t
137 rte_eal_get_physmem_size(void)
138 {
139         const struct rte_mem_config *mcfg;
140         unsigned i = 0;
141         uint64_t total_len = 0;
142
143         /* get pointer to global configuration */
144         mcfg = rte_eal_get_configuration()->mem_config;
145
146         for (i = 0; i < RTE_MAX_MEMSEG; i++) {
147                 if (mcfg->memseg[i].addr == NULL)
148                         break;
149
150                 total_len += mcfg->memseg[i].len;
151         }
152
153         return total_len;
154 }
155
156 /* Dump the physical memory layout on console */
157 void
158 rte_dump_physmem_layout(FILE *f)
159 {
160         const struct rte_mem_config *mcfg;
161         unsigned i = 0;
162
163         /* get pointer to global configuration */
164         mcfg = rte_eal_get_configuration()->mem_config;
165
166         for (i = 0; i < RTE_MAX_MEMSEG; i++) {
167                 if (mcfg->memseg[i].addr == NULL)
168                         break;
169
170                 fprintf(f, "Segment %u: IOVA:0x%"PRIx64", len:%zu, "
171                        "virt:%p, socket_id:%"PRId32", "
172                        "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
173                        "nrank:%"PRIx32"\n", i,
174                        mcfg->memseg[i].iova,
175                        mcfg->memseg[i].len,
176                        mcfg->memseg[i].addr,
177                        mcfg->memseg[i].socket_id,
178                        mcfg->memseg[i].hugepage_sz,
179                        mcfg->memseg[i].nchannel,
180                        mcfg->memseg[i].nrank);
181         }
182 }
183
184 /* return the number of memory channels */
185 unsigned rte_memory_get_nchannel(void)
186 {
187         return rte_eal_get_configuration()->mem_config->nchannel;
188 }
189
190 /* return the number of memory rank */
191 unsigned rte_memory_get_nrank(void)
192 {
193         return rte_eal_get_configuration()->mem_config->nrank;
194 }
195
196 static int
197 rte_eal_memdevice_init(void)
198 {
199         struct rte_config *config;
200
201         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
202                 return 0;
203
204         config = rte_eal_get_configuration();
205         config->mem_config->nchannel = internal_config.force_nchannel;
206         config->mem_config->nrank = internal_config.force_nrank;
207
208         return 0;
209 }
210
211 /* Lock page in physical memory and prevent from swapping. */
212 int
213 rte_mem_lock_page(const void *virt)
214 {
215         unsigned long virtual = (unsigned long)virt;
216         int page_size = getpagesize();
217         unsigned long aligned = (virtual & ~(page_size - 1));
218         return mlock((void *)aligned, page_size);
219 }
220
221 /* init memory subsystem */
222 int
223 rte_eal_memory_init(void)
224 {
225         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
226
227         const int retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
228                         rte_eal_hugepage_init() :
229                         rte_eal_hugepage_attach();
230         if (retval < 0)
231                 return -1;
232
233         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
234                 return -1;
235
236         return 0;
237 }