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