Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2017 6WIND S.A.
3 : : * Copyright 2017 Mellanox Technologies, Ltd
4 : : */
5 : :
6 : : /**
7 : : * @file
8 : : * Memory management functions for mlx4 driver.
9 : : */
10 : :
11 : : #include <errno.h>
12 : : #include <inttypes.h>
13 : : #include <stddef.h>
14 : : #include <stdint.h>
15 : : #include <string.h>
16 : :
17 : : /* Verbs headers do not support -pedantic. */
18 : : #ifdef PEDANTIC
19 : : #pragma GCC diagnostic ignored "-Wpedantic"
20 : : #endif
21 : : #include <infiniband/verbs.h>
22 : : #ifdef PEDANTIC
23 : : #pragma GCC diagnostic error "-Wpedantic"
24 : : #endif
25 : :
26 : : #include <rte_branch_prediction.h>
27 : : #include <rte_common.h>
28 : : #include <rte_eal_memconfig.h>
29 : : #include <rte_errno.h>
30 : : #include <rte_malloc.h>
31 : : #include <rte_memory.h>
32 : : #include <rte_mempool.h>
33 : : #include <rte_rwlock.h>
34 : :
35 : : #include "mlx4_glue.h"
36 : : #include "mlx4_mr.h"
37 : : #include "mlx4_rxtx.h"
38 : : #include "mlx4_utils.h"
39 : :
40 : : struct mr_find_contig_memsegs_data {
41 : : uintptr_t addr;
42 : : uintptr_t start;
43 : : uintptr_t end;
44 : : const struct rte_memseg_list *msl;
45 : : };
46 : :
47 : : struct mr_update_mp_data {
48 : : struct rte_eth_dev *dev;
49 : : struct mlx4_mr_ctrl *mr_ctrl;
50 : : int ret;
51 : : };
52 : :
53 : : /**
54 : : * Expand B-tree table to a given size. Can't be called with holding
55 : : * memory_hotplug_lock or priv->mr.rwlock due to rte_realloc().
56 : : *
57 : : * @param bt
58 : : * Pointer to B-tree structure.
59 : : * @param n
60 : : * Number of entries for expansion.
61 : : *
62 : : * @return
63 : : * 0 on success, -1 on failure.
64 : : */
65 : : static int
66 : 0 : mr_btree_expand(struct mlx4_mr_btree *bt, int n)
67 : : {
68 : : void *mem;
69 : : int ret = 0;
70 : :
71 [ # # ]: 0 : if (n <= bt->size)
72 : : return ret;
73 : : /*
74 : : * Downside of directly using rte_realloc() is that SOCKET_ID_ANY is
75 : : * used inside if there's no room to expand. Because this is a quite
76 : : * rare case and a part of very slow path, it is very acceptable.
77 : : * Initially cache_bh[] will be given practically enough space and once
78 : : * it is expanded, expansion wouldn't be needed again ever.
79 : : */
80 : 0 : mem = rte_realloc(bt->table, n * sizeof(struct mlx4_mr_cache), 0);
81 [ # # ]: 0 : if (mem == NULL) {
82 : : /* Not an error, B-tree search will be skipped. */
83 : 0 : WARN("failed to expand MR B-tree (%p) table", (void *)bt);
84 : : ret = -1;
85 : : } else {
86 : 0 : DEBUG("expanded MR B-tree table (size=%u)", n);
87 : 0 : bt->table = mem;
88 : 0 : bt->size = n;
89 : : }
90 : : return ret;
91 : : }
92 : :
93 : : /**
94 : : * Look up LKey from given B-tree lookup table, store the last index and return
95 : : * searched LKey.
96 : : *
97 : : * @param bt
98 : : * Pointer to B-tree structure.
99 : : * @param[out] idx
100 : : * Pointer to index. Even on search failure, returns index where it stops
101 : : * searching so that index can be used when inserting a new entry.
102 : : * @param addr
103 : : * Search key.
104 : : *
105 : : * @return
106 : : * Searched LKey on success, UINT32_MAX on no match.
107 : : */
108 : : static uint32_t
109 : : mr_btree_lookup(struct mlx4_mr_btree *bt, uint16_t *idx, uintptr_t addr)
110 : : {
111 : : struct mlx4_mr_cache *lkp_tbl;
112 : : uint16_t n;
113 : : uint16_t base = 0;
114 : :
115 : : MLX4_ASSERT(bt != NULL);
116 : 0 : lkp_tbl = *bt->table;
117 : 0 : n = bt->len;
118 : : /* First entry must be NULL for comparison. */
119 : : MLX4_ASSERT(bt->len > 0 || (lkp_tbl[0].start == 0 &&
120 : : lkp_tbl[0].lkey == UINT32_MAX));
121 : : /* Binary search. */
122 : : do {
123 : 0 : register uint16_t delta = n >> 1;
124 : :
125 [ # # # # : 0 : if (addr < lkp_tbl[base + delta].start) {
# # # # ]
126 : : n = delta;
127 : : } else {
128 : 0 : base += delta;
129 : 0 : n -= delta;
130 : : }
131 [ # # # # : 0 : } while (n > 1);
# # # # ]
132 : : MLX4_ASSERT(addr >= lkp_tbl[base].start);
133 : : *idx = base;
134 [ # # # # : 0 : if (addr < lkp_tbl[base].end)
# # # # ]
135 : 0 : return lkp_tbl[base].lkey;
136 : : /* Not found. */
137 : : return UINT32_MAX;
138 : : }
139 : :
140 : : /**
141 : : * Insert an entry to B-tree lookup table.
142 : : *
143 : : * @param bt
144 : : * Pointer to B-tree structure.
145 : : * @param entry
146 : : * Pointer to new entry to insert.
147 : : *
148 : : * @return
149 : : * 0 on success, -1 on failure.
150 : : */
151 : : static int
152 : 0 : mr_btree_insert(struct mlx4_mr_btree *bt, struct mlx4_mr_cache *entry)
153 : : {
154 : : struct mlx4_mr_cache *lkp_tbl;
155 : : uint16_t idx = 0;
156 : : size_t shift;
157 : :
158 : : MLX4_ASSERT(bt != NULL);
159 : : MLX4_ASSERT(bt->len <= bt->size);
160 : : MLX4_ASSERT(bt->len > 0);
161 : 0 : lkp_tbl = *bt->table;
162 : : /* Find out the slot for insertion. */
163 [ # # ]: 0 : if (mr_btree_lookup(bt, &idx, entry->start) != UINT32_MAX) {
164 : 0 : DEBUG("abort insertion to B-tree(%p): already exist at"
165 : : " idx=%u [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
166 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
167 : : /* Already exist, return. */
168 : 0 : return 0;
169 : : }
170 : : /* If table is full, return error. */
171 [ # # ]: 0 : if (unlikely(bt->len == bt->size)) {
172 : 0 : bt->overflow = 1;
173 : 0 : return -1;
174 : : }
175 : : /* Insert entry. */
176 : 0 : ++idx;
177 : 0 : shift = (bt->len - idx) * sizeof(struct mlx4_mr_cache);
178 [ # # ]: 0 : if (shift)
179 : 0 : memmove(&lkp_tbl[idx + 1], &lkp_tbl[idx], shift);
180 : 0 : lkp_tbl[idx] = *entry;
181 : 0 : bt->len++;
182 : 0 : DEBUG("inserted B-tree(%p)[%u],"
183 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
184 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
185 : 0 : return 0;
186 : : }
187 : :
188 : : /**
189 : : * Initialize B-tree and allocate memory for lookup table.
190 : : *
191 : : * @param bt
192 : : * Pointer to B-tree structure.
193 : : * @param n
194 : : * Number of entries to allocate.
195 : : * @param socket
196 : : * NUMA socket on which memory must be allocated.
197 : : *
198 : : * @return
199 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
200 : : */
201 : : int
202 : 0 : mlx4_mr_btree_init(struct mlx4_mr_btree *bt, int n, int socket)
203 : : {
204 [ # # ]: 0 : if (bt == NULL) {
205 : 0 : rte_errno = EINVAL;
206 : 0 : return -rte_errno;
207 : : }
208 : : memset(bt, 0, sizeof(*bt));
209 : 0 : bt->table = rte_calloc_socket("B-tree table",
210 : : n, sizeof(struct mlx4_mr_cache),
211 : : 0, socket);
212 [ # # ]: 0 : if (bt->table == NULL) {
213 : 0 : rte_errno = ENOMEM;
214 : 0 : ERROR("failed to allocate memory for btree cache on socket %d",
215 : : socket);
216 : 0 : return -rte_errno;
217 : : }
218 : 0 : bt->size = n;
219 : : /* First entry must be NULL for binary search. */
220 : 0 : (*bt->table)[bt->len++] = (struct mlx4_mr_cache) {
221 : : .lkey = UINT32_MAX,
222 : : };
223 : 0 : DEBUG("initialized B-tree %p with table %p",
224 : : (void *)bt, (void *)bt->table);
225 : 0 : return 0;
226 : : }
227 : :
228 : : /**
229 : : * Free B-tree resources.
230 : : *
231 : : * @param bt
232 : : * Pointer to B-tree structure.
233 : : */
234 : : void
235 : 0 : mlx4_mr_btree_free(struct mlx4_mr_btree *bt)
236 : : {
237 [ # # ]: 0 : if (bt == NULL)
238 : : return;
239 : 0 : DEBUG("freeing B-tree %p with table %p", (void *)bt, (void *)bt->table);
240 : 0 : rte_free(bt->table);
241 : : memset(bt, 0, sizeof(*bt));
242 : : }
243 : :
244 : : #ifdef RTE_LIBRTE_MLX4_DEBUG
245 : : /**
246 : : * Dump all the entries in a B-tree
247 : : *
248 : : * @param bt
249 : : * Pointer to B-tree structure.
250 : : */
251 : : void
252 : : mlx4_mr_btree_dump(struct mlx4_mr_btree *bt)
253 : : {
254 : : int idx;
255 : : struct mlx4_mr_cache *lkp_tbl;
256 : :
257 : : if (bt == NULL)
258 : : return;
259 : : lkp_tbl = *bt->table;
260 : : for (idx = 0; idx < bt->len; ++idx) {
261 : : struct mlx4_mr_cache *entry = &lkp_tbl[idx];
262 : :
263 : : DEBUG("B-tree(%p)[%u],"
264 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
265 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
266 : : }
267 : : }
268 : : #endif
269 : :
270 : : /**
271 : : * Find virtually contiguous memory chunk in a given MR.
272 : : *
273 : : * @param dev
274 : : * Pointer to MR structure.
275 : : * @param[out] entry
276 : : * Pointer to returning MR cache entry. If not found, this will not be
277 : : * updated.
278 : : * @param start_idx
279 : : * Start index of the memseg bitmap.
280 : : *
281 : : * @return
282 : : * Next index to go on lookup.
283 : : */
284 : : static int
285 : 0 : mr_find_next_chunk(struct mlx4_mr *mr, struct mlx4_mr_cache *entry,
286 : : int base_idx)
287 : : {
288 : : uintptr_t start = 0;
289 : : uintptr_t end = 0;
290 : : uint32_t idx = 0;
291 : :
292 : : /* MR for external memory doesn't have memseg list. */
293 [ # # ]: 0 : if (mr->msl == NULL) {
294 : 0 : struct ibv_mr *ibv_mr = mr->ibv_mr;
295 : :
296 : : MLX4_ASSERT(mr->ms_bmp_n == 1);
297 : : MLX4_ASSERT(mr->ms_n == 1);
298 : : MLX4_ASSERT(base_idx == 0);
299 : : /*
300 : : * Can't search it from memseg list but get it directly from
301 : : * verbs MR as there's only one chunk.
302 : : */
303 : 0 : entry->start = (uintptr_t)ibv_mr->addr;
304 : 0 : entry->end = (uintptr_t)ibv_mr->addr + mr->ibv_mr->length;
305 [ # # ]: 0 : entry->lkey = rte_cpu_to_be_32(mr->ibv_mr->lkey);
306 : : /* Returning 1 ends iteration. */
307 : 0 : return 1;
308 : : }
309 [ # # ]: 0 : for (idx = base_idx; idx < mr->ms_bmp_n; ++idx) {
310 [ # # ]: 0 : if (rte_bitmap_get(mr->ms_bmp, idx)) {
311 : : const struct rte_memseg_list *msl;
312 : : const struct rte_memseg *ms;
313 : :
314 : 0 : msl = mr->msl;
315 : 0 : ms = rte_fbarray_get(&msl->memseg_arr,
316 : 0 : mr->ms_base_idx + idx);
317 : : MLX4_ASSERT(msl->page_sz == ms->hugepage_sz);
318 [ # # ]: 0 : if (!start)
319 : 0 : start = ms->addr_64;
320 : 0 : end = ms->addr_64 + ms->hugepage_sz;
321 [ # # ]: 0 : } else if (start) {
322 : : /* Passed the end of a fragment. */
323 : : break;
324 : : }
325 : : }
326 [ # # ]: 0 : if (start) {
327 : : /* Found one chunk. */
328 : 0 : entry->start = start;
329 : 0 : entry->end = end;
330 [ # # ]: 0 : entry->lkey = rte_cpu_to_be_32(mr->ibv_mr->lkey);
331 : : }
332 : 0 : return idx;
333 : : }
334 : :
335 : : /**
336 : : * Insert a MR to the global B-tree cache. It may fail due to low-on-memory.
337 : : * Then, this entry will have to be searched by mr_lookup_dev_list() in
338 : : * mlx4_mr_create() on miss.
339 : : *
340 : : * @param dev
341 : : * Pointer to Ethernet device.
342 : : * @param mr
343 : : * Pointer to MR to insert.
344 : : *
345 : : * @return
346 : : * 0 on success, -1 on failure.
347 : : */
348 : : static int
349 : 0 : mr_insert_dev_cache(struct rte_eth_dev *dev, struct mlx4_mr *mr)
350 : : {
351 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
352 : : unsigned int n;
353 : :
354 : 0 : DEBUG("port %u inserting MR(%p) to global cache",
355 : : dev->data->port_id, (void *)mr);
356 [ # # ]: 0 : for (n = 0; n < mr->ms_bmp_n; ) {
357 : : struct mlx4_mr_cache entry;
358 : :
359 : : memset(&entry, 0, sizeof(entry));
360 : : /* Find a contiguous chunk and advance the index. */
361 : 0 : n = mr_find_next_chunk(mr, &entry, n);
362 [ # # ]: 0 : if (!entry.end)
363 : : break;
364 [ # # ]: 0 : if (mr_btree_insert(&priv->mr.cache, &entry) < 0) {
365 : : /*
366 : : * Overflowed, but the global table cannot be expanded
367 : : * because of deadlock.
368 : : */
369 : 0 : return -1;
370 : : }
371 : : }
372 : : return 0;
373 : : }
374 : :
375 : : /**
376 : : * Look up address in the original global MR list.
377 : : *
378 : : * @param dev
379 : : * Pointer to Ethernet device.
380 : : * @param[out] entry
381 : : * Pointer to returning MR cache entry. If no match, this will not be updated.
382 : : * @param addr
383 : : * Search key.
384 : : *
385 : : * @return
386 : : * Found MR on match, NULL otherwise.
387 : : */
388 : : static struct mlx4_mr *
389 : 0 : mr_lookup_dev_list(struct rte_eth_dev *dev, struct mlx4_mr_cache *entry,
390 : : uintptr_t addr)
391 : : {
392 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
393 : : struct mlx4_mr *mr;
394 : :
395 : : /* Iterate all the existing MRs. */
396 [ # # ]: 0 : LIST_FOREACH(mr, &priv->mr.mr_list, mr) {
397 : : unsigned int n;
398 : :
399 [ # # ]: 0 : if (mr->ms_n == 0)
400 : 0 : continue;
401 [ # # ]: 0 : for (n = 0; n < mr->ms_bmp_n; ) {
402 : : struct mlx4_mr_cache ret;
403 : :
404 : : memset(&ret, 0, sizeof(ret));
405 : 0 : n = mr_find_next_chunk(mr, &ret, n);
406 [ # # # # ]: 0 : if (addr >= ret.start && addr < ret.end) {
407 : : /* Found. */
408 : 0 : *entry = ret;
409 : 0 : return mr;
410 : : }
411 : : }
412 : : }
413 : : return NULL;
414 : : }
415 : :
416 : : /**
417 : : * Look up address on device.
418 : : *
419 : : * @param dev
420 : : * Pointer to Ethernet device.
421 : : * @param[out] entry
422 : : * Pointer to returning MR cache entry. If no match, this will not be updated.
423 : : * @param addr
424 : : * Search key.
425 : : *
426 : : * @return
427 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
428 : : */
429 : : static uint32_t
430 : 0 : mr_lookup_dev(struct rte_eth_dev *dev, struct mlx4_mr_cache *entry,
431 : : uintptr_t addr)
432 : : {
433 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
434 : : uint16_t idx;
435 : : uint32_t lkey = UINT32_MAX;
436 : : struct mlx4_mr *mr;
437 : :
438 : : /*
439 : : * If the global cache has overflowed since it failed to expand the
440 : : * B-tree table, it can't have all the existing MRs. Then, the address
441 : : * has to be searched by traversing the original MR list instead, which
442 : : * is very slow path. Otherwise, the global cache is all inclusive.
443 : : */
444 [ # # ]: 0 : if (!unlikely(priv->mr.cache.overflow)) {
445 : : lkey = mr_btree_lookup(&priv->mr.cache, &idx, addr);
446 [ # # ]: 0 : if (lkey != UINT32_MAX)
447 : 0 : *entry = (*priv->mr.cache.table)[idx];
448 : : } else {
449 : : /* Falling back to the slowest path. */
450 : 0 : mr = mr_lookup_dev_list(dev, entry, addr);
451 [ # # ]: 0 : if (mr != NULL)
452 : 0 : lkey = entry->lkey;
453 : : }
454 : : MLX4_ASSERT(lkey == UINT32_MAX || (addr >= entry->start &&
455 : : addr < entry->end));
456 : 0 : return lkey;
457 : : }
458 : :
459 : : /**
460 : : * Free MR resources. MR lock must not be held to avoid a deadlock. rte_free()
461 : : * can raise memory free event and the callback function will spin on the lock.
462 : : *
463 : : * @param mr
464 : : * Pointer to MR to free.
465 : : */
466 : : static void
467 : 0 : mr_free(struct mlx4_mr *mr)
468 : : {
469 [ # # ]: 0 : if (mr == NULL)
470 : : return;
471 : 0 : DEBUG("freeing MR(%p):", (void *)mr);
472 [ # # ]: 0 : if (mr->ibv_mr != NULL)
473 : 0 : claim_zero(mlx4_glue->dereg_mr(mr->ibv_mr));
474 : : rte_bitmap_free(mr->ms_bmp);
475 : 0 : rte_free(mr);
476 : : }
477 : :
478 : : /**
479 : : * Release resources of detached MR having no online entry.
480 : : *
481 : : * @param dev
482 : : * Pointer to Ethernet device.
483 : : */
484 : : static void
485 : 0 : mlx4_mr_garbage_collect(struct rte_eth_dev *dev)
486 : : {
487 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
488 : : struct mlx4_mr *mr_next;
489 : : struct mlx4_mr_list free_list = LIST_HEAD_INITIALIZER(free_list);
490 : :
491 : : /* Must be called from the primary process. */
492 : : MLX4_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
493 : : /*
494 : : * MR can't be freed with holding the lock because rte_free() could call
495 : : * memory free callback function. This will be a deadlock situation.
496 : : */
497 : 0 : rte_rwlock_write_lock(&priv->mr.rwlock);
498 : : /* Detach the whole free list and release it after unlocking. */
499 : 0 : free_list = priv->mr.mr_free_list;
500 : 0 : LIST_INIT(&priv->mr.mr_free_list);
501 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
502 : : /* Release resources. */
503 : : mr_next = LIST_FIRST(&free_list);
504 [ # # ]: 0 : while (mr_next != NULL) {
505 : : struct mlx4_mr *mr = mr_next;
506 : :
507 : 0 : mr_next = LIST_NEXT(mr, mr);
508 : 0 : mr_free(mr);
509 : : }
510 : 0 : }
511 : :
512 : : /* Called during rte_memseg_contig_walk() by mlx4_mr_create(). */
513 : : static int
514 : 0 : mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl,
515 : : const struct rte_memseg *ms, size_t len, void *arg)
516 : : {
517 : : struct mr_find_contig_memsegs_data *data = arg;
518 : :
519 [ # # # # ]: 0 : if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)
520 : : return 0;
521 : : /* Found, save it and stop walking. */
522 : 0 : data->start = ms->addr_64;
523 : 0 : data->end = ms->addr_64 + len;
524 : 0 : data->msl = msl;
525 : 0 : return 1;
526 : : }
527 : :
528 : : /**
529 : : * Create a new global Memory Region (MR) for a missing virtual address.
530 : : * This API should be called on a secondary process, then a request is sent to
531 : : * the primary process in order to create a MR for the address. As the global MR
532 : : * list is on the shared memory, following LKey lookup should succeed unless the
533 : : * request fails.
534 : : *
535 : : * @param dev
536 : : * Pointer to Ethernet device.
537 : : * @param[out] entry
538 : : * Pointer to returning MR cache entry, found in the global cache or newly
539 : : * created. If failed to create one, this will not be updated.
540 : : * @param addr
541 : : * Target virtual address to register.
542 : : *
543 : : * @return
544 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
545 : : */
546 : : static uint32_t
547 : 0 : mlx4_mr_create_secondary(struct rte_eth_dev *dev, struct mlx4_mr_cache *entry,
548 : : uintptr_t addr)
549 : : {
550 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
551 : : int ret;
552 : :
553 : 0 : DEBUG("port %u requesting MR creation for address (%p)",
554 : : dev->data->port_id, (void *)addr);
555 : 0 : ret = mlx4_mp_req_mr_create(dev, addr);
556 [ # # ]: 0 : if (ret) {
557 : 0 : DEBUG("port %u fail to request MR creation for address (%p)",
558 : : dev->data->port_id, (void *)addr);
559 : 0 : return UINT32_MAX;
560 : : }
561 : 0 : rte_rwlock_read_lock(&priv->mr.rwlock);
562 : : /* Fill in output data. */
563 : 0 : mr_lookup_dev(dev, entry, addr);
564 : : /* Lookup can't fail. */
565 : : MLX4_ASSERT(entry->lkey != UINT32_MAX);
566 : : rte_rwlock_read_unlock(&priv->mr.rwlock);
567 : 0 : DEBUG("port %u MR CREATED by primary process for %p:",
568 : : dev->data->port_id, (void *)addr);
569 : 0 : DEBUG(" [0x%" PRIxPTR ", 0x%" PRIxPTR "), lkey=0x%x",
570 : : entry->start, entry->end, entry->lkey);
571 : 0 : return entry->lkey;
572 : : }
573 : :
574 : : /**
575 : : * Create a new global Memory Region (MR) for a missing virtual address.
576 : : * Register entire virtually contiguous memory chunk around the address.
577 : : * This must be called from the primary process.
578 : : *
579 : : * @param dev
580 : : * Pointer to Ethernet device.
581 : : * @param[out] entry
582 : : * Pointer to returning MR cache entry, found in the global cache or newly
583 : : * created. If failed to create one, this will not be updated.
584 : : * @param addr
585 : : * Target virtual address to register.
586 : : *
587 : : * @return
588 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
589 : : */
590 : : uint32_t
591 : 0 : mlx4_mr_create_primary(struct rte_eth_dev *dev, struct mlx4_mr_cache *entry,
592 : : uintptr_t addr)
593 : : {
594 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
595 : : const struct rte_memseg_list *msl;
596 : : const struct rte_memseg *ms;
597 : : struct mlx4_mr *mr = NULL;
598 : : size_t len;
599 : : uint32_t ms_n;
600 : : uint32_t bmp_size;
601 : : void *bmp_mem;
602 : : int ms_idx_shift = -1;
603 : : unsigned int n;
604 : 0 : struct mr_find_contig_memsegs_data data = {
605 : : .addr = addr,
606 : : };
607 : : struct mr_find_contig_memsegs_data data_re;
608 : :
609 : 0 : DEBUG("port %u creating a MR using address (%p)",
610 : : dev->data->port_id, (void *)addr);
611 : : /*
612 : : * Release detached MRs if any. This can't be called with holding either
613 : : * memory_hotplug_lock or priv->mr.rwlock. MRs on the free list have
614 : : * been detached by the memory free event but it couldn't be released
615 : : * inside the callback due to deadlock. As a result, releasing resources
616 : : * is quite opportunistic.
617 : : */
618 : 0 : mlx4_mr_garbage_collect(dev);
619 : : /*
620 : : * If enabled, find out a contiguous virtual address chunk in use, to
621 : : * which the given address belongs, in order to register maximum range.
622 : : * In the best case where mempools are not dynamically recreated and
623 : : * '--socket-mem' is specified as an EAL option, it is very likely to
624 : : * have only one MR(LKey) per a socket and per a hugepage-size even
625 : : * though the system memory is highly fragmented. As the whole memory
626 : : * chunk will be pinned by kernel, it can't be reused unless entire
627 : : * chunk is freed from EAL.
628 : : *
629 : : * If disabled, just register one memseg (page). Then, memory
630 : : * consumption will be minimized but it may drop performance if there
631 : : * are many MRs to lookup on the datapath.
632 : : */
633 [ # # ]: 0 : if (!priv->mr_ext_memseg_en) {
634 : 0 : data.msl = rte_mem_virt2memseg_list((void *)addr);
635 : 0 : data.start = RTE_ALIGN_FLOOR(addr, data.msl->page_sz);
636 : 0 : data.end = data.start + data.msl->page_sz;
637 [ # # ]: 0 : } else if (!rte_memseg_contig_walk(mr_find_contig_memsegs_cb, &data)) {
638 : 0 : WARN("port %u unable to find virtually contiguous"
639 : : " chunk for address (%p)."
640 : : " rte_memseg_contig_walk() failed.",
641 : : dev->data->port_id, (void *)addr);
642 : 0 : rte_errno = ENXIO;
643 : 0 : goto err_nolock;
644 : : }
645 : 0 : alloc_resources:
646 : : /* Addresses must be page-aligned. */
647 : : MLX4_ASSERT(rte_is_aligned((void *)data.start, data.msl->page_sz));
648 : : MLX4_ASSERT(rte_is_aligned((void *)data.end, data.msl->page_sz));
649 : 0 : msl = data.msl;
650 : 0 : ms = rte_mem_virt2memseg((void *)data.start, msl);
651 : 0 : len = data.end - data.start;
652 : : MLX4_ASSERT(msl->page_sz == ms->hugepage_sz);
653 : : /* Number of memsegs in the range. */
654 : 0 : ms_n = len / msl->page_sz;
655 : 0 : DEBUG("port %u extending %p to [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
656 : : " page_sz=0x%" PRIx64 ", ms_n=%u",
657 : : dev->data->port_id, (void *)addr,
658 : : data.start, data.end, msl->page_sz, ms_n);
659 : : /* Size of memory for bitmap. */
660 : 0 : bmp_size = rte_bitmap_get_memory_footprint(ms_n);
661 : 0 : mr = rte_zmalloc_socket(NULL,
662 : : RTE_ALIGN_CEIL(sizeof(*mr),
663 : : RTE_CACHE_LINE_SIZE) +
664 : : bmp_size,
665 : 0 : RTE_CACHE_LINE_SIZE, msl->socket_id);
666 [ # # ]: 0 : if (mr == NULL) {
667 : 0 : WARN("port %u unable to allocate memory for a new MR of"
668 : : " address (%p).",
669 : : dev->data->port_id, (void *)addr);
670 : 0 : rte_errno = ENOMEM;
671 : 0 : goto err_nolock;
672 : : }
673 : 0 : mr->msl = msl;
674 : : /*
675 : : * Save the index of the first memseg and initialize memseg bitmap. To
676 : : * see if a memseg of ms_idx in the memseg-list is still valid, check:
677 : : * rte_bitmap_get(mr->bmp, ms_idx - mr->ms_base_idx)
678 : : */
679 : 0 : mr->ms_base_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
680 : 0 : bmp_mem = RTE_PTR_ALIGN_CEIL(mr + 1, RTE_CACHE_LINE_SIZE);
681 : 0 : mr->ms_bmp = rte_bitmap_init(ms_n, bmp_mem, bmp_size);
682 [ # # ]: 0 : if (mr->ms_bmp == NULL) {
683 : 0 : WARN("port %u unable to initialize bitmap for a new MR of"
684 : : " address (%p).",
685 : : dev->data->port_id, (void *)addr);
686 : 0 : rte_errno = EINVAL;
687 : 0 : goto err_nolock;
688 : : }
689 : : /*
690 : : * Should recheck whether the extended contiguous chunk is still valid.
691 : : * Because memory_hotplug_lock can't be held if there's any memory
692 : : * related calls in a critical path, resource allocation above can't be
693 : : * locked. If the memory has been changed at this point, try again with
694 : : * just single page. If not, go on with the big chunk atomically from
695 : : * here.
696 : : */
697 : 0 : rte_mcfg_mem_read_lock();
698 : 0 : data_re = data;
699 [ # # # # ]: 0 : if (len > msl->page_sz &&
700 : 0 : !rte_memseg_contig_walk(mr_find_contig_memsegs_cb, &data_re)) {
701 : 0 : WARN("port %u unable to find virtually contiguous"
702 : : " chunk for address (%p)."
703 : : " rte_memseg_contig_walk() failed.",
704 : : dev->data->port_id, (void *)addr);
705 : 0 : rte_errno = ENXIO;
706 : 0 : goto err_memlock;
707 : : }
708 [ # # # # ]: 0 : if (data.start != data_re.start || data.end != data_re.end) {
709 : : /*
710 : : * The extended contiguous chunk has been changed. Try again
711 : : * with single memseg instead.
712 : : */
713 : 0 : data.start = RTE_ALIGN_FLOOR(addr, msl->page_sz);
714 : 0 : data.end = data.start + msl->page_sz;
715 : 0 : rte_mcfg_mem_read_unlock();
716 : 0 : mr_free(mr);
717 : 0 : goto alloc_resources;
718 : : }
719 : : MLX4_ASSERT(data.msl == data_re.msl);
720 : 0 : rte_rwlock_write_lock(&priv->mr.rwlock);
721 : : /*
722 : : * Check the address is really missing. If other thread already created
723 : : * one or it is not found due to overflow, abort and return.
724 : : */
725 [ # # ]: 0 : if (mr_lookup_dev(dev, entry, addr) != UINT32_MAX) {
726 : : /*
727 : : * Insert to the global cache table. It may fail due to
728 : : * low-on-memory. Then, this entry will have to be searched
729 : : * here again.
730 : : */
731 : 0 : mr_btree_insert(&priv->mr.cache, entry);
732 : 0 : DEBUG("port %u found MR for %p on final lookup, abort",
733 : : dev->data->port_id, (void *)addr);
734 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
735 : 0 : rte_mcfg_mem_read_unlock();
736 : : /*
737 : : * Must be unlocked before calling rte_free() because
738 : : * mlx4_mr_mem_event_free_cb() can be called inside.
739 : : */
740 : 0 : mr_free(mr);
741 : 0 : return entry->lkey;
742 : : }
743 : : /*
744 : : * Trim start and end addresses for verbs MR. Set bits for registering
745 : : * memsegs but exclude already registered ones. Bitmap can be
746 : : * fragmented.
747 : : */
748 [ # # ]: 0 : for (n = 0; n < ms_n; ++n) {
749 : : uintptr_t start;
750 : : struct mlx4_mr_cache ret;
751 : :
752 : : memset(&ret, 0, sizeof(ret));
753 : 0 : start = data_re.start + n * msl->page_sz;
754 : : /* Exclude memsegs already registered by other MRs. */
755 [ # # ]: 0 : if (mr_lookup_dev(dev, &ret, start) == UINT32_MAX) {
756 : : /*
757 : : * Start from the first unregistered memseg in the
758 : : * extended range.
759 : : */
760 [ # # ]: 0 : if (ms_idx_shift == -1) {
761 : 0 : mr->ms_base_idx += n;
762 : 0 : data.start = start;
763 : 0 : ms_idx_shift = n;
764 : : }
765 : 0 : data.end = start + msl->page_sz;
766 : 0 : rte_bitmap_set(mr->ms_bmp, n - ms_idx_shift);
767 : 0 : ++mr->ms_n;
768 : : }
769 : : }
770 : 0 : len = data.end - data.start;
771 : 0 : mr->ms_bmp_n = len / msl->page_sz;
772 : : MLX4_ASSERT(ms_idx_shift + mr->ms_bmp_n <= ms_n);
773 : : /*
774 : : * Finally create a verbs MR for the memory chunk. ibv_reg_mr() can be
775 : : * called with holding the memory lock because it doesn't use
776 : : * mlx4_alloc_buf_extern() which eventually calls rte_malloc_socket()
777 : : * through mlx4_alloc_verbs_buf().
778 : : */
779 : 0 : mr->ibv_mr = mlx4_glue->reg_mr(priv->pd, (void *)data.start, len,
780 : : IBV_ACCESS_LOCAL_WRITE);
781 [ # # ]: 0 : if (mr->ibv_mr == NULL) {
782 : 0 : WARN("port %u fail to create a verbs MR for address (%p)",
783 : : dev->data->port_id, (void *)addr);
784 : 0 : rte_errno = EINVAL;
785 : 0 : goto err_mrlock;
786 : : }
787 : : MLX4_ASSERT((uintptr_t)mr->ibv_mr->addr == data.start);
788 : : MLX4_ASSERT(mr->ibv_mr->length == len);
789 [ # # ]: 0 : LIST_INSERT_HEAD(&priv->mr.mr_list, mr, mr);
790 : 0 : DEBUG("port %u MR CREATED (%p) for %p:",
791 : : dev->data->port_id, (void *)mr, (void *)addr);
792 [ # # ]: 0 : DEBUG(" [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
793 : : " lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
794 : : data.start, data.end, rte_cpu_to_be_32(mr->ibv_mr->lkey),
795 : : mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
796 : : /* Insert to the global cache table. */
797 : 0 : mr_insert_dev_cache(dev, mr);
798 : : /* Fill in output data. */
799 : 0 : mr_lookup_dev(dev, entry, addr);
800 : : /* Lookup can't fail. */
801 : : MLX4_ASSERT(entry->lkey != UINT32_MAX);
802 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
803 : 0 : rte_mcfg_mem_read_unlock();
804 : 0 : return entry->lkey;
805 : : err_mrlock:
806 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
807 : 0 : err_memlock:
808 : 0 : rte_mcfg_mem_read_unlock();
809 : 0 : err_nolock:
810 : : /*
811 : : * In case of error, as this can be called in a datapath, a warning
812 : : * message per an error is preferable instead. Must be unlocked before
813 : : * calling rte_free() because mlx4_mr_mem_event_free_cb() can be called
814 : : * inside.
815 : : */
816 : 0 : mr_free(mr);
817 : 0 : return UINT32_MAX;
818 : : }
819 : :
820 : : /**
821 : : * Create a new global Memory Region (MR) for a missing virtual address.
822 : : * This can be called from primary and secondary process.
823 : : *
824 : : * @param dev
825 : : * Pointer to Ethernet device.
826 : : * @param[out] entry
827 : : * Pointer to returning MR cache entry, found in the global cache or newly
828 : : * created. If failed to create one, this will not be updated.
829 : : * @param addr
830 : : * Target virtual address to register.
831 : : *
832 : : * @return
833 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
834 : : */
835 : : static uint32_t
836 : 0 : mlx4_mr_create(struct rte_eth_dev *dev, struct mlx4_mr_cache *entry,
837 : : uintptr_t addr)
838 : : {
839 : : uint32_t ret = 0;
840 : :
841 [ # # # ]: 0 : switch (rte_eal_process_type()) {
842 : 0 : case RTE_PROC_PRIMARY:
843 : 0 : ret = mlx4_mr_create_primary(dev, entry, addr);
844 : 0 : break;
845 : 0 : case RTE_PROC_SECONDARY:
846 : 0 : ret = mlx4_mr_create_secondary(dev, entry, addr);
847 : 0 : break;
848 : : default:
849 : : break;
850 : : }
851 : 0 : return ret;
852 : : }
853 : :
854 : : /**
855 : : * Rebuild the global B-tree cache of device from the original MR list.
856 : : *
857 : : * @param dev
858 : : * Pointer to Ethernet device.
859 : : */
860 : : static void
861 : 0 : mr_rebuild_dev_cache(struct rte_eth_dev *dev)
862 : : {
863 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
864 : : struct mlx4_mr *mr;
865 : :
866 : 0 : DEBUG("port %u rebuild dev cache[]", dev->data->port_id);
867 : : /* Flush cache to rebuild. */
868 : 0 : priv->mr.cache.len = 1;
869 : 0 : priv->mr.cache.overflow = 0;
870 : : /* Iterate all the existing MRs. */
871 [ # # ]: 0 : LIST_FOREACH(mr, &priv->mr.mr_list, mr)
872 [ # # ]: 0 : if (mr_insert_dev_cache(dev, mr) < 0)
873 : : return;
874 : : }
875 : :
876 : : /**
877 : : * Callback for memory free event. Iterate freed memsegs and check whether it
878 : : * belongs to an existing MR. If found, clear the bit from bitmap of MR. As a
879 : : * result, the MR would be fragmented. If it becomes empty, the MR will be freed
880 : : * later by mlx4_mr_garbage_collect().
881 : : *
882 : : * The global cache must be rebuilt if there's any change and this event has to
883 : : * be propagated to dataplane threads to flush the local caches.
884 : : *
885 : : * @param dev
886 : : * Pointer to Ethernet device.
887 : : * @param addr
888 : : * Address of freed memory.
889 : : * @param len
890 : : * Size of freed memory.
891 : : */
892 : : static void
893 : 0 : mlx4_mr_mem_event_free_cb(struct rte_eth_dev *dev, const void *addr, size_t len)
894 : : {
895 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
896 : : const struct rte_memseg_list *msl;
897 : : struct mlx4_mr *mr;
898 : : int ms_n;
899 : : int i;
900 : : int rebuild = 0;
901 : :
902 : 0 : DEBUG("port %u free callback: addr=%p, len=%zu",
903 : : dev->data->port_id, addr, len);
904 : 0 : msl = rte_mem_virt2memseg_list(addr);
905 : : /* addr and len must be page-aligned. */
906 : : MLX4_ASSERT((uintptr_t)addr ==
907 : : RTE_ALIGN((uintptr_t)addr, msl->page_sz));
908 : : MLX4_ASSERT(len == RTE_ALIGN(len, msl->page_sz));
909 : 0 : ms_n = len / msl->page_sz;
910 : 0 : rte_rwlock_write_lock(&priv->mr.rwlock);
911 : : /* Clear bits of freed memsegs from MR. */
912 [ # # ]: 0 : for (i = 0; i < ms_n; ++i) {
913 : : const struct rte_memseg *ms;
914 : : struct mlx4_mr_cache entry;
915 : : uintptr_t start;
916 : : int ms_idx;
917 : : uint32_t pos;
918 : :
919 : : /* Find MR having this memseg. */
920 : 0 : start = (uintptr_t)addr + i * msl->page_sz;
921 : 0 : mr = mr_lookup_dev_list(dev, &entry, start);
922 [ # # ]: 0 : if (mr == NULL)
923 : 0 : continue;
924 : : MLX4_ASSERT(mr->msl); /* Can't be external memory. */
925 : 0 : ms = rte_mem_virt2memseg((void *)start, msl);
926 : : MLX4_ASSERT(ms != NULL);
927 : : MLX4_ASSERT(msl->page_sz == ms->hugepage_sz);
928 : 0 : ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
929 : 0 : pos = ms_idx - mr->ms_base_idx;
930 : : MLX4_ASSERT(rte_bitmap_get(mr->ms_bmp, pos));
931 : : MLX4_ASSERT(pos < mr->ms_bmp_n);
932 : 0 : DEBUG("port %u MR(%p): clear bitmap[%u] for addr %p",
933 : : dev->data->port_id, (void *)mr, pos, (void *)start);
934 : 0 : rte_bitmap_clear(mr->ms_bmp, pos);
935 [ # # ]: 0 : if (--mr->ms_n == 0) {
936 [ # # ]: 0 : LIST_REMOVE(mr, mr);
937 [ # # ]: 0 : LIST_INSERT_HEAD(&priv->mr.mr_free_list, mr, mr);
938 : 0 : DEBUG("port %u remove MR(%p) from list",
939 : : dev->data->port_id, (void *)mr);
940 : : }
941 : : /*
942 : : * MR is fragmented or will be freed. the global cache must be
943 : : * rebuilt.
944 : : */
945 : : rebuild = 1;
946 : : }
947 [ # # ]: 0 : if (rebuild) {
948 : 0 : mr_rebuild_dev_cache(dev);
949 : : /*
950 : : * No explicit wmb is needed after updating dev_gen due to
951 : : * store-release ordering in unlock that provides the
952 : : * implicit barrier at the software visible level.
953 : : */
954 : 0 : ++priv->mr.dev_gen;
955 : 0 : DEBUG("broadcasting local cache flush, gen=%d",
956 : : priv->mr.dev_gen);
957 : : }
958 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
959 : : #ifdef RTE_LIBRTE_MLX4_DEBUG
960 : : if (rebuild)
961 : : mlx4_mr_dump_dev(dev);
962 : : #endif
963 : 0 : }
964 : :
965 : : /**
966 : : * Callback for memory event.
967 : : *
968 : : * @param event_type
969 : : * Memory event type.
970 : : * @param addr
971 : : * Address of memory.
972 : : * @param len
973 : : * Size of memory.
974 : : */
975 : : void
976 : 0 : mlx4_mr_mem_event_cb(enum rte_mem_event event_type, const void *addr,
977 : : size_t len, void *arg __rte_unused)
978 : : {
979 : : struct mlx4_priv *priv;
980 : 0 : struct mlx4_dev_list *dev_list = &mlx4_shared_data->mem_event_cb_list;
981 : :
982 : : /* Must be called from the primary process. */
983 : : MLX4_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
984 [ # # ]: 0 : switch (event_type) {
985 : 0 : case RTE_MEM_EVENT_FREE:
986 : 0 : rte_rwlock_read_lock(&mlx4_shared_data->mem_event_rwlock);
987 : : /* Iterate all the existing mlx4 devices. */
988 [ # # ]: 0 : LIST_FOREACH(priv, dev_list, mem_event_cb)
989 : 0 : mlx4_mr_mem_event_free_cb(ETH_DEV(priv), addr, len);
990 : 0 : rte_rwlock_read_unlock(&mlx4_shared_data->mem_event_rwlock);
991 : : break;
992 : : case RTE_MEM_EVENT_ALLOC:
993 : : default:
994 : : break;
995 : : }
996 : 0 : }
997 : :
998 : : /**
999 : : * Look up address in the global MR cache table. If not found, create a new MR.
1000 : : * Insert the found/created entry to local bottom-half cache table.
1001 : : *
1002 : : * @param dev
1003 : : * Pointer to Ethernet device.
1004 : : * @param mr_ctrl
1005 : : * Pointer to per-queue MR control structure.
1006 : : * @param[out] entry
1007 : : * Pointer to returning MR cache entry, found in the global cache or newly
1008 : : * created. If failed to create one, this is not written.
1009 : : * @param addr
1010 : : * Search key.
1011 : : *
1012 : : * @return
1013 : : * Searched LKey on success, UINT32_MAX on no match.
1014 : : */
1015 : : static uint32_t
1016 : 0 : mlx4_mr_lookup_dev(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
1017 : : struct mlx4_mr_cache *entry, uintptr_t addr)
1018 : : {
1019 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
1020 : 0 : struct mlx4_mr_btree *bt = &mr_ctrl->cache_bh;
1021 : : uint16_t idx;
1022 : : uint32_t lkey;
1023 : :
1024 : : /* If local cache table is full, try to double it. */
1025 [ # # ]: 0 : if (unlikely(bt->len == bt->size))
1026 : 0 : mr_btree_expand(bt, bt->size << 1);
1027 : : /* Look up in the global cache. */
1028 : 0 : rte_rwlock_read_lock(&priv->mr.rwlock);
1029 : : lkey = mr_btree_lookup(&priv->mr.cache, &idx, addr);
1030 [ # # ]: 0 : if (lkey != UINT32_MAX) {
1031 : : /* Found. */
1032 : 0 : *entry = (*priv->mr.cache.table)[idx];
1033 : : rte_rwlock_read_unlock(&priv->mr.rwlock);
1034 : : /*
1035 : : * Update local cache. Even if it fails, return the found entry
1036 : : * to update top-half cache. Next time, this entry will be found
1037 : : * in the global cache.
1038 : : */
1039 : 0 : mr_btree_insert(bt, entry);
1040 : 0 : return lkey;
1041 : : }
1042 : : rte_rwlock_read_unlock(&priv->mr.rwlock);
1043 : : /* First time to see the address? Create a new MR. */
1044 : 0 : lkey = mlx4_mr_create(dev, entry, addr);
1045 : : /*
1046 : : * Update the local cache if successfully created a new global MR. Even
1047 : : * if failed to create one, there's no action to take in this datapath
1048 : : * code. As returning LKey is invalid, this will eventually make HW
1049 : : * fail.
1050 : : */
1051 [ # # ]: 0 : if (lkey != UINT32_MAX)
1052 : 0 : mr_btree_insert(bt, entry);
1053 : : return lkey;
1054 : : }
1055 : :
1056 : : /**
1057 : : * Bottom-half of LKey search on datapath. Firstly search in cache_bh[] and if
1058 : : * misses, search in the global MR cache table and update the new entry to
1059 : : * per-queue local caches.
1060 : : *
1061 : : * @param dev
1062 : : * Pointer to Ethernet device.
1063 : : * @param mr_ctrl
1064 : : * Pointer to per-queue MR control structure.
1065 : : * @param addr
1066 : : * Search key.
1067 : : *
1068 : : * @return
1069 : : * Searched LKey on success, UINT32_MAX on no match.
1070 : : */
1071 : : static uint32_t
1072 : 0 : mlx4_mr_addr2mr_bh(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
1073 : : uintptr_t addr)
1074 : : {
1075 : : uint32_t lkey;
1076 : : uint16_t bh_idx = 0;
1077 : : /* Victim in top-half cache to replace with new entry. */
1078 : 0 : struct mlx4_mr_cache *repl = &mr_ctrl->cache[mr_ctrl->head];
1079 : :
1080 : : /* Binary-search MR translation table. */
1081 : : lkey = mr_btree_lookup(&mr_ctrl->cache_bh, &bh_idx, addr);
1082 : : /* Update top-half cache. */
1083 [ # # ]: 0 : if (likely(lkey != UINT32_MAX)) {
1084 : 0 : *repl = (*mr_ctrl->cache_bh.table)[bh_idx];
1085 : : } else {
1086 : : /*
1087 : : * If missed in local lookup table, search in the global cache
1088 : : * and local cache_bh[] will be updated inside if possible.
1089 : : * Top-half cache entry will also be updated.
1090 : : */
1091 : 0 : lkey = mlx4_mr_lookup_dev(dev, mr_ctrl, repl, addr);
1092 [ # # ]: 0 : if (unlikely(lkey == UINT32_MAX))
1093 : : return UINT32_MAX;
1094 : : }
1095 : : /* Update the most recently used entry. */
1096 : 0 : mr_ctrl->mru = mr_ctrl->head;
1097 : : /* Point to the next victim, the oldest. */
1098 : 0 : mr_ctrl->head = (mr_ctrl->head + 1) % MLX4_MR_CACHE_N;
1099 : 0 : return lkey;
1100 : : }
1101 : :
1102 : : /**
1103 : : * Bottom-half of LKey search on Rx.
1104 : : *
1105 : : * @param rxq
1106 : : * Pointer to Rx queue structure.
1107 : : * @param addr
1108 : : * Search key.
1109 : : *
1110 : : * @return
1111 : : * Searched LKey on success, UINT32_MAX on no match.
1112 : : */
1113 : : uint32_t
1114 : 0 : mlx4_rx_addr2mr_bh(struct rxq *rxq, uintptr_t addr)
1115 : : {
1116 : 0 : struct mlx4_mr_ctrl *mr_ctrl = &rxq->mr_ctrl;
1117 : 0 : struct mlx4_priv *priv = rxq->priv;
1118 : :
1119 : 0 : return mlx4_mr_addr2mr_bh(ETH_DEV(priv), mr_ctrl, addr);
1120 : : }
1121 : :
1122 : : /**
1123 : : * Bottom-half of LKey search on Tx.
1124 : : *
1125 : : * @param txq
1126 : : * Pointer to Tx queue structure.
1127 : : * @param addr
1128 : : * Search key.
1129 : : *
1130 : : * @return
1131 : : * Searched LKey on success, UINT32_MAX on no match.
1132 : : */
1133 : : static uint32_t
1134 : : mlx4_tx_addr2mr_bh(struct txq *txq, uintptr_t addr)
1135 : : {
1136 : 0 : struct mlx4_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
1137 : 0 : struct mlx4_priv *priv = txq->priv;
1138 : :
1139 : 0 : return mlx4_mr_addr2mr_bh(ETH_DEV(priv), mr_ctrl, addr);
1140 : : }
1141 : :
1142 : : /**
1143 : : * Bottom-half of LKey search on Tx. If it can't be searched in the memseg
1144 : : * list, register the mempool of the mbuf as externally allocated memory.
1145 : : *
1146 : : * @param txq
1147 : : * Pointer to Tx queue structure.
1148 : : * @param mb
1149 : : * Pointer to mbuf.
1150 : : *
1151 : : * @return
1152 : : * Searched LKey on success, UINT32_MAX on no match.
1153 : : */
1154 : : uint32_t
1155 : 0 : mlx4_tx_mb2mr_bh(struct txq *txq, struct rte_mbuf *mb)
1156 : : {
1157 : 0 : uintptr_t addr = (uintptr_t)mb->buf_addr;
1158 : : uint32_t lkey;
1159 : :
1160 : : lkey = mlx4_tx_addr2mr_bh(txq, addr);
1161 [ # # # # ]: 0 : if (lkey == UINT32_MAX && rte_errno == ENXIO) {
1162 : : /* Mempool may have externally allocated memory. */
1163 : 0 : return mlx4_tx_update_ext_mp(txq, addr, mlx4_mb2mp(mb));
1164 : : }
1165 : : return lkey;
1166 : : }
1167 : :
1168 : : /**
1169 : : * Flush all of the local cache entries.
1170 : : *
1171 : : * @param mr_ctrl
1172 : : * Pointer to per-queue MR control structure.
1173 : : */
1174 : : void
1175 : 0 : mlx4_mr_flush_local_cache(struct mlx4_mr_ctrl *mr_ctrl)
1176 : : {
1177 : : /* Reset the most-recently-used index. */
1178 : 0 : mr_ctrl->mru = 0;
1179 : : /* Reset the linear search array. */
1180 : 0 : mr_ctrl->head = 0;
1181 : 0 : memset(mr_ctrl->cache, 0, sizeof(mr_ctrl->cache));
1182 : : /* Reset the B-tree table. */
1183 : 0 : mr_ctrl->cache_bh.len = 1;
1184 : 0 : mr_ctrl->cache_bh.overflow = 0;
1185 : : /* Update the generation number. */
1186 : 0 : mr_ctrl->cur_gen = *mr_ctrl->dev_gen_ptr;
1187 : 0 : DEBUG("mr_ctrl(%p): flushed, cur_gen=%d",
1188 : : (void *)mr_ctrl, mr_ctrl->cur_gen);
1189 : 0 : }
1190 : :
1191 : : /**
1192 : : * Called during rte_mempool_mem_iter() by mlx4_mr_update_ext_mp().
1193 : : *
1194 : : * Externally allocated chunk is registered and a MR is created for the chunk.
1195 : : * The MR object is added to the global list. If memseg list of a MR object
1196 : : * (mr->msl) is null, the MR object can be regarded as externally allocated
1197 : : * memory.
1198 : : *
1199 : : * Once external memory is registered, it should be static. If the memory is
1200 : : * freed and the virtual address range has different physical memory mapped
1201 : : * again, it may cause crash on device due to the wrong translation entry. PMD
1202 : : * can't track the free event of the external memory for now.
1203 : : */
1204 : : static void
1205 : 0 : mlx4_mr_update_ext_mp_cb(struct rte_mempool *mp, void *opaque,
1206 : : struct rte_mempool_memhdr *memhdr,
1207 : : unsigned mem_idx __rte_unused)
1208 : : {
1209 : : struct mr_update_mp_data *data = opaque;
1210 : 0 : struct rte_eth_dev *dev = data->dev;
1211 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
1212 : 0 : struct mlx4_mr_ctrl *mr_ctrl = data->mr_ctrl;
1213 : : struct mlx4_mr *mr = NULL;
1214 : 0 : uintptr_t addr = (uintptr_t)memhdr->addr;
1215 : 0 : size_t len = memhdr->len;
1216 : : struct mlx4_mr_cache entry;
1217 : : uint32_t lkey;
1218 : :
1219 : : MLX4_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
1220 : : /* If already registered, it should return. */
1221 : 0 : rte_rwlock_read_lock(&priv->mr.rwlock);
1222 : 0 : lkey = mr_lookup_dev(dev, &entry, addr);
1223 : : rte_rwlock_read_unlock(&priv->mr.rwlock);
1224 [ # # ]: 0 : if (lkey != UINT32_MAX)
1225 : 0 : return;
1226 : 0 : mr = rte_zmalloc_socket(NULL,
1227 : : RTE_ALIGN_CEIL(sizeof(*mr),
1228 : : RTE_CACHE_LINE_SIZE),
1229 : : RTE_CACHE_LINE_SIZE, mp->socket_id);
1230 [ # # ]: 0 : if (mr == NULL) {
1231 : 0 : WARN("port %u unable to allocate memory for a new MR of"
1232 : : " mempool (%s).",
1233 : : dev->data->port_id, mp->name);
1234 : 0 : data->ret = -1;
1235 : 0 : return;
1236 : : }
1237 : 0 : DEBUG("port %u register MR for chunk #%d of mempool (%s)",
1238 : : dev->data->port_id, mem_idx, mp->name);
1239 : 0 : mr->ibv_mr = mlx4_glue->reg_mr(priv->pd, (void *)addr, len,
1240 : : IBV_ACCESS_LOCAL_WRITE);
1241 [ # # ]: 0 : if (mr->ibv_mr == NULL) {
1242 : 0 : WARN("port %u fail to create a verbs MR for address (%p)",
1243 : : dev->data->port_id, (void *)addr);
1244 : 0 : rte_free(mr);
1245 : 0 : data->ret = -1;
1246 : 0 : return;
1247 : : }
1248 : 0 : mr->msl = NULL; /* Mark it is external memory. */
1249 : 0 : mr->ms_bmp = NULL;
1250 : 0 : mr->ms_n = 1;
1251 : 0 : mr->ms_bmp_n = 1;
1252 : 0 : rte_rwlock_write_lock(&priv->mr.rwlock);
1253 [ # # ]: 0 : LIST_INSERT_HEAD(&priv->mr.mr_list, mr, mr);
1254 : 0 : DEBUG("port %u MR CREATED (%p) for external memory %p:",
1255 : : dev->data->port_id, (void *)mr, (void *)addr);
1256 [ # # ]: 0 : DEBUG(" [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
1257 : : " lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
1258 : : addr, addr + len, rte_cpu_to_be_32(mr->ibv_mr->lkey),
1259 : : mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
1260 : : /* Insert to the global cache table. */
1261 : 0 : mr_insert_dev_cache(dev, mr);
1262 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
1263 : : /* Insert to the local cache table */
1264 : 0 : mlx4_mr_addr2mr_bh(dev, mr_ctrl, addr);
1265 : : }
1266 : :
1267 : : /**
1268 : : * Register MR for entire memory chunks in a Mempool having externally allocated
1269 : : * memory and fill in local cache.
1270 : : *
1271 : : * @param dev
1272 : : * Pointer to Ethernet device.
1273 : : * @param mr_ctrl
1274 : : * Pointer to per-queue MR control structure.
1275 : : * @param mp
1276 : : * Pointer to registering Mempool.
1277 : : *
1278 : : * @return
1279 : : * 0 on success, -1 on failure.
1280 : : */
1281 : : static uint32_t
1282 : : mlx4_mr_update_ext_mp(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
1283 : : struct rte_mempool *mp)
1284 : : {
1285 : 0 : struct mr_update_mp_data data = {
1286 : : .dev = dev,
1287 : : .mr_ctrl = mr_ctrl,
1288 : : .ret = 0,
1289 : : };
1290 : :
1291 : 0 : rte_mempool_mem_iter(mp, mlx4_mr_update_ext_mp_cb, &data);
1292 : 0 : return data.ret;
1293 : : }
1294 : :
1295 : : /**
1296 : : * Register MR entire memory chunks in a Mempool having externally allocated
1297 : : * memory and search LKey of the address to return.
1298 : : *
1299 : : * @param dev
1300 : : * Pointer to Ethernet device.
1301 : : * @param addr
1302 : : * Search key.
1303 : : * @param mp
1304 : : * Pointer to registering Mempool where addr belongs.
1305 : : *
1306 : : * @return
1307 : : * LKey for address on success, UINT32_MAX on failure.
1308 : : */
1309 : : uint32_t
1310 : 0 : mlx4_tx_update_ext_mp(struct txq *txq, uintptr_t addr, struct rte_mempool *mp)
1311 : : {
1312 : 0 : struct mlx4_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
1313 : 0 : struct mlx4_priv *priv = txq->priv;
1314 : :
1315 [ # # ]: 0 : if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
1316 : 0 : WARN("port %u using address (%p) from unregistered mempool"
1317 : : " having externally allocated memory"
1318 : : " in secondary process, please create mempool"
1319 : : " prior to rte_eth_dev_start()",
1320 : : PORT_ID(priv), (void *)addr);
1321 : 0 : return UINT32_MAX;
1322 : : }
1323 : 0 : mlx4_mr_update_ext_mp(ETH_DEV(priv), mr_ctrl, mp);
1324 : 0 : return mlx4_tx_addr2mr_bh(txq, addr);
1325 : : }
1326 : :
1327 : : /* Called during rte_mempool_mem_iter() by mlx4_mr_update_mp(). */
1328 : : static void
1329 : 0 : mlx4_mr_update_mp_cb(struct rte_mempool *mp __rte_unused, void *opaque,
1330 : : struct rte_mempool_memhdr *memhdr,
1331 : : unsigned mem_idx __rte_unused)
1332 : : {
1333 : : struct mr_update_mp_data *data = opaque;
1334 : : uint32_t lkey;
1335 : :
1336 : : /* Stop iteration if failed in the previous walk. */
1337 [ # # ]: 0 : if (data->ret < 0)
1338 : : return;
1339 : : /* Register address of the chunk and update local caches. */
1340 : 0 : lkey = mlx4_mr_addr2mr_bh(data->dev, data->mr_ctrl,
1341 : 0 : (uintptr_t)memhdr->addr);
1342 [ # # ]: 0 : if (lkey == UINT32_MAX)
1343 : 0 : data->ret = -1;
1344 : : }
1345 : :
1346 : : /**
1347 : : * Register entire memory chunks in a Mempool.
1348 : : *
1349 : : * @param dev
1350 : : * Pointer to Ethernet device.
1351 : : * @param mr_ctrl
1352 : : * Pointer to per-queue MR control structure.
1353 : : * @param mp
1354 : : * Pointer to registering Mempool.
1355 : : *
1356 : : * @return
1357 : : * 0 on success, -1 on failure.
1358 : : */
1359 : : int
1360 : 0 : mlx4_mr_update_mp(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
1361 : : struct rte_mempool *mp)
1362 : : {
1363 : 0 : struct mr_update_mp_data data = {
1364 : : .dev = dev,
1365 : : .mr_ctrl = mr_ctrl,
1366 : : .ret = 0,
1367 : : };
1368 : :
1369 : 0 : rte_mempool_mem_iter(mp, mlx4_mr_update_mp_cb, &data);
1370 [ # # # # ]: 0 : if (data.ret < 0 && rte_errno == ENXIO) {
1371 : : /* Mempool may have externally allocated memory. */
1372 : 0 : return mlx4_mr_update_ext_mp(dev, mr_ctrl, mp);
1373 : : }
1374 : : return data.ret;
1375 : : }
1376 : :
1377 : : #ifdef RTE_LIBRTE_MLX4_DEBUG
1378 : : /**
1379 : : * Dump all the created MRs and the global cache entries.
1380 : : *
1381 : : * @param dev
1382 : : * Pointer to Ethernet device.
1383 : : */
1384 : : void
1385 : : mlx4_mr_dump_dev(struct rte_eth_dev *dev)
1386 : : {
1387 : : struct mlx4_priv *priv = dev->data->dev_private;
1388 : : struct mlx4_mr *mr;
1389 : : int mr_n = 0;
1390 : : int chunk_n = 0;
1391 : :
1392 : : rte_rwlock_read_lock(&priv->mr.rwlock);
1393 : : /* Iterate all the existing MRs. */
1394 : : LIST_FOREACH(mr, &priv->mr.mr_list, mr) {
1395 : : unsigned int n;
1396 : :
1397 : : DEBUG("port %u MR[%u], LKey = 0x%x, ms_n = %u, ms_bmp_n = %u",
1398 : : dev->data->port_id, mr_n++,
1399 : : rte_cpu_to_be_32(mr->ibv_mr->lkey),
1400 : : mr->ms_n, mr->ms_bmp_n);
1401 : : if (mr->ms_n == 0)
1402 : : continue;
1403 : : for (n = 0; n < mr->ms_bmp_n; ) {
1404 : : struct mlx4_mr_cache ret;
1405 : :
1406 : : memset(&ret, 0, sizeof(ret));
1407 : : n = mr_find_next_chunk(mr, &ret, n);
1408 : : if (!ret.end)
1409 : : break;
1410 : : DEBUG(" chunk[%u], [0x%" PRIxPTR ", 0x%" PRIxPTR ")",
1411 : : chunk_n++, ret.start, ret.end);
1412 : : }
1413 : : }
1414 : : DEBUG("port %u dumping global cache", dev->data->port_id);
1415 : : mlx4_mr_btree_dump(&priv->mr.cache);
1416 : : rte_rwlock_read_unlock(&priv->mr.rwlock);
1417 : : }
1418 : : #endif
1419 : :
1420 : : /**
1421 : : * Release all the created MRs and resources. Remove device from memory callback
1422 : : * list.
1423 : : *
1424 : : * @param dev
1425 : : * Pointer to Ethernet device.
1426 : : */
1427 : : void
1428 : 0 : mlx4_mr_release(struct rte_eth_dev *dev)
1429 : : {
1430 : 0 : struct mlx4_priv *priv = dev->data->dev_private;
1431 : : struct mlx4_mr *mr_next;
1432 : :
1433 : : /* Remove from memory callback device list. */
1434 : 0 : rte_rwlock_write_lock(&mlx4_shared_data->mem_event_rwlock);
1435 [ # # ]: 0 : LIST_REMOVE(priv, mem_event_cb);
1436 : 0 : rte_rwlock_write_unlock(&mlx4_shared_data->mem_event_rwlock);
1437 : : #ifdef RTE_LIBRTE_MLX4_DEBUG
1438 : : mlx4_mr_dump_dev(dev);
1439 : : #endif
1440 : 0 : rte_rwlock_write_lock(&priv->mr.rwlock);
1441 : : /* Detach from MR list and move to free list. */
1442 : 0 : mr_next = LIST_FIRST(&priv->mr.mr_list);
1443 [ # # ]: 0 : while (mr_next != NULL) {
1444 : : struct mlx4_mr *mr = mr_next;
1445 : :
1446 : 0 : mr_next = LIST_NEXT(mr, mr);
1447 [ # # ]: 0 : LIST_REMOVE(mr, mr);
1448 [ # # ]: 0 : LIST_INSERT_HEAD(&priv->mr.mr_free_list, mr, mr);
1449 : : }
1450 : 0 : LIST_INIT(&priv->mr.mr_list);
1451 : : /* Free global cache. */
1452 : 0 : mlx4_mr_btree_free(&priv->mr.cache);
1453 : : rte_rwlock_write_unlock(&priv->mr.rwlock);
1454 : : /* Free all remaining MRs. */
1455 : 0 : mlx4_mr_garbage_collect(dev);
1456 : 0 : }
|