eal/bsd: concatenate adjacent memory segments
[dpdk.git] / lib / librte_eal / bsdapp / eal / eal_memory.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 #include <sys/mman.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8 #include <inttypes.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <fcntl.h>
12
13 #include <rte_eal.h>
14 #include <rte_eal_memconfig.h>
15 #include <rte_log.h>
16 #include <rte_string_fns.h>
17 #include "eal_private.h"
18 #include "eal_internal_cfg.h"
19 #include "eal_filesystem.h"
20
21 #define EAL_PAGE_SIZE (sysconf(_SC_PAGESIZE))
22
23 /*
24  * Get physical address of any mapped virtual address in the current process.
25  */
26 phys_addr_t
27 rte_mem_virt2phy(const void *virtaddr)
28 {
29         /* XXX not implemented. This function is only used by
30          * rte_mempool_virt2iova() when hugepages are disabled. */
31         (void)virtaddr;
32         return RTE_BAD_IOVA;
33 }
34 rte_iova_t
35 rte_mem_virt2iova(const void *virtaddr)
36 {
37         return rte_mem_virt2phy(virtaddr);
38 }
39
40 int
41 rte_eal_hugepage_init(void)
42 {
43         struct rte_mem_config *mcfg;
44         uint64_t total_mem = 0;
45         void *addr;
46         unsigned int i, j, seg_idx = 0;
47
48         /* get pointer to global configuration */
49         mcfg = rte_eal_get_configuration()->mem_config;
50
51         /* for debug purposes, hugetlbfs can be disabled */
52         if (internal_config.no_hugetlbfs) {
53                 struct rte_memseg_list *msl;
54                 struct rte_fbarray *arr;
55                 struct rte_memseg *ms;
56                 uint64_t page_sz;
57                 int n_segs, cur_seg;
58
59                 /* create a memseg list */
60                 msl = &mcfg->memsegs[0];
61
62                 page_sz = RTE_PGSIZE_4K;
63                 n_segs = internal_config.memory / page_sz;
64
65                 if (rte_fbarray_init(&msl->memseg_arr, "nohugemem", n_segs,
66                                 sizeof(struct rte_memseg))) {
67                         RTE_LOG(ERR, EAL, "Cannot allocate memseg list\n");
68                         return -1;
69                 }
70
71                 addr = mmap(NULL, internal_config.memory,
72                                 PROT_READ | PROT_WRITE,
73                                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
74                 if (addr == MAP_FAILED) {
75                         RTE_LOG(ERR, EAL, "%s: mmap() failed: %s\n", __func__,
76                                         strerror(errno));
77                         return -1;
78                 }
79                 msl->base_va = addr;
80                 msl->page_sz = page_sz;
81                 msl->socket_id = 0;
82
83                 /* populate memsegs. each memseg is 1 page long */
84                 for (cur_seg = 0; cur_seg < n_segs; cur_seg++) {
85                         arr = &msl->memseg_arr;
86
87                         ms = rte_fbarray_get(arr, cur_seg);
88                         if (rte_eal_iova_mode() == RTE_IOVA_VA)
89                                 ms->iova = (uintptr_t)addr;
90                         else
91                                 ms->iova = RTE_BAD_IOVA;
92                         ms->addr = addr;
93                         ms->hugepage_sz = page_sz;
94                         ms->len = page_sz;
95                         ms->socket_id = 0;
96
97                         rte_fbarray_set_used(arr, cur_seg);
98
99                         addr = RTE_PTR_ADD(addr, page_sz);
100                 }
101                 return 0;
102         }
103
104         /* map all hugepages and sort them */
105         for (i = 0; i < internal_config.num_hugepage_sizes; i ++){
106                 struct hugepage_info *hpi;
107                 rte_iova_t prev_end = 0;
108                 int prev_ms_idx = -1;
109                 uint64_t page_sz, mem_needed;
110                 unsigned int n_pages, max_pages;
111
112                 hpi = &internal_config.hugepage_info[i];
113                 page_sz = hpi->hugepage_sz;
114                 max_pages = hpi->num_pages[0];
115                 mem_needed = RTE_ALIGN_CEIL(internal_config.memory - total_mem,
116                                 page_sz);
117
118                 n_pages = RTE_MIN(mem_needed / page_sz, max_pages);
119
120                 for (j = 0; j < n_pages; j++) {
121                         struct rte_memseg_list *msl;
122                         struct rte_fbarray *arr;
123                         struct rte_memseg *seg;
124                         int msl_idx, ms_idx;
125                         rte_iova_t physaddr;
126                         int error;
127                         size_t sysctl_size = sizeof(physaddr);
128                         char physaddr_str[64];
129                         bool is_adjacent;
130
131                         /* first, check if this segment is IOVA-adjacent to
132                          * the previous one.
133                          */
134                         snprintf(physaddr_str, sizeof(physaddr_str),
135                                         "hw.contigmem.physaddr.%d", j);
136                         error = sysctlbyname(physaddr_str, &physaddr,
137                                         &sysctl_size, NULL, 0);
138                         if (error < 0) {
139                                 RTE_LOG(ERR, EAL, "Failed to get physical addr for buffer %u "
140                                                 "from %s\n", j, hpi->hugedir);
141                                 return -1;
142                         }
143
144                         is_adjacent = prev_end != 0 && physaddr == prev_end;
145                         prev_end = physaddr + hpi->hugepage_sz;
146
147                         for (msl_idx = 0; msl_idx < RTE_MAX_MEMSEG_LISTS;
148                                         msl_idx++) {
149                                 bool empty, need_hole;
150                                 msl = &mcfg->memsegs[msl_idx];
151                                 arr = &msl->memseg_arr;
152
153                                 if (msl->page_sz != page_sz)
154                                         continue;
155
156                                 empty = arr->count == 0;
157
158                                 /* we need a hole if this isn't an empty memseg
159                                  * list, and if previous segment was not
160                                  * adjacent to current one.
161                                  */
162                                 need_hole = !empty && !is_adjacent;
163
164                                 /* we need 1, plus hole if not adjacent */
165                                 ms_idx = rte_fbarray_find_next_n_free(arr,
166                                                 0, 1 + (need_hole ? 1 : 0));
167
168                                 /* memseg list is full? */
169                                 if (ms_idx < 0)
170                                         continue;
171
172                                 if (need_hole && prev_ms_idx == ms_idx - 1)
173                                         ms_idx++;
174                                 prev_ms_idx = ms_idx;
175
176                                 break;
177                         }
178                         if (msl_idx == RTE_MAX_MEMSEG_LISTS) {
179                                 RTE_LOG(ERR, EAL, "Could not find space for memseg. Please increase %s and/or %s in configuration.\n",
180                                         RTE_STR(CONFIG_RTE_MAX_MEMSEG_PER_TYPE),
181                                         RTE_STR(CONFIG_RTE_MAX_MEM_PER_TYPE));
182                                 return -1;
183                         }
184                         arr = &msl->memseg_arr;
185                         seg = rte_fbarray_get(arr, ms_idx);
186
187                         addr = RTE_PTR_ADD(msl->base_va,
188                                         (size_t)msl->page_sz * ms_idx);
189
190                         /* address is already mapped in memseg list, so using
191                          * MAP_FIXED here is safe.
192                          */
193                         addr = mmap(addr, page_sz, PROT_READ|PROT_WRITE,
194                                         MAP_SHARED | MAP_FIXED,
195                                         hpi->lock_descriptor,
196                                         j * EAL_PAGE_SIZE);
197                         if (addr == MAP_FAILED) {
198                                 RTE_LOG(ERR, EAL, "Failed to mmap buffer %u from %s\n",
199                                                 j, hpi->hugedir);
200                                 return -1;
201                         }
202
203                         seg->addr = addr;
204                         seg->iova = physaddr;
205                         seg->hugepage_sz = page_sz;
206                         seg->len = page_sz;
207                         seg->nchannel = mcfg->nchannel;
208                         seg->nrank = mcfg->nrank;
209                         seg->socket_id = 0;
210
211                         rte_fbarray_set_used(arr, ms_idx);
212
213                         RTE_LOG(INFO, EAL, "Mapped memory segment %u @ %p: physaddr:0x%"
214                                         PRIx64", len %zu\n",
215                                         seg_idx++, addr, physaddr, page_sz);
216
217                         total_mem += seg->len;
218                 }
219                 if (total_mem >= internal_config.memory)
220                         break;
221         }
222         if (total_mem < internal_config.memory) {
223                 RTE_LOG(ERR, EAL, "Couldn't reserve requested memory, "
224                                 "requested: %" PRIu64 "M "
225                                 "available: %" PRIu64 "M\n",
226                                 internal_config.memory >> 20, total_mem >> 20);
227                 return -1;
228         }
229         return 0;
230 }
231
232 struct attach_walk_args {
233         int fd_hugepage;
234         int seg_idx;
235 };
236 static int
237 attach_segment(const struct rte_memseg_list *msl __rte_unused,
238                 const struct rte_memseg *ms, void *arg)
239 {
240         struct attach_walk_args *wa = arg;
241         void *addr;
242
243         addr = mmap(ms->addr, ms->len, PROT_READ | PROT_WRITE,
244                         MAP_SHARED | MAP_FIXED, wa->fd_hugepage,
245                         wa->seg_idx * EAL_PAGE_SIZE);
246         if (addr == MAP_FAILED || addr != ms->addr)
247                 return -1;
248         wa->seg_idx++;
249
250         return 0;
251 }
252
253 int
254 rte_eal_hugepage_attach(void)
255 {
256         const struct hugepage_info *hpi;
257         int fd_hugepage = -1;
258         unsigned int i;
259
260         hpi = &internal_config.hugepage_info[0];
261
262         for (i = 0; i < internal_config.num_hugepage_sizes; i++) {
263                 const struct hugepage_info *cur_hpi = &hpi[i];
264                 struct attach_walk_args wa;
265
266                 memset(&wa, 0, sizeof(wa));
267
268                 /* Obtain a file descriptor for contiguous memory */
269                 fd_hugepage = open(cur_hpi->hugedir, O_RDWR);
270                 if (fd_hugepage < 0) {
271                         RTE_LOG(ERR, EAL, "Could not open %s\n",
272                                         cur_hpi->hugedir);
273                         goto error;
274                 }
275                 wa.fd_hugepage = fd_hugepage;
276                 wa.seg_idx = 0;
277
278                 /* Map the contiguous memory into each memory segment */
279                 if (rte_memseg_walk(attach_segment, &wa) < 0) {
280                         RTE_LOG(ERR, EAL, "Failed to mmap buffer %u from %s\n",
281                                 wa.seg_idx, cur_hpi->hugedir);
282                         goto error;
283                 }
284
285                 close(fd_hugepage);
286                 fd_hugepage = -1;
287         }
288
289         /* hugepage_info is no longer required */
290         return 0;
291
292 error:
293         if (fd_hugepage >= 0)
294                 close(fd_hugepage);
295         return -1;
296 }
297
298 int
299 rte_eal_using_phys_addrs(void)
300 {
301         return 0;
302 }