build: remove redundant config include
[dpdk.git] / lib / librte_eal / common / eal_common_memalloc.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017-2018 Intel Corporation
3  */
4
5 #include <string.h>
6
7 #include <rte_errno.h>
8 #include <rte_lcore.h>
9 #include <rte_fbarray.h>
10 #include <rte_memzone.h>
11 #include <rte_memory.h>
12 #include <rte_string_fns.h>
13 #include <rte_rwlock.h>
14
15 #include "eal_private.h"
16 #include "eal_internal_cfg.h"
17 #include "eal_memalloc.h"
18
19 struct mem_event_callback_entry {
20         TAILQ_ENTRY(mem_event_callback_entry) next;
21         char name[RTE_MEM_EVENT_CALLBACK_NAME_LEN];
22         rte_mem_event_callback_t clb;
23         void *arg;
24 };
25
26 struct mem_alloc_validator_entry {
27         TAILQ_ENTRY(mem_alloc_validator_entry) next;
28         char name[RTE_MEM_ALLOC_VALIDATOR_NAME_LEN];
29         rte_mem_alloc_validator_t clb;
30         int socket_id;
31         size_t limit;
32 };
33
34 /** Double linked list of actions. */
35 TAILQ_HEAD(mem_event_callback_entry_list, mem_event_callback_entry);
36 TAILQ_HEAD(mem_alloc_validator_entry_list, mem_alloc_validator_entry);
37
38 static struct mem_event_callback_entry_list mem_event_callback_list =
39         TAILQ_HEAD_INITIALIZER(mem_event_callback_list);
40 static rte_rwlock_t mem_event_rwlock = RTE_RWLOCK_INITIALIZER;
41
42 static struct mem_alloc_validator_entry_list mem_alloc_validator_list =
43         TAILQ_HEAD_INITIALIZER(mem_alloc_validator_list);
44 static rte_rwlock_t mem_alloc_validator_rwlock = RTE_RWLOCK_INITIALIZER;
45
46 static struct mem_event_callback_entry *
47 find_mem_event_callback(const char *name, void *arg)
48 {
49         struct mem_event_callback_entry *r;
50
51         TAILQ_FOREACH(r, &mem_event_callback_list, next) {
52                 if (!strcmp(r->name, name) && r->arg == arg)
53                         break;
54         }
55         return r;
56 }
57
58 static struct mem_alloc_validator_entry *
59 find_mem_alloc_validator(const char *name, int socket_id)
60 {
61         struct mem_alloc_validator_entry *r;
62
63         TAILQ_FOREACH(r, &mem_alloc_validator_list, next) {
64                 if (!strcmp(r->name, name) && r->socket_id == socket_id)
65                         break;
66         }
67         return r;
68 }
69
70 bool
71 eal_memalloc_is_contig(const struct rte_memseg_list *msl, void *start,
72                 size_t len)
73 {
74         void *end, *aligned_start, *aligned_end;
75         size_t pgsz = (size_t)msl->page_sz;
76         const struct rte_memseg *ms;
77
78         /* for IOVA_VA, it's always contiguous */
79         if (rte_eal_iova_mode() == RTE_IOVA_VA && !msl->external)
80                 return true;
81
82         /* for legacy memory, it's always contiguous */
83         if (internal_config.legacy_mem)
84                 return true;
85
86         end = RTE_PTR_ADD(start, len);
87
88         /* for nohuge, we check pagemap, otherwise check memseg */
89         if (!rte_eal_has_hugepages()) {
90                 rte_iova_t cur, expected;
91
92                 aligned_start = RTE_PTR_ALIGN_FLOOR(start, pgsz);
93                 aligned_end = RTE_PTR_ALIGN_CEIL(end, pgsz);
94
95                 /* if start and end are on the same page, bail out early */
96                 if (RTE_PTR_DIFF(aligned_end, aligned_start) == pgsz)
97                         return true;
98
99                 /* skip first iteration */
100                 cur = rte_mem_virt2iova(aligned_start);
101                 expected = cur + pgsz;
102                 aligned_start = RTE_PTR_ADD(aligned_start, pgsz);
103
104                 while (aligned_start < aligned_end) {
105                         cur = rte_mem_virt2iova(aligned_start);
106                         if (cur != expected)
107                                 return false;
108                         aligned_start = RTE_PTR_ADD(aligned_start, pgsz);
109                         expected += pgsz;
110                 }
111         } else {
112                 int start_seg, end_seg, cur_seg;
113                 rte_iova_t cur, expected;
114
115                 aligned_start = RTE_PTR_ALIGN_FLOOR(start, pgsz);
116                 aligned_end = RTE_PTR_ALIGN_CEIL(end, pgsz);
117
118                 start_seg = RTE_PTR_DIFF(aligned_start, msl->base_va) /
119                                 pgsz;
120                 end_seg = RTE_PTR_DIFF(aligned_end, msl->base_va) /
121                                 pgsz;
122
123                 /* if start and end are on the same page, bail out early */
124                 if (RTE_PTR_DIFF(aligned_end, aligned_start) == pgsz)
125                         return true;
126
127                 /* skip first iteration */
128                 ms = rte_fbarray_get(&msl->memseg_arr, start_seg);
129                 cur = ms->iova;
130                 expected = cur + pgsz;
131
132                 /* if we can't access IOVA addresses, assume non-contiguous */
133                 if (cur == RTE_BAD_IOVA)
134                         return false;
135
136                 for (cur_seg = start_seg + 1; cur_seg < end_seg;
137                                 cur_seg++, expected += pgsz) {
138                         ms = rte_fbarray_get(&msl->memseg_arr, cur_seg);
139
140                         if (ms->iova != expected)
141                                 return false;
142                 }
143         }
144         return true;
145 }
146
147 int
148 eal_memalloc_mem_event_callback_register(const char *name,
149                 rte_mem_event_callback_t clb, void *arg)
150 {
151         struct mem_event_callback_entry *entry;
152         int ret, len;
153         if (name == NULL || clb == NULL) {
154                 rte_errno = EINVAL;
155                 return -1;
156         }
157         len = strnlen(name, RTE_MEM_EVENT_CALLBACK_NAME_LEN);
158         if (len == 0) {
159                 rte_errno = EINVAL;
160                 return -1;
161         } else if (len == RTE_MEM_EVENT_CALLBACK_NAME_LEN) {
162                 rte_errno = ENAMETOOLONG;
163                 return -1;
164         }
165         rte_rwlock_write_lock(&mem_event_rwlock);
166
167         entry = find_mem_event_callback(name, arg);
168         if (entry != NULL) {
169                 rte_errno = EEXIST;
170                 ret = -1;
171                 goto unlock;
172         }
173
174         entry = malloc(sizeof(*entry));
175         if (entry == NULL) {
176                 rte_errno = ENOMEM;
177                 ret = -1;
178                 goto unlock;
179         }
180
181         /* callback successfully created and is valid, add it to the list */
182         entry->clb = clb;
183         entry->arg = arg;
184         strlcpy(entry->name, name, RTE_MEM_EVENT_CALLBACK_NAME_LEN);
185         TAILQ_INSERT_TAIL(&mem_event_callback_list, entry, next);
186
187         ret = 0;
188
189         RTE_LOG(DEBUG, EAL, "Mem event callback '%s:%p' registered\n",
190                         name, arg);
191
192 unlock:
193         rte_rwlock_write_unlock(&mem_event_rwlock);
194         return ret;
195 }
196
197 int
198 eal_memalloc_mem_event_callback_unregister(const char *name, void *arg)
199 {
200         struct mem_event_callback_entry *entry;
201         int ret, len;
202
203         if (name == NULL) {
204                 rte_errno = EINVAL;
205                 return -1;
206         }
207         len = strnlen(name, RTE_MEM_EVENT_CALLBACK_NAME_LEN);
208         if (len == 0) {
209                 rte_errno = EINVAL;
210                 return -1;
211         } else if (len == RTE_MEM_EVENT_CALLBACK_NAME_LEN) {
212                 rte_errno = ENAMETOOLONG;
213                 return -1;
214         }
215         rte_rwlock_write_lock(&mem_event_rwlock);
216
217         entry = find_mem_event_callback(name, arg);
218         if (entry == NULL) {
219                 rte_errno = ENOENT;
220                 ret = -1;
221                 goto unlock;
222         }
223         TAILQ_REMOVE(&mem_event_callback_list, entry, next);
224         free(entry);
225
226         ret = 0;
227
228         RTE_LOG(DEBUG, EAL, "Mem event callback '%s:%p' unregistered\n",
229                         name, arg);
230
231 unlock:
232         rte_rwlock_write_unlock(&mem_event_rwlock);
233         return ret;
234 }
235
236 void
237 eal_memalloc_mem_event_notify(enum rte_mem_event event, const void *start,
238                 size_t len)
239 {
240         struct mem_event_callback_entry *entry;
241
242         rte_rwlock_read_lock(&mem_event_rwlock);
243
244         TAILQ_FOREACH(entry, &mem_event_callback_list, next) {
245                 RTE_LOG(DEBUG, EAL, "Calling mem event callback '%s:%p'\n",
246                         entry->name, entry->arg);
247                 entry->clb(event, start, len, entry->arg);
248         }
249
250         rte_rwlock_read_unlock(&mem_event_rwlock);
251 }
252
253 int
254 eal_memalloc_mem_alloc_validator_register(const char *name,
255                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
256 {
257         struct mem_alloc_validator_entry *entry;
258         int ret, len;
259         if (name == NULL || clb == NULL || socket_id < 0) {
260                 rte_errno = EINVAL;
261                 return -1;
262         }
263         len = strnlen(name, RTE_MEM_ALLOC_VALIDATOR_NAME_LEN);
264         if (len == 0) {
265                 rte_errno = EINVAL;
266                 return -1;
267         } else if (len == RTE_MEM_ALLOC_VALIDATOR_NAME_LEN) {
268                 rte_errno = ENAMETOOLONG;
269                 return -1;
270         }
271         rte_rwlock_write_lock(&mem_alloc_validator_rwlock);
272
273         entry = find_mem_alloc_validator(name, socket_id);
274         if (entry != NULL) {
275                 rte_errno = EEXIST;
276                 ret = -1;
277                 goto unlock;
278         }
279
280         entry = malloc(sizeof(*entry));
281         if (entry == NULL) {
282                 rte_errno = ENOMEM;
283                 ret = -1;
284                 goto unlock;
285         }
286
287         /* callback successfully created and is valid, add it to the list */
288         entry->clb = clb;
289         entry->socket_id = socket_id;
290         entry->limit = limit;
291         strlcpy(entry->name, name, RTE_MEM_ALLOC_VALIDATOR_NAME_LEN);
292         TAILQ_INSERT_TAIL(&mem_alloc_validator_list, entry, next);
293
294         ret = 0;
295
296         RTE_LOG(DEBUG, EAL, "Mem alloc validator '%s' on socket %i with limit %zu registered\n",
297                 name, socket_id, limit);
298
299 unlock:
300         rte_rwlock_write_unlock(&mem_alloc_validator_rwlock);
301         return ret;
302 }
303
304 int
305 eal_memalloc_mem_alloc_validator_unregister(const char *name, int socket_id)
306 {
307         struct mem_alloc_validator_entry *entry;
308         int ret, len;
309
310         if (name == NULL || socket_id < 0) {
311                 rte_errno = EINVAL;
312                 return -1;
313         }
314         len = strnlen(name, RTE_MEM_ALLOC_VALIDATOR_NAME_LEN);
315         if (len == 0) {
316                 rte_errno = EINVAL;
317                 return -1;
318         } else if (len == RTE_MEM_ALLOC_VALIDATOR_NAME_LEN) {
319                 rte_errno = ENAMETOOLONG;
320                 return -1;
321         }
322         rte_rwlock_write_lock(&mem_alloc_validator_rwlock);
323
324         entry = find_mem_alloc_validator(name, socket_id);
325         if (entry == NULL) {
326                 rte_errno = ENOENT;
327                 ret = -1;
328                 goto unlock;
329         }
330         TAILQ_REMOVE(&mem_alloc_validator_list, entry, next);
331         free(entry);
332
333         ret = 0;
334
335         RTE_LOG(DEBUG, EAL, "Mem alloc validator '%s' on socket %i unregistered\n",
336                 name, socket_id);
337
338 unlock:
339         rte_rwlock_write_unlock(&mem_alloc_validator_rwlock);
340         return ret;
341 }
342
343 int
344 eal_memalloc_mem_alloc_validate(int socket_id, size_t new_len)
345 {
346         struct mem_alloc_validator_entry *entry;
347         int ret = 0;
348
349         rte_rwlock_read_lock(&mem_alloc_validator_rwlock);
350
351         TAILQ_FOREACH(entry, &mem_alloc_validator_list, next) {
352                 if (entry->socket_id != socket_id || entry->limit > new_len)
353                         continue;
354                 RTE_LOG(DEBUG, EAL, "Calling mem alloc validator '%s' on socket %i\n",
355                         entry->name, entry->socket_id);
356                 if (entry->clb(socket_id, entry->limit, new_len) < 0)
357                         ret = -1;
358         }
359
360         rte_rwlock_read_unlock(&mem_alloc_validator_rwlock);
361
362         return ret;
363 }