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