mem: add contig walk function
[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 static int
135 physmem_size(const struct rte_memseg *ms, void *arg)
136 {
137         uint64_t *total_len = arg;
138
139         *total_len += ms->len;
140
141         return 0;
142 }
143
144 /* get the total size of memory */
145 uint64_t
146 rte_eal_get_physmem_size(void)
147 {
148         uint64_t total_len = 0;
149
150         rte_memseg_walk(physmem_size, &total_len);
151
152         return total_len;
153 }
154
155 static int
156 dump_memseg(const struct rte_memseg *ms, void *arg)
157 {
158         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
159         int i = ms - mcfg->memseg;
160         FILE *f = arg;
161
162         if (i < 0 || i >= RTE_MAX_MEMSEG)
163                 return -1;
164
165         fprintf(f, "Segment %u: IOVA:0x%"PRIx64", len:%zu, "
166                         "virt:%p, socket_id:%"PRId32", "
167                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
168                         "nrank:%"PRIx32"\n", i,
169                         mcfg->memseg[i].iova,
170                         mcfg->memseg[i].len,
171                         mcfg->memseg[i].addr,
172                         mcfg->memseg[i].socket_id,
173                         mcfg->memseg[i].hugepage_sz,
174                         mcfg->memseg[i].nchannel,
175                         mcfg->memseg[i].nrank);
176
177         return 0;
178 }
179
180 /* Dump the physical memory layout on console */
181 void
182 rte_dump_physmem_layout(FILE *f)
183 {
184         rte_memseg_walk(dump_memseg, f);
185 }
186
187 /* return the number of memory channels */
188 unsigned rte_memory_get_nchannel(void)
189 {
190         return rte_eal_get_configuration()->mem_config->nchannel;
191 }
192
193 /* return the number of memory rank */
194 unsigned rte_memory_get_nrank(void)
195 {
196         return rte_eal_get_configuration()->mem_config->nrank;
197 }
198
199 static int
200 rte_eal_memdevice_init(void)
201 {
202         struct rte_config *config;
203
204         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
205                 return 0;
206
207         config = rte_eal_get_configuration();
208         config->mem_config->nchannel = internal_config.force_nchannel;
209         config->mem_config->nrank = internal_config.force_nrank;
210
211         return 0;
212 }
213
214 /* Lock page in physical memory and prevent from swapping. */
215 int
216 rte_mem_lock_page(const void *virt)
217 {
218         unsigned long virtual = (unsigned long)virt;
219         int page_size = getpagesize();
220         unsigned long aligned = (virtual & ~(page_size - 1));
221         return mlock((void *)aligned, page_size);
222 }
223
224 int __rte_experimental
225 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
226 {
227         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
228         int i, ret;
229
230         for (i = 0; i < RTE_MAX_MEMSEG; i++) {
231                 const struct rte_memseg *ms = &mcfg->memseg[i];
232
233                 if (ms->addr == NULL)
234                         continue;
235
236                 ret = func(ms, arg);
237                 if (ret < 0)
238                         return -1;
239                 if (ret > 0)
240                         return 1;
241         }
242         return 0;
243 }
244
245 int __rte_experimental
246 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
247 {
248         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
249         int i, j, ret;
250
251         for (i = 0; i < RTE_MAX_MEMSEG; i++) {
252                 const struct rte_memseg *ms = &mcfg->memseg[i];
253                 size_t total_len;
254                 void *end_addr;
255
256                 if (ms->addr == NULL)
257                         continue;
258
259                 end_addr = RTE_PTR_ADD(ms->addr, ms->len);
260
261                 /* check how many more segments are contiguous to this one */
262                 for (j = i + 1; j < RTE_MAX_MEMSEG; j++) {
263                         const struct rte_memseg *next = &mcfg->memseg[j];
264
265                         if (next->addr != end_addr)
266                                 break;
267
268                         end_addr = RTE_PTR_ADD(next->addr, next->len);
269                         i++;
270                 }
271                 total_len = RTE_PTR_DIFF(end_addr, ms->addr);
272
273                 ret = func(ms, total_len, arg);
274                 if (ret < 0)
275                         return -1;
276                 if (ret > 0)
277                         return 1;
278         }
279         return 0;
280 }
281
282 /* init memory subsystem */
283 int
284 rte_eal_memory_init(void)
285 {
286         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
287
288         const int retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
289                         rte_eal_hugepage_init() :
290                         rte_eal_hugepage_attach();
291         if (retval < 0)
292                 return -1;
293
294         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
295                 return -1;
296
297         return 0;
298 }