Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2016 6WIND S.A.
3 : : * Copyright 2020 Mellanox Technologies, Ltd
4 : : */
5 : : #include <stddef.h>
6 : :
7 : : #include <eal_export.h>
8 : : #include <rte_eal_memconfig.h>
9 : : #include <rte_eal_paging.h>
10 : : #include <rte_errno.h>
11 : : #include <rte_mempool.h>
12 : : #include <rte_malloc.h>
13 : : #include <rte_rwlock.h>
14 : :
15 : : #include "mlx5_glue.h"
16 : : #include "mlx5_common.h"
17 : : #include "mlx5_common_mp.h"
18 : : #include "mlx5_common_mr.h"
19 : : #include "mlx5_common_os.h"
20 : : #include "mlx5_common_log.h"
21 : : #include "mlx5_malloc.h"
22 : :
23 : : struct mr_find_contig_memsegs_data {
24 : : uintptr_t addr;
25 : : uintptr_t start;
26 : : uintptr_t end;
27 : : const struct rte_memseg_list *msl;
28 : : };
29 : :
30 : : /* Virtual memory range. */
31 : : struct mlx5_range {
32 : : uintptr_t start;
33 : : uintptr_t end;
34 : : };
35 : :
36 : : /** Memory region for a mempool. */
37 : : struct mlx5_mempool_mr {
38 : : struct mlx5_pmd_mr pmd_mr;
39 : : RTE_ATOMIC(uint32_t) refcnt; /**< Number of mempools sharing this MR. */
40 : : };
41 : :
42 : : /* Mempool registration. */
43 : : struct mlx5_mempool_reg {
44 : : LIST_ENTRY(mlx5_mempool_reg) next;
45 : : /** Registered mempool, used to designate registrations. */
46 : : struct rte_mempool *mp;
47 : : /** Memory regions for the address ranges of the mempool. */
48 : : struct mlx5_mempool_mr *mrs;
49 : : /** Number of memory regions. */
50 : : unsigned int mrs_n;
51 : : /** Whether the MR were created for external pinned memory. */
52 : : bool is_extmem;
53 : : };
54 : :
55 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mprq_buf_free_cb)
56 : : void
57 : 0 : mlx5_mprq_buf_free_cb(void *addr __rte_unused, void *opaque)
58 : : {
59 : : struct mlx5_mprq_buf *buf = opaque;
60 : :
61 [ # # ]: 0 : if (rte_atomic_load_explicit(&buf->refcnt, rte_memory_order_relaxed) == 1) {
62 [ # # ]: 0 : rte_mempool_put(buf->mp, buf);
63 [ # # ]: 0 : } else if (unlikely(rte_atomic_fetch_sub_explicit(&buf->refcnt, 1,
64 : : rte_memory_order_relaxed) - 1 == 0)) {
65 : 0 : rte_atomic_store_explicit(&buf->refcnt, 1, rte_memory_order_relaxed);
66 [ # # ]: 0 : rte_mempool_put(buf->mp, buf);
67 : : }
68 : 0 : }
69 : :
70 : : /**
71 : : * Expand B-tree table to a given size. Can't be called with holding
72 : : * memory_hotplug_lock or share_cache.rwlock due to rte_realloc().
73 : : *
74 : : * @param bt
75 : : * Pointer to B-tree structure.
76 : : * @param n
77 : : * Number of entries for expansion.
78 : : *
79 : : * @return
80 : : * 0 on success, -1 on failure.
81 : : */
82 : : static int
83 : 0 : mr_btree_expand(struct mlx5_mr_btree *bt, uint32_t n)
84 : : {
85 : : void *mem;
86 : : int ret = 0;
87 : :
88 [ # # ]: 0 : if (n <= bt->size)
89 : : return ret;
90 : : /*
91 : : * Downside of directly using rte_realloc() is that SOCKET_ID_ANY is
92 : : * used inside if there's no room to expand. Because this is a quite
93 : : * rare case and a part of very slow path, it is very acceptable.
94 : : * Initially cache_bh[] will be given practically enough space and once
95 : : * it is expanded, expansion wouldn't be needed again ever.
96 : : */
97 : 0 : mem = mlx5_realloc(bt->table, MLX5_MEM_RTE | MLX5_MEM_ZERO,
98 : : n * sizeof(struct mr_cache_entry), 0, SOCKET_ID_ANY);
99 [ # # ]: 0 : if (mem == NULL) {
100 : : /* Not an error, B-tree search will be skipped. */
101 : 0 : DRV_LOG(WARNING, "failed to expand MR B-tree (%p) table",
102 : : (void *)bt);
103 : : ret = -1;
104 : : } else {
105 : 0 : DRV_LOG(DEBUG, "expanded MR B-tree table (size=%u)", n);
106 : 0 : bt->table = mem;
107 : 0 : bt->size = n;
108 : : }
109 : : return ret;
110 : : }
111 : :
112 : : /**
113 : : * Look up LKey from given B-tree lookup table, store the last index and return
114 : : * searched LKey.
115 : : *
116 : : * @param bt
117 : : * Pointer to B-tree structure.
118 : : * @param[out] idx
119 : : * Pointer to index. Even on search failure, returns index where it stops
120 : : * searching so that index can be used when inserting a new entry.
121 : : * @param addr
122 : : * Search key.
123 : : *
124 : : * @return
125 : : * Searched LKey on success, UINT32_MAX on no match.
126 : : */
127 : : static uint32_t
128 : : mr_btree_lookup(struct mlx5_mr_btree *bt, uint32_t *idx, uintptr_t addr)
129 : : {
130 : : struct mr_cache_entry *lkp_tbl;
131 : : uint32_t n;
132 : : uint32_t base = 0;
133 : :
134 : : MLX5_ASSERT(bt != NULL);
135 : 0 : lkp_tbl = *bt->table;
136 : 0 : n = bt->len;
137 : : /* First entry must be NULL for comparison. */
138 : : MLX5_ASSERT(bt->len > 0 || (lkp_tbl[0].start == 0 &&
139 : : lkp_tbl[0].lkey == UINT32_MAX));
140 : : /* Binary search. */
141 : : do {
142 : 0 : register uint32_t delta = n >> 1;
143 : :
144 [ # # # # : 0 : if (addr < lkp_tbl[base + delta].start) {
# # # # #
# # # ]
145 : : n = delta;
146 : : } else {
147 : : base += delta;
148 : 0 : n -= delta;
149 : : }
150 [ # # # # : 0 : } while (n > 1);
# # # # #
# # # ]
151 : : MLX5_ASSERT(addr >= lkp_tbl[base].start);
152 : : *idx = base;
153 [ # # # # : 0 : if (addr < lkp_tbl[base].end)
# # # # #
# # # ]
154 : 0 : return lkp_tbl[base].lkey;
155 : : /* Not found. */
156 : : return UINT32_MAX;
157 : : }
158 : :
159 : : /**
160 : : * Insert an entry to B-tree lookup table.
161 : : *
162 : : * @param bt
163 : : * Pointer to B-tree structure.
164 : : * @param entry
165 : : * Pointer to new entry to insert.
166 : : *
167 : : * @return
168 : : * 0 on success, -1 on failure.
169 : : */
170 : : static int
171 : 0 : mr_btree_insert(struct mlx5_mr_btree *bt, struct mr_cache_entry *entry)
172 : : {
173 : : struct mr_cache_entry *lkp_tbl;
174 : : uint32_t idx = 0;
175 : : size_t shift;
176 : :
177 : : MLX5_ASSERT(bt != NULL);
178 : : MLX5_ASSERT(bt->len <= bt->size);
179 : : MLX5_ASSERT(bt->len > 0);
180 : 0 : lkp_tbl = *bt->table;
181 : : /* Find out the slot for insertion. */
182 [ # # ]: 0 : if (mr_btree_lookup(bt, &idx, entry->start) != UINT32_MAX) {
183 : 0 : DRV_LOG(DEBUG,
184 : : "abort insertion to B-tree(%p): already exist at"
185 : : " idx=%u [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
186 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
187 : : /* Already exist, return. */
188 : 0 : return 0;
189 : : }
190 : : /* Caller must ensure that there is enough place for a new entry. */
191 : : MLX5_ASSERT(bt->len < bt->size);
192 : : /* Insert entry. */
193 : 0 : ++idx;
194 : 0 : shift = (bt->len - idx) * sizeof(struct mr_cache_entry);
195 [ # # ]: 0 : if (shift)
196 : 0 : memmove(&lkp_tbl[idx + 1], &lkp_tbl[idx], shift);
197 : 0 : lkp_tbl[idx] = *entry;
198 : 0 : bt->len++;
199 : 0 : DRV_LOG(DEBUG,
200 : : "inserted B-tree(%p)[%u],"
201 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
202 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
203 : 0 : return 0;
204 : : }
205 : :
206 : : /**
207 : : * Initialize B-tree and allocate memory for lookup table.
208 : : *
209 : : * @param bt
210 : : * Pointer to B-tree structure.
211 : : * @param n
212 : : * Number of entries to allocate.
213 : : * @param socket
214 : : * NUMA socket on which memory must be allocated.
215 : : *
216 : : * @return
217 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
218 : : */
219 : : static int
220 : 0 : mlx5_mr_btree_init(struct mlx5_mr_btree *bt, int n, int socket)
221 : : {
222 [ # # ]: 0 : if (bt == NULL) {
223 : 0 : rte_errno = EINVAL;
224 : 0 : return -rte_errno;
225 : : }
226 : : MLX5_ASSERT(!bt->table && !bt->size);
227 : : memset(bt, 0, sizeof(*bt));
228 : 0 : bt->table = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO,
229 : : sizeof(struct mr_cache_entry) * n,
230 : : 0, socket);
231 [ # # ]: 0 : if (bt->table == NULL) {
232 : 0 : rte_errno = ENOMEM;
233 : 0 : DRV_LOG(DEBUG,
234 : : "failed to allocate memory for btree cache on socket "
235 : : "%d", socket);
236 : 0 : return -rte_errno;
237 : : }
238 : 0 : bt->size = n;
239 : : /* First entry must be NULL for binary search. */
240 : 0 : (*bt->table)[bt->len++] = (struct mr_cache_entry) {
241 : : .lkey = UINT32_MAX,
242 : : };
243 : 0 : DRV_LOG(DEBUG, "initialized B-tree %p with table %p",
244 : : (void *)bt, (void *)bt->table);
245 : 0 : return 0;
246 : : }
247 : :
248 : : /**
249 : : * Free B-tree resources.
250 : : *
251 : : * @param bt
252 : : * Pointer to B-tree structure.
253 : : */
254 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_btree_free)
255 : : void
256 : 0 : mlx5_mr_btree_free(struct mlx5_mr_btree *bt)
257 : : {
258 [ # # ]: 0 : if (bt == NULL)
259 : : return;
260 : 0 : DRV_LOG(DEBUG, "freeing B-tree %p with table %p",
261 : : (void *)bt, (void *)bt->table);
262 : 0 : mlx5_free(bt->table);
263 : : memset(bt, 0, sizeof(*bt));
264 : : }
265 : :
266 : : /**
267 : : * Dump all the entries in a B-tree
268 : : *
269 : : * @param bt
270 : : * Pointer to B-tree structure.
271 : : */
272 : : void
273 : 0 : mlx5_mr_btree_dump(struct mlx5_mr_btree *bt __rte_unused)
274 : : {
275 : : #ifdef RTE_LIBRTE_MLX5_DEBUG
276 : : uint32_t idx;
277 : : struct mr_cache_entry *lkp_tbl;
278 : :
279 : : if (bt == NULL)
280 : : return;
281 : : lkp_tbl = *bt->table;
282 : : for (idx = 0; idx < bt->len; ++idx) {
283 : : struct mr_cache_entry *entry = &lkp_tbl[idx];
284 : :
285 : : DRV_LOG(DEBUG, "B-tree(%p)[%u],"
286 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR ") lkey=0x%x",
287 : : (void *)bt, idx, entry->start, entry->end, entry->lkey);
288 : : }
289 : : #endif
290 : 0 : }
291 : :
292 : : /**
293 : : * Initialize per-queue MR control descriptor.
294 : : *
295 : : * @param mr_ctrl
296 : : * Pointer to MR control structure.
297 : : * @param dev_gen_ptr
298 : : * Pointer to generation number of global cache.
299 : : * @param socket
300 : : * NUMA socket on which memory must be allocated.
301 : : *
302 : : * @return
303 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
304 : : */
305 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_ctrl_init)
306 : : int
307 : 0 : mlx5_mr_ctrl_init(struct mlx5_mr_ctrl *mr_ctrl, uint32_t *dev_gen_ptr,
308 : : int socket)
309 : : {
310 [ # # ]: 0 : if (mr_ctrl == NULL) {
311 : 0 : rte_errno = EINVAL;
312 : 0 : return -rte_errno;
313 : : }
314 : : /* Save pointer of global generation number to check memory event. */
315 : 0 : mr_ctrl->dev_gen_ptr = dev_gen_ptr;
316 : : /* Initialize B-tree and allocate memory for bottom-half cache table. */
317 : 0 : return mlx5_mr_btree_init(&mr_ctrl->cache_bh, MLX5_MR_BTREE_CACHE_N,
318 : : socket);
319 : : }
320 : :
321 : : /**
322 : : * Find virtually contiguous memory chunk in a given MR.
323 : : *
324 : : * @param dev
325 : : * Pointer to MR structure.
326 : : * @param[out] entry
327 : : * Pointer to returning MR cache entry. If not found, this will not be
328 : : * updated.
329 : : * @param start_idx
330 : : * Start index of the memseg bitmap.
331 : : *
332 : : * @return
333 : : * Next index to go on lookup.
334 : : */
335 : : static int
336 : 0 : mr_find_next_chunk(struct mlx5_mr *mr, struct mr_cache_entry *entry,
337 : : int base_idx)
338 : : {
339 : : uintptr_t start = 0;
340 : : uintptr_t end = 0;
341 : : uint32_t idx = 0;
342 : :
343 : : /* MR for external memory doesn't have memseg list. */
344 [ # # ]: 0 : if (mr->msl == NULL) {
345 : : MLX5_ASSERT(mr->ms_bmp_n == 1);
346 : : MLX5_ASSERT(mr->ms_n == 1);
347 : : MLX5_ASSERT(base_idx == 0);
348 : : /*
349 : : * Can't search it from memseg list but get it directly from
350 : : * pmd_mr as there's only one chunk.
351 : : */
352 : 0 : entry->start = (uintptr_t)mr->pmd_mr.addr;
353 : 0 : entry->end = (uintptr_t)mr->pmd_mr.addr + mr->pmd_mr.len;
354 [ # # ]: 0 : entry->lkey = rte_cpu_to_be_32(mr->pmd_mr.lkey);
355 : : /* Returning 1 ends iteration. */
356 : 0 : return 1;
357 : : }
358 [ # # ]: 0 : for (idx = base_idx; idx < mr->ms_bmp_n; ++idx) {
359 [ # # ]: 0 : if (rte_bitmap_get(mr->ms_bmp, idx)) {
360 : : const struct rte_memseg_list *msl;
361 : : const struct rte_memseg *ms;
362 : :
363 : 0 : msl = mr->msl;
364 : 0 : ms = rte_fbarray_get(&msl->memseg_arr,
365 : 0 : mr->ms_base_idx + idx);
366 : : MLX5_ASSERT(msl->page_sz == ms->hugepage_sz);
367 [ # # ]: 0 : if (!start)
368 : 0 : start = ms->addr_64;
369 : 0 : end = ms->addr_64 + ms->hugepage_sz;
370 [ # # ]: 0 : } else if (start) {
371 : : /* Passed the end of a fragment. */
372 : : break;
373 : : }
374 : : }
375 [ # # ]: 0 : if (start) {
376 : : /* Found one chunk. */
377 : 0 : entry->start = start;
378 : 0 : entry->end = end;
379 [ # # ]: 0 : entry->lkey = rte_cpu_to_be_32(mr->pmd_mr.lkey);
380 : : }
381 : 0 : return idx;
382 : : }
383 : :
384 : : /**
385 : : * Insert a MR to the global B-tree cache. It may fail due to low-on-memory.
386 : : * Then, this entry will have to be searched by mr_lookup_list() in
387 : : * mlx5_mr_create() on miss.
388 : : *
389 : : * @param share_cache
390 : : * Pointer to a global shared MR cache.
391 : : * @param mr
392 : : * Pointer to MR to insert.
393 : : *
394 : : * @return
395 : : * 0 on success, -1 on failure.
396 : : */
397 : : int
398 : 0 : mlx5_mr_insert_cache(struct mlx5_mr_share_cache *share_cache,
399 : : struct mlx5_mr *mr)
400 : : {
401 : : unsigned int n;
402 : :
403 : 0 : DRV_LOG(DEBUG, "Inserting MR(%p) to global cache(%p)",
404 : : (void *)mr, (void *)share_cache);
405 [ # # ]: 0 : for (n = 0; n < mr->ms_bmp_n; ) {
406 : : struct mr_cache_entry entry;
407 : :
408 : : memset(&entry, 0, sizeof(entry));
409 : : /* Find a contiguous chunk and advance the index. */
410 : 0 : n = mr_find_next_chunk(mr, &entry, n);
411 [ # # ]: 0 : if (!entry.end)
412 : : break;
413 [ # # ]: 0 : if (mr_btree_insert(&share_cache->cache, &entry) < 0)
414 : 0 : return -1;
415 : : }
416 : : return 0;
417 : : }
418 : :
419 : : /**
420 : : * Look up address in the original global MR list.
421 : : *
422 : : * @param share_cache
423 : : * Pointer to a global shared MR cache.
424 : : * @param[out] entry
425 : : * Pointer to returning MR cache entry. If no match, this will not be updated.
426 : : * @param addr
427 : : * Search key.
428 : : *
429 : : * @return
430 : : * Found MR on match, NULL otherwise.
431 : : */
432 : : struct mlx5_mr *
433 : 0 : mlx5_mr_lookup_list(struct mlx5_mr_share_cache *share_cache,
434 : : struct mr_cache_entry *entry, uintptr_t addr)
435 : : {
436 : : struct mlx5_mr *mr;
437 : :
438 : : /* Iterate all the existing MRs. */
439 [ # # ]: 0 : LIST_FOREACH(mr, &share_cache->mr_list, mr) {
440 : : unsigned int n;
441 : :
442 [ # # ]: 0 : if (mr->ms_n == 0)
443 : 0 : continue;
444 [ # # ]: 0 : for (n = 0; n < mr->ms_bmp_n; ) {
445 : : struct mr_cache_entry ret;
446 : :
447 : : memset(&ret, 0, sizeof(ret));
448 : 0 : n = mr_find_next_chunk(mr, &ret, n);
449 [ # # # # ]: 0 : if (addr >= ret.start && addr < ret.end) {
450 : : /* Found. */
451 : 0 : *entry = ret;
452 : 0 : return mr;
453 : : }
454 : : }
455 : : }
456 : : return NULL;
457 : : }
458 : :
459 : : /**
460 : : * Look up address on global MR cache.
461 : : *
462 : : * @param share_cache
463 : : * Pointer to a global shared MR cache.
464 : : * @param[out] entry
465 : : * Pointer to returning MR cache entry. If no match, this will not be updated.
466 : : * @param addr
467 : : * Search key.
468 : : *
469 : : * @return
470 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
471 : : */
472 : : static uint32_t
473 : 0 : mlx5_mr_lookup_cache(struct mlx5_mr_share_cache *share_cache,
474 : : struct mr_cache_entry *entry, uintptr_t addr)
475 : : {
476 : : uint32_t idx;
477 : : uint32_t lkey;
478 : :
479 : : lkey = mr_btree_lookup(&share_cache->cache, &idx, addr);
480 [ # # ]: 0 : if (lkey != UINT32_MAX)
481 : 0 : *entry = (*share_cache->cache.table)[idx];
482 : : MLX5_ASSERT(lkey == UINT32_MAX || (addr >= entry->start &&
483 : : addr < entry->end));
484 : 0 : return lkey;
485 : : }
486 : :
487 : : /**
488 : : * Free MR resources. MR lock must not be held to avoid a deadlock. rte_free()
489 : : * can raise memory free event and the callback function will spin on the lock.
490 : : *
491 : : * @param mr
492 : : * Pointer to MR to free.
493 : : */
494 : : void
495 : 0 : mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb)
496 : : {
497 [ # # ]: 0 : if (mr == NULL)
498 : : return;
499 : 0 : DRV_LOG(DEBUG, "freeing MR(%p):", (void *)mr);
500 : 0 : dereg_mr_cb(&mr->pmd_mr);
501 : : rte_bitmap_free(mr->ms_bmp);
502 : 0 : mlx5_free(mr);
503 : : }
504 : :
505 : : void
506 : 0 : mlx5_mr_rebuild_cache(struct mlx5_mr_share_cache *share_cache)
507 : : {
508 : : struct mlx5_mr *mr;
509 : :
510 : 0 : DRV_LOG(DEBUG, "Rebuild dev cache[] %p", (void *)share_cache);
511 : : /* Flush cache to rebuild. */
512 : 0 : share_cache->cache.len = 1;
513 : : /* Iterate all the existing MRs. */
514 [ # # ]: 0 : LIST_FOREACH(mr, &share_cache->mr_list, mr)
515 [ # # ]: 0 : if (mlx5_mr_insert_cache(share_cache, mr) < 0)
516 : : return;
517 : : }
518 : :
519 : : /**
520 : : * Release resources of detached MR having no online entry.
521 : : *
522 : : * @param share_cache
523 : : * Pointer to a global shared MR cache.
524 : : */
525 : : static void
526 : 0 : mlx5_mr_garbage_collect(struct mlx5_mr_share_cache *share_cache)
527 : : {
528 : : struct mlx5_mr *mr_next;
529 : : struct mlx5_mr_list free_list = LIST_HEAD_INITIALIZER(free_list);
530 : :
531 : : /* Must be called from the primary process. */
532 : : MLX5_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
533 : : /*
534 : : * MR can't be freed with holding the lock because rte_free() could call
535 : : * memory free callback function. This will be a deadlock situation.
536 : : */
537 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
538 : : /* Detach the whole free list and release it after unlocking. */
539 : 0 : free_list = share_cache->mr_free_list;
540 : 0 : LIST_INIT(&share_cache->mr_free_list);
541 : : rte_rwlock_write_unlock(&share_cache->rwlock);
542 : : /* Release resources. */
543 : : mr_next = LIST_FIRST(&free_list);
544 [ # # ]: 0 : while (mr_next != NULL) {
545 : : struct mlx5_mr *mr = mr_next;
546 : :
547 : 0 : mr_next = LIST_NEXT(mr, mr);
548 : 0 : mlx5_mr_free(mr, share_cache->dereg_mr_cb);
549 : : }
550 : 0 : }
551 : :
552 : : /* Called during rte_memseg_contig_walk() by mlx5_mr_create(). */
553 : : static int
554 : 0 : mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl,
555 : : const struct rte_memseg *ms, size_t len, void *arg)
556 : : {
557 : : struct mr_find_contig_memsegs_data *data = arg;
558 : :
559 [ # # # # ]: 0 : if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)
560 : : return 0;
561 : : /* Found, save it and stop walking. */
562 : 0 : data->start = ms->addr_64;
563 : 0 : data->end = ms->addr_64 + len;
564 : 0 : data->msl = msl;
565 : 0 : return 1;
566 : : }
567 : :
568 : : /**
569 : : * Get the number of virtually-contiguous chunks in the MR.
570 : : * HW MR does not need to be already created to use this function.
571 : : *
572 : : * @param mr
573 : : * Pointer to the MR.
574 : : *
575 : : * @return
576 : : * Number of chunks.
577 : : */
578 : : static uint32_t
579 : 0 : mr_get_chunk_count(const struct mlx5_mr *mr)
580 : : {
581 : : uint32_t i, count = 0;
582 : : bool was_in_chunk = false;
583 : : bool is_in_chunk;
584 : :
585 : : /* There is only one chunk in case of external memory. */
586 [ # # ]: 0 : if (mr->msl == NULL)
587 : : return 1;
588 [ # # ]: 0 : for (i = 0; i < mr->ms_bmp_n; i++) {
589 [ # # ]: 0 : is_in_chunk = rte_bitmap_get(mr->ms_bmp, i);
590 [ # # ]: 0 : if (!was_in_chunk && is_in_chunk)
591 : 0 : count++;
592 : : was_in_chunk = is_in_chunk;
593 : : }
594 : : return count;
595 : : }
596 : :
597 : : /**
598 : : * Thread-safely expand the global MR cache to at least @p new_size slots.
599 : : *
600 : : * @param share_cache
601 : : * Shared MR cache for locking.
602 : : * @param new_size
603 : : * Desired cache size.
604 : : * @param socket
605 : : * NUMA node.
606 : : *
607 : : * @return
608 : : * 0 in success, negative on failure and rte_errno is set.
609 : : */
610 : : int
611 : 0 : mlx5_mr_expand_cache(struct mlx5_mr_share_cache *share_cache,
612 : : uint32_t size, int socket)
613 : : {
614 : 0 : struct mlx5_mr_btree cache = {0};
615 : : struct mlx5_mr_btree *bt;
616 : : struct mr_cache_entry *lkp_tbl;
617 : : int ret;
618 : :
619 : : size = rte_align32pow2(size);
620 : 0 : ret = mlx5_mr_btree_init(&cache, size, socket);
621 [ # # ]: 0 : if (ret < 0)
622 : : return ret;
623 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
624 : : bt = &share_cache->cache;
625 : 0 : lkp_tbl = *bt->table;
626 [ # # ]: 0 : if (cache.size > bt->size) {
627 [ # # ]: 0 : rte_memcpy(cache.table, lkp_tbl, bt->len * sizeof(lkp_tbl[0]));
628 : 0 : RTE_SWAP(*bt, cache);
629 : 0 : DRV_LOG(DEBUG, "Global MR cache expanded to %u slots", size);
630 : : }
631 : : rte_rwlock_write_unlock(&share_cache->rwlock);
632 : 0 : mlx5_mr_btree_free(&cache);
633 : 0 : return 0;
634 : : }
635 : :
636 : : /**
637 : : * Create a new global Memory Region (MR) for a missing virtual address.
638 : : * This API should be called on a secondary process, then a request is sent to
639 : : * the primary process in order to create a MR for the address. As the global MR
640 : : * list is on the shared memory, following LKey lookup should succeed unless the
641 : : * request fails.
642 : : *
643 : : * @param cdev
644 : : * Pointer to the mlx5 common device.
645 : : * @param share_cache
646 : : * Pointer to a global shared MR cache.
647 : : * @param[out] entry
648 : : * Pointer to returning MR cache entry, found in the global cache or newly
649 : : * created. If failed to create one, this will not be updated.
650 : : * @param addr
651 : : * Target virtual address to register.
652 : : *
653 : : * @return
654 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
655 : : */
656 : : static uint32_t
657 : 0 : mlx5_mr_create_secondary(struct mlx5_common_device *cdev,
658 : : struct mlx5_mr_share_cache *share_cache,
659 : : struct mr_cache_entry *entry, uintptr_t addr)
660 : : {
661 : : int ret;
662 : :
663 : 0 : DRV_LOG(DEBUG, "Requesting MR creation for address (%p)", (void *)addr);
664 : 0 : ret = mlx5_mp_req_mr_create(cdev, addr);
665 [ # # ]: 0 : if (ret) {
666 : 0 : DRV_LOG(DEBUG, "Fail to request MR creation for address (%p)",
667 : : (void *)addr);
668 : 0 : return UINT32_MAX;
669 : : }
670 : 0 : rte_rwlock_read_lock(&share_cache->rwlock);
671 : : /* Fill in output data. */
672 : 0 : mlx5_mr_lookup_cache(share_cache, entry, addr);
673 : : /* Lookup can't fail. */
674 : : MLX5_ASSERT(entry->lkey != UINT32_MAX);
675 : : rte_rwlock_read_unlock(&share_cache->rwlock);
676 : 0 : DRV_LOG(DEBUG, "MR CREATED by primary process for %p:\n"
677 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR "), lkey=0x%x",
678 : : (void *)addr, entry->start, entry->end, entry->lkey);
679 : 0 : return entry->lkey;
680 : : }
681 : :
682 : : /**
683 : : * Create a new global Memory Region (MR) for a missing virtual address.
684 : : * Register entire virtually contiguous memory chunk around the address.
685 : : *
686 : : * @param pd
687 : : * Pointer to pd of a device (net, regex, vdpa,...).
688 : : * @param share_cache
689 : : * Pointer to a global shared MR cache.
690 : : * @param[out] entry
691 : : * Pointer to returning MR cache entry, found in the global cache or newly
692 : : * created. If failed to create one, this will not be updated.
693 : : * @param addr
694 : : * Target virtual address to register.
695 : : * @param mr_ext_memseg_en
696 : : * Configurable flag about external memory segment enable or not.
697 : : *
698 : : * @return
699 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
700 : : */
701 : : static uint32_t
702 : 0 : mlx5_mr_create_primary(void *pd,
703 : : struct mlx5_mr_share_cache *share_cache,
704 : : struct mr_cache_entry *entry, uintptr_t addr,
705 : : unsigned int mr_ext_memseg_en)
706 : : {
707 : 0 : struct mr_find_contig_memsegs_data data = {.addr = addr, };
708 : : struct mr_find_contig_memsegs_data data_re;
709 : : const struct rte_memseg_list *msl;
710 : : const struct rte_memseg *ms;
711 : : struct mlx5_mr_btree *bt;
712 : : struct mlx5_mr *mr = NULL;
713 : : int ms_idx_shift = -1;
714 : : uint32_t bmp_size;
715 : : void *bmp_mem;
716 : : uint32_t ms_n;
717 : : uint32_t n;
718 : : uint32_t chunks_n;
719 : : size_t len;
720 : :
721 : 0 : DRV_LOG(DEBUG, "Creating a MR using address (%p)", (void *)addr);
722 : : /*
723 : : * Release detached MRs if any. This can't be called with holding either
724 : : * memory_hotplug_lock or share_cache->rwlock. MRs on the free list have
725 : : * been detached by the memory free event but it couldn't be released
726 : : * inside the callback due to deadlock. As a result, releasing resources
727 : : * is quite opportunistic.
728 : : */
729 : 0 : mlx5_mr_garbage_collect(share_cache);
730 : 0 : find_range:
731 : : /*
732 : : * If enabled, find out a contiguous virtual address chunk in use, to
733 : : * which the given address belongs, in order to register maximum range.
734 : : * In the best case where mempools are not dynamically recreated and
735 : : * '--socket-mem' is specified as an EAL option, it is very likely to
736 : : * have only one MR(LKey) per a socket and per a hugepage-size even
737 : : * though the system memory is highly fragmented. As the whole memory
738 : : * chunk will be pinned by kernel, it can't be reused unless entire
739 : : * chunk is freed from EAL.
740 : : *
741 : : * If disabled, just register one memseg (page). Then, memory
742 : : * consumption will be minimized but it may drop performance if there
743 : : * are many MRs to lookup on the datapath.
744 : : */
745 [ # # ]: 0 : if (!mr_ext_memseg_en) {
746 : 0 : data.msl = rte_mem_virt2memseg_list((void *)addr);
747 : 0 : data.start = RTE_ALIGN_FLOOR(addr, data.msl->page_sz);
748 : 0 : data.end = data.start + data.msl->page_sz;
749 [ # # ]: 0 : } else if (!rte_memseg_contig_walk(mr_find_contig_memsegs_cb, &data)) {
750 : 0 : DRV_LOG(WARNING,
751 : : "Unable to find virtually contiguous"
752 : : " chunk for address (%p)."
753 : : " rte_memseg_contig_walk() failed.", (void *)addr);
754 : 0 : rte_errno = ENXIO;
755 : 0 : goto err_nolock;
756 : : }
757 : 0 : alloc_resources:
758 : : /* Addresses must be page-aligned. */
759 : : MLX5_ASSERT(data.msl);
760 : : MLX5_ASSERT(rte_is_aligned((void *)data.start, data.msl->page_sz));
761 : : MLX5_ASSERT(rte_is_aligned((void *)data.end, data.msl->page_sz));
762 : 0 : msl = data.msl;
763 : 0 : ms = rte_mem_virt2memseg((void *)data.start, msl);
764 : 0 : len = data.end - data.start;
765 : : MLX5_ASSERT(ms);
766 : : MLX5_ASSERT(msl->page_sz == ms->hugepage_sz);
767 : : /* Number of memsegs in the range. */
768 : 0 : ms_n = len / msl->page_sz;
769 : 0 : DRV_LOG(DEBUG, "Extending %p to [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
770 : : " page_sz=0x%" PRIx64 ", ms_n=%u",
771 : : (void *)addr, data.start, data.end, msl->page_sz, ms_n);
772 : : /* Size of memory for bitmap. */
773 : 0 : bmp_size = rte_bitmap_get_memory_footprint(ms_n);
774 : 0 : mr = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO,
775 : : RTE_ALIGN_CEIL(sizeof(*mr), RTE_CACHE_LINE_SIZE) +
776 : 0 : bmp_size, RTE_CACHE_LINE_SIZE, msl->socket_id);
777 [ # # ]: 0 : if (mr == NULL) {
778 : 0 : DRV_LOG(DEBUG, "Unable to allocate memory for a new MR of"
779 : : " address (%p).", (void *)addr);
780 : 0 : rte_errno = ENOMEM;
781 : 0 : goto err_nolock;
782 : : }
783 : 0 : mr->msl = msl;
784 : : /*
785 : : * Save the index of the first memseg and initialize memseg bitmap. To
786 : : * see if a memseg of ms_idx in the memseg-list is still valid, check:
787 : : * rte_bitmap_get(mr->bmp, ms_idx - mr->ms_base_idx)
788 : : */
789 : 0 : mr->ms_base_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
790 : 0 : bmp_mem = RTE_PTR_ALIGN_CEIL(mr + 1, RTE_CACHE_LINE_SIZE);
791 : 0 : mr->ms_bmp = rte_bitmap_init(ms_n, bmp_mem, bmp_size);
792 [ # # ]: 0 : if (mr->ms_bmp == NULL) {
793 : 0 : DRV_LOG(DEBUG, "Unable to initialize bitmap for a new MR of"
794 : : " address (%p).", (void *)addr);
795 : 0 : rte_errno = EINVAL;
796 : 0 : goto err_nolock;
797 : : }
798 : : /*
799 : : * Should recheck whether the extended contiguous chunk is still valid.
800 : : * Because memory_hotplug_lock can't be held if there's any memory
801 : : * related calls in a critical path, resource allocation above can't be
802 : : * locked. If the memory has been changed at this point, try again with
803 : : * just single page. If not, go on with the big chunk atomically from
804 : : * here.
805 : : */
806 : 0 : rte_mcfg_mem_read_lock();
807 : 0 : data_re = data;
808 [ # # # # ]: 0 : if (len > msl->page_sz &&
809 : 0 : !rte_memseg_contig_walk(mr_find_contig_memsegs_cb, &data_re)) {
810 : 0 : DRV_LOG(DEBUG,
811 : : "Unable to find virtually contiguous chunk for address "
812 : : "(%p). rte_memseg_contig_walk() failed.", (void *)addr);
813 : 0 : rte_errno = ENXIO;
814 : 0 : goto err_memlock;
815 : : }
816 [ # # # # ]: 0 : if (data.start != data_re.start || data.end != data_re.end) {
817 : : /*
818 : : * The extended contiguous chunk has been changed. Try again
819 : : * with single memseg instead.
820 : : */
821 : 0 : data.start = RTE_ALIGN_FLOOR(addr, msl->page_sz);
822 : 0 : data.end = data.start + msl->page_sz;
823 : 0 : rte_mcfg_mem_read_unlock();
824 : 0 : mlx5_mr_free(mr, share_cache->dereg_mr_cb);
825 : 0 : goto alloc_resources;
826 : : }
827 : : MLX5_ASSERT(data.msl == data_re.msl);
828 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
829 : : /*
830 : : * Check the address is really missing. If other thread already created
831 : : * one or it is not found due to overflow, abort and return.
832 : : */
833 [ # # ]: 0 : if (mlx5_mr_lookup_cache(share_cache, entry, addr) != UINT32_MAX) {
834 : : /*
835 : : * Insert to the global cache table. It may fail due to
836 : : * low-on-memory. Then, this entry will have to be searched
837 : : * here again.
838 : : */
839 : 0 : mr_btree_insert(&share_cache->cache, entry);
840 : 0 : DRV_LOG(DEBUG, "Found MR for %p on final lookup, abort",
841 : : (void *)addr);
842 : : rte_rwlock_write_unlock(&share_cache->rwlock);
843 : 0 : rte_mcfg_mem_read_unlock();
844 : : /*
845 : : * Must be unlocked before calling rte_free() because
846 : : * mlx5_mr_mem_event_free_cb() can be called inside.
847 : : */
848 : 0 : mlx5_mr_free(mr, share_cache->dereg_mr_cb);
849 : 0 : return entry->lkey;
850 : : }
851 : : /*
852 : : * Trim start and end addresses for verbs MR. Set bits for registering
853 : : * memsegs but exclude already registered ones. Bitmap can be
854 : : * fragmented.
855 : : */
856 [ # # ]: 0 : for (n = 0; n < ms_n; ++n) {
857 : : uintptr_t start;
858 : : struct mr_cache_entry ret;
859 : :
860 : : memset(&ret, 0, sizeof(ret));
861 : 0 : start = data_re.start + n * msl->page_sz;
862 : : /* Exclude memsegs already registered by other MRs. */
863 [ # # ]: 0 : if (mlx5_mr_lookup_cache(share_cache, &ret, start) ==
864 : : UINT32_MAX) {
865 : : /*
866 : : * Start from the first unregistered memseg in the
867 : : * extended range.
868 : : */
869 [ # # ]: 0 : if (ms_idx_shift == -1) {
870 : 0 : mr->ms_base_idx += n;
871 : 0 : data.start = start;
872 : 0 : ms_idx_shift = n;
873 : : }
874 : 0 : data.end = start + msl->page_sz;
875 : 0 : rte_bitmap_set(mr->ms_bmp, n - ms_idx_shift);
876 : 0 : ++mr->ms_n;
877 : : }
878 : : }
879 : 0 : len = data.end - data.start;
880 : 0 : mr->ms_bmp_n = len / msl->page_sz;
881 : : MLX5_ASSERT(ms_idx_shift + mr->ms_bmp_n <= ms_n);
882 : : /*
883 : : * It is now known how many entries will be used in the global cache.
884 : : * If there is not enough, expand the cache.
885 : : * This cannot be done while holding the memory hotplug lock.
886 : : * While it is released, memory layout may change,
887 : : * so the process must be repeated from the beginning.
888 : : */
889 : : bt = &share_cache->cache;
890 : 0 : chunks_n = mr_get_chunk_count(mr);
891 [ # # ]: 0 : if (bt->len + chunks_n > bt->size) {
892 : : struct mlx5_common_device *cdev;
893 : : uint32_t size;
894 : :
895 : 0 : size = bt->size + chunks_n;
896 : : MLX5_ASSERT(size > bt->size);
897 : 0 : cdev = container_of(share_cache, struct mlx5_common_device,
898 : : mr_scache);
899 : : rte_rwlock_write_unlock(&share_cache->rwlock);
900 : 0 : rte_mcfg_mem_read_unlock();
901 [ # # ]: 0 : if (mlx5_mr_expand_cache(share_cache, size,
902 : 0 : cdev->dev->numa_node) < 0) {
903 : 0 : DRV_LOG(ERR, "Failed to expand global MR cache to %u slots",
904 : : size);
905 : 0 : goto err_nolock;
906 : : }
907 : 0 : goto find_range;
908 : : }
909 : : /*
910 : : * Finally create an MR for the memory chunk. Verbs: ibv_reg_mr() can
911 : : * be called with holding the memory lock because it doesn't use
912 : : * mlx5_alloc_buf_extern() which eventually calls rte_malloc_socket()
913 : : * through mlx5_alloc_verbs_buf().
914 : : */
915 : 0 : share_cache->reg_mr_cb(pd, (void *)data.start, len, &mr->pmd_mr);
916 [ # # ]: 0 : if (mr->pmd_mr.obj == NULL) {
917 : 0 : DRV_LOG(DEBUG, "Fail to create an MR for address (%p)",
918 : : (void *)addr);
919 : 0 : rte_errno = EINVAL;
920 : 0 : goto err_mrlock;
921 : : }
922 : : MLX5_ASSERT((uintptr_t)mr->pmd_mr.addr == data.start);
923 : : MLX5_ASSERT(mr->pmd_mr.len);
924 [ # # ]: 0 : LIST_INSERT_HEAD(&share_cache->mr_list, mr, mr);
925 [ # # ]: 0 : DRV_LOG(DEBUG, "MR CREATED (%p) for %p:\n"
926 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
927 : : " lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
928 : : (void *)mr, (void *)addr, data.start, data.end,
929 : : rte_cpu_to_be_32(mr->pmd_mr.lkey),
930 : : mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
931 : : /* Insert to the global cache table. */
932 : 0 : mlx5_mr_insert_cache(share_cache, mr);
933 : : /* Fill in output data. */
934 : 0 : mlx5_mr_lookup_cache(share_cache, entry, addr);
935 : : /* Lookup can't fail. */
936 : : MLX5_ASSERT(entry->lkey != UINT32_MAX);
937 : : rte_rwlock_write_unlock(&share_cache->rwlock);
938 : 0 : rte_mcfg_mem_read_unlock();
939 : 0 : return entry->lkey;
940 : : err_mrlock:
941 : : rte_rwlock_write_unlock(&share_cache->rwlock);
942 : 0 : err_memlock:
943 : 0 : rte_mcfg_mem_read_unlock();
944 : 0 : err_nolock:
945 : : /*
946 : : * In case of error, as this can be called in a datapath, a warning
947 : : * message per an error is preferable instead. Must be unlocked before
948 : : * calling rte_free() because mlx5_mr_mem_event_free_cb() can be called
949 : : * inside.
950 : : */
951 : 0 : mlx5_mr_free(mr, share_cache->dereg_mr_cb);
952 : 0 : return UINT32_MAX;
953 : : }
954 : :
955 : : /**
956 : : * Create a new global Memory Region (MR) for a missing virtual address.
957 : : * This can be called from primary and secondary process.
958 : : *
959 : : * @param cdev
960 : : * Pointer to the mlx5 common device.
961 : : * @param share_cache
962 : : * Pointer to a global shared MR cache.
963 : : * @param[out] entry
964 : : * Pointer to returning MR cache entry, found in the global cache or newly
965 : : * created. If failed to create one, this will not be updated.
966 : : * @param addr
967 : : * Target virtual address to register.
968 : : *
969 : : * @return
970 : : * Searched LKey on success, UINT32_MAX on failure and rte_errno is set.
971 : : */
972 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_create)
973 : : uint32_t
974 : 0 : mlx5_mr_create(struct mlx5_common_device *cdev,
975 : : struct mlx5_mr_share_cache *share_cache,
976 : : struct mr_cache_entry *entry, uintptr_t addr)
977 : : {
978 : : uint32_t ret = 0;
979 : :
980 [ # # # ]: 0 : switch (rte_eal_process_type()) {
981 : 0 : case RTE_PROC_PRIMARY:
982 : 0 : ret = mlx5_mr_create_primary(cdev->pd, share_cache, entry, addr,
983 : 0 : cdev->config.mr_ext_memseg_en);
984 : 0 : break;
985 : 0 : case RTE_PROC_SECONDARY:
986 : 0 : ret = mlx5_mr_create_secondary(cdev, share_cache, entry, addr);
987 : 0 : break;
988 : : default:
989 : : break;
990 : : }
991 : 0 : return ret;
992 : : }
993 : :
994 : : /**
995 : : * Look up address in the global MR cache table. If not found, create a new MR.
996 : : * Insert the found/created entry to local bottom-half cache table.
997 : : *
998 : : * @param mr_ctrl
999 : : * Pointer to per-queue MR control structure.
1000 : : * @param[out] entry
1001 : : * Pointer to returning MR cache entry, found in the global cache or newly
1002 : : * created. If failed to create one, this is not written.
1003 : : * @param addr
1004 : : * Search key.
1005 : : *
1006 : : * @return
1007 : : * Searched LKey on success, UINT32_MAX on no match.
1008 : : */
1009 : : static uint32_t
1010 : 0 : mr_lookup_caches(struct mlx5_mr_ctrl *mr_ctrl,
1011 : : struct mr_cache_entry *entry, uintptr_t addr)
1012 : : {
1013 : : struct mlx5_mr_share_cache *share_cache =
1014 : 0 : container_of(mr_ctrl->dev_gen_ptr, struct mlx5_mr_share_cache,
1015 : : dev_gen);
1016 : : struct mlx5_common_device *cdev =
1017 : 0 : container_of(share_cache, struct mlx5_common_device, mr_scache);
1018 : 0 : struct mlx5_mr_btree *bt = &mr_ctrl->cache_bh;
1019 : : uint32_t lkey;
1020 : : uint32_t idx;
1021 : :
1022 : : /* If local cache table is full, try to double it. */
1023 [ # # ]: 0 : if (unlikely(bt->len == bt->size))
1024 : 0 : mr_btree_expand(bt, bt->size << 1);
1025 : : /* Look up in the global cache. */
1026 : 0 : rte_rwlock_read_lock(&share_cache->rwlock);
1027 : : lkey = mr_btree_lookup(&share_cache->cache, &idx, addr);
1028 [ # # ]: 0 : if (lkey != UINT32_MAX) {
1029 : : /* Found. */
1030 : 0 : *entry = (*share_cache->cache.table)[idx];
1031 : : rte_rwlock_read_unlock(&share_cache->rwlock);
1032 : : /*
1033 : : * Update local cache. Even if it fails, return the found entry
1034 : : * to update top-half cache. Next time, this entry will be found
1035 : : * in the global cache.
1036 : : */
1037 : 0 : mr_btree_insert(bt, entry);
1038 : 0 : return lkey;
1039 : : }
1040 : : rte_rwlock_read_unlock(&share_cache->rwlock);
1041 : : /* First time to see the address? Create a new MR. */
1042 : 0 : lkey = mlx5_mr_create(cdev, share_cache, entry, addr);
1043 : : /*
1044 : : * Update the local cache if successfully created a new global MR. Even
1045 : : * if failed to create one, there's no action to take in this datapath
1046 : : * code. As returning LKey is invalid, this will eventually make HW
1047 : : * fail.
1048 : : */
1049 [ # # ]: 0 : if (lkey != UINT32_MAX)
1050 : 0 : mr_btree_insert(bt, entry);
1051 : : return lkey;
1052 : : }
1053 : :
1054 : : /**
1055 : : * Bottom-half of LKey search on datapath. First search in cache_bh[] and if
1056 : : * misses, search in the global MR cache table and update the new entry to
1057 : : * per-queue local caches.
1058 : : *
1059 : : * @param mr_ctrl
1060 : : * Pointer to per-queue MR control structure.
1061 : : * @param addr
1062 : : * Search key.
1063 : : *
1064 : : * @return
1065 : : * Searched LKey on success, UINT32_MAX on no match.
1066 : : */
1067 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_addr2mr_bh)
1068 : : uint32_t
1069 : 0 : mlx5_mr_addr2mr_bh(struct mlx5_mr_ctrl *mr_ctrl, uintptr_t addr)
1070 : : {
1071 : : uint32_t lkey;
1072 : : uint32_t bh_idx = 0;
1073 : : /* Victim in top-half cache to replace with new entry. */
1074 : 0 : struct mr_cache_entry *repl = &mr_ctrl->cache[mr_ctrl->head];
1075 : :
1076 : : /* Binary-search MR translation table. */
1077 : : lkey = mr_btree_lookup(&mr_ctrl->cache_bh, &bh_idx, addr);
1078 : : /* Update top-half cache. */
1079 [ # # ]: 0 : if (likely(lkey != UINT32_MAX)) {
1080 : 0 : *repl = (*mr_ctrl->cache_bh.table)[bh_idx];
1081 : : } else {
1082 : : /*
1083 : : * If missed in local lookup table, search in the global cache
1084 : : * and local cache_bh[] will be updated inside if possible.
1085 : : * Top-half cache entry will also be updated.
1086 : : */
1087 : 0 : lkey = mr_lookup_caches(mr_ctrl, repl, addr);
1088 [ # # ]: 0 : if (unlikely(lkey == UINT32_MAX))
1089 : : return UINT32_MAX;
1090 : : }
1091 : : /* Update the most recently used entry. */
1092 : 0 : mr_ctrl->mru = mr_ctrl->head;
1093 : : /* Point to the next victim, the oldest. */
1094 : 0 : mr_ctrl->head = (mr_ctrl->head + 1) % MLX5_MR_CACHE_N;
1095 : 0 : return lkey;
1096 : : }
1097 : :
1098 : : /**
1099 : : * Release all the created MRs and resources on global MR cache of a device
1100 : : * list.
1101 : : *
1102 : : * @param share_cache
1103 : : * Pointer to a global shared MR cache.
1104 : : */
1105 : : void
1106 : 0 : mlx5_mr_release_cache(struct mlx5_mr_share_cache *share_cache)
1107 : : {
1108 : : struct mlx5_mr *mr_next;
1109 : :
1110 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
1111 : : /* Detach from MR list and move to free list. */
1112 : 0 : mr_next = LIST_FIRST(&share_cache->mr_list);
1113 [ # # ]: 0 : while (mr_next != NULL) {
1114 : : struct mlx5_mr *mr = mr_next;
1115 : :
1116 : 0 : mr_next = LIST_NEXT(mr, mr);
1117 [ # # ]: 0 : LIST_REMOVE(mr, mr);
1118 [ # # ]: 0 : LIST_INSERT_HEAD(&share_cache->mr_free_list, mr, mr);
1119 : : }
1120 : 0 : LIST_INIT(&share_cache->mr_list);
1121 : : /* Free global cache. */
1122 : 0 : mlx5_mr_btree_free(&share_cache->cache);
1123 : : rte_rwlock_write_unlock(&share_cache->rwlock);
1124 : : /* Free all remaining MRs. */
1125 : 0 : mlx5_mr_garbage_collect(share_cache);
1126 : 0 : }
1127 : :
1128 : : /**
1129 : : * Initialize global MR cache of a device.
1130 : : *
1131 : : * @param share_cache
1132 : : * Pointer to a global shared MR cache.
1133 : : * @param socket
1134 : : * NUMA socket on which memory must be allocated.
1135 : : *
1136 : : * @return
1137 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
1138 : : */
1139 : : int
1140 : 0 : mlx5_mr_create_cache(struct mlx5_mr_share_cache *share_cache, int socket)
1141 : : {
1142 : : /* Set the reg_mr and dereg_mr callback functions */
1143 : 0 : mlx5_os_set_reg_mr_cb(&share_cache->reg_mr_cb,
1144 : : &share_cache->dereg_mr_cb);
1145 : : rte_rwlock_init(&share_cache->rwlock);
1146 : : rte_rwlock_init(&share_cache->mprwlock);
1147 : : /* Initialize B-tree and allocate memory for global MR cache table. */
1148 : 0 : return mlx5_mr_btree_init(&share_cache->cache,
1149 : : MLX5_MR_BTREE_CACHE_N * 2, socket);
1150 : : }
1151 : :
1152 : : /**
1153 : : * Flush all of the local cache entries.
1154 : : *
1155 : : * @param mr_ctrl
1156 : : * Pointer to per-queue MR local cache.
1157 : : */
1158 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_flush_local_cache)
1159 : : void
1160 : 0 : mlx5_mr_flush_local_cache(struct mlx5_mr_ctrl *mr_ctrl)
1161 : : {
1162 : : /* Reset the most-recently-used index. */
1163 : 0 : mr_ctrl->mru = 0;
1164 : : /* Reset the linear search array. */
1165 : 0 : mr_ctrl->head = 0;
1166 : 0 : memset(mr_ctrl->cache, 0, sizeof(mr_ctrl->cache));
1167 : : /* Reset the B-tree table. */
1168 : 0 : mr_ctrl->cache_bh.len = 1;
1169 : : /* Update the generation number. */
1170 : 0 : mr_ctrl->cur_gen = *mr_ctrl->dev_gen_ptr;
1171 : 0 : DRV_LOG(DEBUG, "mr_ctrl(%p): flushed, cur_gen=%d",
1172 : : (void *)mr_ctrl, mr_ctrl->cur_gen);
1173 : 0 : }
1174 : :
1175 : : /**
1176 : : * Creates a memory region for external memory, that is memory which is not
1177 : : * part of the DPDK memory segments.
1178 : : *
1179 : : * @param pd
1180 : : * Pointer to pd of a device (net, regex, vdpa,...).
1181 : : * @param addr
1182 : : * Starting virtual address of memory.
1183 : : * @param len
1184 : : * Length of memory segment being mapped.
1185 : : * @param socked_id
1186 : : * Socket to allocate heap memory for the control structures.
1187 : : *
1188 : : * @return
1189 : : * Pointer to MR structure on success, NULL otherwise.
1190 : : */
1191 : : struct mlx5_mr *
1192 : 0 : mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
1193 : : mlx5_reg_mr_t reg_mr_cb)
1194 : : {
1195 : : struct mlx5_mr *mr = NULL;
1196 : :
1197 : 0 : mr = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO,
1198 : : RTE_ALIGN_CEIL(sizeof(*mr), RTE_CACHE_LINE_SIZE),
1199 : : RTE_CACHE_LINE_SIZE, socket_id);
1200 [ # # ]: 0 : if (mr == NULL)
1201 : : return NULL;
1202 : 0 : reg_mr_cb(pd, (void *)addr, len, &mr->pmd_mr);
1203 [ # # ]: 0 : if (mr->pmd_mr.obj == NULL) {
1204 : 0 : DRV_LOG(WARNING,
1205 : : "Fail to create MR for address (%p)",
1206 : : (void *)addr);
1207 : 0 : mlx5_free(mr);
1208 : 0 : return NULL;
1209 : : }
1210 : 0 : mr->msl = NULL; /* Mark it is external memory. */
1211 : 0 : mr->ms_bmp = NULL;
1212 : 0 : mr->ms_n = 1;
1213 : 0 : mr->ms_bmp_n = 1;
1214 [ # # ]: 0 : DRV_LOG(DEBUG,
1215 : : "MR CREATED (%p) for external memory %p:\n"
1216 : : " [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
1217 : : " lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
1218 : : (void *)mr, (void *)addr,
1219 : : addr, addr + len, rte_cpu_to_be_32(mr->pmd_mr.lkey),
1220 : : mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
1221 : 0 : return mr;
1222 : : }
1223 : :
1224 : : /**
1225 : : * Callback for memory free event. Iterate freed memsegs and check whether it
1226 : : * belongs to an existing MR. If found, clear the bit from bitmap of MR. As a
1227 : : * result, the MR would be fragmented. If it becomes empty, the MR will be freed
1228 : : * later by mlx5_mr_garbage_collect(). Even if this callback is called from a
1229 : : * secondary process, the garbage collector will be called in primary process
1230 : : * as the secondary process can't call mlx5_mr_create().
1231 : : *
1232 : : * The global cache must be rebuilt if there's any change and this event has to
1233 : : * be propagated to dataplane threads to flush the local caches.
1234 : : *
1235 : : * @param share_cache
1236 : : * Pointer to a global shared MR cache.
1237 : : * @param ibdev_name
1238 : : * Name of ibv device.
1239 : : * @param addr
1240 : : * Address of freed memory.
1241 : : * @param len
1242 : : * Size of freed memory.
1243 : : */
1244 : : void
1245 : 0 : mlx5_free_mr_by_addr(struct mlx5_mr_share_cache *share_cache,
1246 : : const char *ibdev_name, const void *addr, size_t len)
1247 : : {
1248 : : const struct rte_memseg_list *msl;
1249 : : struct mlx5_mr *mr;
1250 : : int ms_n;
1251 : : int i;
1252 : : int rebuild = 0;
1253 : :
1254 : 0 : DRV_LOG(DEBUG, "device %s free callback: addr=%p, len=%zu",
1255 : : ibdev_name, addr, len);
1256 : 0 : msl = rte_mem_virt2memseg_list(addr);
1257 : : /* addr and len must be page-aligned. */
1258 : : MLX5_ASSERT((uintptr_t)addr ==
1259 : : RTE_ALIGN((uintptr_t)addr, msl->page_sz));
1260 : : MLX5_ASSERT(len == RTE_ALIGN(len, msl->page_sz));
1261 : 0 : ms_n = len / msl->page_sz;
1262 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
1263 : : /* Clear bits of freed memsegs from MR. */
1264 [ # # ]: 0 : for (i = 0; i < ms_n; ++i) {
1265 : : const struct rte_memseg *ms;
1266 : : struct mr_cache_entry entry;
1267 : : uintptr_t start;
1268 : : int ms_idx;
1269 : : uint32_t pos;
1270 : :
1271 : : /* Find MR having this memseg. */
1272 : 0 : start = (uintptr_t)addr + i * msl->page_sz;
1273 : 0 : mr = mlx5_mr_lookup_list(share_cache, &entry, start);
1274 [ # # ]: 0 : if (mr == NULL)
1275 : 0 : continue;
1276 : : MLX5_ASSERT(mr->msl); /* Can't be external memory. */
1277 : 0 : ms = rte_mem_virt2memseg((void *)start, msl);
1278 : : MLX5_ASSERT(ms != NULL);
1279 : : MLX5_ASSERT(msl->page_sz == ms->hugepage_sz);
1280 : 0 : ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
1281 : 0 : pos = ms_idx - mr->ms_base_idx;
1282 : : MLX5_ASSERT(rte_bitmap_get(mr->ms_bmp, pos));
1283 : : MLX5_ASSERT(pos < mr->ms_bmp_n);
1284 : 0 : DRV_LOG(DEBUG, "device %s MR(%p): clear bitmap[%u] for addr %p",
1285 : : ibdev_name, (void *)mr, pos, (void *)start);
1286 : 0 : rte_bitmap_clear(mr->ms_bmp, pos);
1287 [ # # ]: 0 : if (--mr->ms_n == 0) {
1288 [ # # ]: 0 : LIST_REMOVE(mr, mr);
1289 [ # # ]: 0 : LIST_INSERT_HEAD(&share_cache->mr_free_list, mr, mr);
1290 : 0 : DRV_LOG(DEBUG, "device %s remove MR(%p) from list",
1291 : : ibdev_name, (void *)mr);
1292 : : }
1293 : : /*
1294 : : * MR is fragmented or will be freed. the global cache must be
1295 : : * rebuilt.
1296 : : */
1297 : : rebuild = 1;
1298 : : }
1299 [ # # ]: 0 : if (rebuild) {
1300 : 0 : mlx5_mr_rebuild_cache(share_cache);
1301 : : /*
1302 : : * No explicit wmb is needed after updating dev_gen due to
1303 : : * store-release ordering in unlock that provides the
1304 : : * implicit barrier at the software visible level.
1305 : : */
1306 : 0 : ++share_cache->dev_gen;
1307 : 0 : DRV_LOG(DEBUG, "broadcasting local cache flush, gen=%d",
1308 : : share_cache->dev_gen);
1309 : : }
1310 : : rte_rwlock_write_unlock(&share_cache->rwlock);
1311 : 0 : }
1312 : :
1313 : : /**
1314 : : * Dump all the created MRs and the global cache entries.
1315 : : *
1316 : : * @param share_cache
1317 : : * Pointer to a global shared MR cache.
1318 : : */
1319 : : void
1320 : 0 : mlx5_mr_dump_cache(struct mlx5_mr_share_cache *share_cache __rte_unused)
1321 : : {
1322 : : #ifdef RTE_LIBRTE_MLX5_DEBUG
1323 : : struct mlx5_mr *mr;
1324 : : int mr_n = 0;
1325 : : int chunk_n = 0;
1326 : :
1327 : : rte_rwlock_read_lock(&share_cache->rwlock);
1328 : : /* Iterate all the existing MRs. */
1329 : : LIST_FOREACH(mr, &share_cache->mr_list, mr) {
1330 : : unsigned int n;
1331 : :
1332 : : DRV_LOG(DEBUG, "MR[%u], LKey = 0x%x, ms_n = %u, ms_bmp_n = %u",
1333 : : mr_n++, rte_cpu_to_be_32(mr->pmd_mr.lkey),
1334 : : mr->ms_n, mr->ms_bmp_n);
1335 : : if (mr->ms_n == 0)
1336 : : continue;
1337 : : for (n = 0; n < mr->ms_bmp_n; ) {
1338 : : struct mr_cache_entry ret = { 0, };
1339 : :
1340 : : n = mr_find_next_chunk(mr, &ret, n);
1341 : : if (!ret.end)
1342 : : break;
1343 : : DRV_LOG(DEBUG,
1344 : : " chunk[%u], [0x%" PRIxPTR ", 0x%" PRIxPTR ")",
1345 : : chunk_n++, ret.start, ret.end);
1346 : : }
1347 : : }
1348 : : DRV_LOG(DEBUG, "Dumping global cache %p", (void *)share_cache);
1349 : : mlx5_mr_btree_dump(&share_cache->cache);
1350 : : rte_rwlock_read_unlock(&share_cache->rwlock);
1351 : : #endif
1352 : 0 : }
1353 : :
1354 : : static int
1355 : 0 : mlx5_range_compare_start(const void *lhs, const void *rhs)
1356 : : {
1357 : : const struct mlx5_range *r1 = lhs, *r2 = rhs;
1358 : :
1359 [ # # ]: 0 : if (r1->start > r2->start)
1360 : : return 1;
1361 [ # # ]: 0 : else if (r1->start < r2->start)
1362 : 0 : return -1;
1363 : : return 0;
1364 : : }
1365 : :
1366 : : static void
1367 : 0 : mlx5_range_from_mempool_chunk(struct rte_mempool *mp, void *opaque,
1368 : : struct rte_mempool_memhdr *memhdr,
1369 : : unsigned int idx)
1370 : : {
1371 : 0 : struct mlx5_range *ranges = opaque, *range = &ranges[idx];
1372 : 0 : uintptr_t start = (uintptr_t)memhdr->addr;
1373 : 0 : uint64_t page_size = rte_mem_page_size();
1374 : :
1375 : : RTE_SET_USED(mp);
1376 : 0 : range->start = RTE_ALIGN_FLOOR(start, page_size);
1377 : 0 : range->end = RTE_ALIGN_CEIL(start + memhdr->len, page_size);
1378 : 0 : }
1379 : :
1380 : : /**
1381 : : * Collect page-aligned memory ranges of the mempool.
1382 : : */
1383 : : static int
1384 : 0 : mlx5_mempool_get_chunks(struct rte_mempool *mp, struct mlx5_range **out,
1385 : : unsigned int *out_n)
1386 : : {
1387 : : unsigned int n;
1388 : :
1389 : 0 : DRV_LOG(DEBUG, "Collecting chunks of regular mempool %s", mp->name);
1390 : 0 : n = mp->nb_mem_chunks;
1391 : 0 : *out = calloc(n, sizeof(**out));
1392 [ # # ]: 0 : if (*out == NULL)
1393 : : return -1;
1394 : 0 : rte_mempool_mem_iter(mp, mlx5_range_from_mempool_chunk, *out);
1395 : 0 : *out_n = n;
1396 : 0 : return 0;
1397 : : }
1398 : :
1399 : : struct mlx5_mempool_get_extmem_data {
1400 : : struct mlx5_range *heap;
1401 : : unsigned int heap_size;
1402 : : int ret;
1403 : : };
1404 : :
1405 : : static void
1406 : 0 : mlx5_mempool_get_extmem_cb(struct rte_mempool *mp, void *opaque,
1407 : : void *obj, unsigned int obj_idx)
1408 : : {
1409 : : struct mlx5_mempool_get_extmem_data *data = opaque;
1410 : : struct rte_mbuf *mbuf = obj;
1411 : 0 : uintptr_t addr = (uintptr_t)mbuf->buf_addr;
1412 : : struct mlx5_range *seg, *heap;
1413 : : struct rte_memseg_list *msl;
1414 : : size_t page_size;
1415 : : uintptr_t page_start;
1416 : 0 : unsigned int pos = 0, len = data->heap_size, delta;
1417 : :
1418 : : RTE_SET_USED(mp);
1419 : : RTE_SET_USED(obj_idx);
1420 [ # # ]: 0 : if (data->ret < 0)
1421 : : return;
1422 : : /* Binary search for an already visited page. */
1423 [ # # ]: 0 : while (len > 1) {
1424 : 0 : delta = len / 2;
1425 [ # # ]: 0 : if (addr < data->heap[pos + delta].start) {
1426 : : len = delta;
1427 : : } else {
1428 : : pos += delta;
1429 : 0 : len -= delta;
1430 : : }
1431 : : }
1432 [ # # ]: 0 : if (data->heap != NULL) {
1433 : 0 : seg = &data->heap[pos];
1434 [ # # # # ]: 0 : if (seg->start <= addr && addr < seg->end)
1435 : : return;
1436 : : }
1437 : : /* Determine the page boundaries and remember them. */
1438 : 0 : heap = realloc(data->heap, sizeof(heap[0]) * (data->heap_size + 1));
1439 [ # # ]: 0 : if (heap == NULL) {
1440 : 0 : free(data->heap);
1441 : 0 : data->heap = NULL;
1442 : 0 : data->ret = -1;
1443 : 0 : return;
1444 : : }
1445 : 0 : data->heap = heap;
1446 : 0 : data->heap_size++;
1447 : 0 : seg = &heap[data->heap_size - 1];
1448 : 0 : msl = rte_mem_virt2memseg_list((void *)addr);
1449 [ # # ]: 0 : page_size = msl != NULL ? msl->page_sz : rte_mem_page_size();
1450 : 0 : page_start = RTE_PTR_ALIGN_FLOOR(addr, page_size);
1451 : 0 : seg->start = page_start;
1452 : 0 : seg->end = page_start + page_size;
1453 : : /* Maintain the heap order. */
1454 : 0 : qsort(data->heap, data->heap_size, sizeof(heap[0]),
1455 : : mlx5_range_compare_start);
1456 : : }
1457 : :
1458 : : /**
1459 : : * Recover pages of external memory as close as possible
1460 : : * for a mempool with RTE_PKTMBUF_POOL_PINNED_EXT_BUF.
1461 : : * Pages are stored in a heap for efficient search, for mbufs are many.
1462 : : */
1463 : : static int
1464 : 0 : mlx5_mempool_get_extmem(struct rte_mempool *mp, struct mlx5_range **out,
1465 : : unsigned int *out_n)
1466 : : {
1467 : : struct mlx5_mempool_get_extmem_data data;
1468 : :
1469 : 0 : DRV_LOG(DEBUG, "Recovering external pinned pages of mempool %s",
1470 : : mp->name);
1471 : : memset(&data, 0, sizeof(data));
1472 : 0 : rte_mempool_obj_iter(mp, mlx5_mempool_get_extmem_cb, &data);
1473 : 0 : *out = data.heap;
1474 : 0 : *out_n = data.heap_size;
1475 : 0 : return data.ret;
1476 : : }
1477 : :
1478 : : /**
1479 : : * Get VA-contiguous ranges of the mempool memory.
1480 : : * Each range start and end is aligned to the system page size.
1481 : : *
1482 : : * @param[in] mp
1483 : : * Analyzed mempool.
1484 : : * @param[in] is_extmem
1485 : : * Whether the pool is contains only external pinned buffers.
1486 : : * @param[out] out
1487 : : * Receives the ranges, caller must release it with free().
1488 : : * @param[out] out_n
1489 : : * Receives the number of @p out elements.
1490 : : *
1491 : : * @return
1492 : : * 0 on success, (-1) on failure.
1493 : : */
1494 : : static int
1495 : 0 : mlx5_get_mempool_ranges(struct rte_mempool *mp, bool is_extmem,
1496 : : struct mlx5_range **out, unsigned int *out_n)
1497 : : {
1498 : : struct mlx5_range *chunks;
1499 : : unsigned int chunks_n, contig_n, i;
1500 : : int ret;
1501 : :
1502 : : /* Collect the pool underlying memory. */
1503 [ # # ]: 0 : ret = is_extmem ? mlx5_mempool_get_extmem(mp, &chunks, &chunks_n) :
1504 : 0 : mlx5_mempool_get_chunks(mp, &chunks, &chunks_n);
1505 [ # # ]: 0 : if (ret < 0)
1506 : : return ret;
1507 : : /* Merge adjacent chunks and place them at the beginning. */
1508 : 0 : qsort(chunks, chunks_n, sizeof(chunks[0]), mlx5_range_compare_start);
1509 : : contig_n = 1;
1510 [ # # ]: 0 : for (i = 1; i < chunks_n; i++)
1511 [ # # ]: 0 : if (chunks[i - 1].end != chunks[i].start) {
1512 : 0 : chunks[contig_n - 1].end = chunks[i - 1].end;
1513 : 0 : chunks[contig_n] = chunks[i];
1514 : 0 : contig_n++;
1515 : : }
1516 : : /* Extend the last contiguous chunk to the end of the mempool. */
1517 : 0 : chunks[contig_n - 1].end = chunks[i - 1].end;
1518 : 0 : *out = chunks;
1519 : 0 : *out_n = contig_n;
1520 : 0 : return 0;
1521 : : }
1522 : :
1523 : : /**
1524 : : * Analyze mempool memory to select memory ranges to register.
1525 : : *
1526 : : * @param[in] mp
1527 : : * Mempool to analyze.
1528 : : * @param[in] is_extmem
1529 : : * Whether the pool is contains only external pinned buffers.
1530 : : * @param[out] out
1531 : : * Receives memory ranges to register, aligned to the system page size.
1532 : : * The caller must release them with free().
1533 : : * @param[out] out_n
1534 : : * Receives the number of @p out items.
1535 : : * @param[out] share_hugepage
1536 : : * Receives True if the entire pool resides within a single hugepage.
1537 : : *
1538 : : * @return
1539 : : * 0 on success, (-1) on failure.
1540 : : */
1541 : : static int
1542 : 0 : mlx5_mempool_reg_analyze(struct rte_mempool *mp, bool is_extmem,
1543 : : struct mlx5_range **out, unsigned int *out_n,
1544 : : bool *share_hugepage)
1545 : : {
1546 : 0 : struct mlx5_range *ranges = NULL;
1547 : 0 : unsigned int i, ranges_n = 0;
1548 : : struct rte_memseg_list *msl;
1549 : :
1550 [ # # ]: 0 : if (mlx5_get_mempool_ranges(mp, is_extmem, &ranges, &ranges_n) < 0) {
1551 : 0 : DRV_LOG(ERR, "Cannot get address ranges for mempool %s",
1552 : : mp->name);
1553 : 0 : return -1;
1554 : : }
1555 : : /* Check if the hugepage of the pool can be shared. */
1556 : 0 : *share_hugepage = false;
1557 : 0 : msl = rte_mem_virt2memseg_list((void *)ranges[0].start);
1558 [ # # ]: 0 : if (msl != NULL) {
1559 : : uint64_t hugepage_sz = 0;
1560 : :
1561 : : /* Check that all ranges are on pages of the same size. */
1562 [ # # ]: 0 : for (i = 0; i < ranges_n; i++) {
1563 [ # # # # ]: 0 : if (hugepage_sz != 0 && hugepage_sz != msl->page_sz)
1564 : : break;
1565 : 0 : hugepage_sz = msl->page_sz;
1566 : : }
1567 [ # # ]: 0 : if (i == ranges_n) {
1568 : : /*
1569 : : * If the entire pool is within one hugepage,
1570 : : * combine all ranges into one of the hugepage size.
1571 : : */
1572 : 0 : uintptr_t reg_start = ranges[0].start;
1573 : 0 : uintptr_t reg_end = ranges[ranges_n - 1].end;
1574 : 0 : uintptr_t hugepage_start =
1575 : 0 : RTE_ALIGN_FLOOR(reg_start, hugepage_sz);
1576 : 0 : uintptr_t hugepage_end = hugepage_start + hugepage_sz;
1577 [ # # ]: 0 : if (reg_end < hugepage_end) {
1578 : 0 : ranges[0].start = hugepage_start;
1579 : 0 : ranges[0].end = hugepage_end;
1580 : 0 : ranges_n = 1;
1581 : 0 : *share_hugepage = true;
1582 : : }
1583 : : }
1584 : : }
1585 : 0 : *out = ranges;
1586 : 0 : *out_n = ranges_n;
1587 : 0 : return 0;
1588 : : }
1589 : :
1590 : : /** Create a registration object for the mempool. */
1591 : : static struct mlx5_mempool_reg *
1592 : 0 : mlx5_mempool_reg_create(struct rte_mempool *mp, unsigned int mrs_n,
1593 : : bool is_extmem)
1594 : : {
1595 : : struct mlx5_mempool_reg *mpr = NULL;
1596 : :
1597 : 0 : mpr = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO,
1598 : : sizeof(struct mlx5_mempool_reg),
1599 : : RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1600 [ # # ]: 0 : if (mpr == NULL) {
1601 : 0 : DRV_LOG(ERR, "Cannot allocate mempool %s registration object",
1602 : : mp->name);
1603 : 0 : return NULL;
1604 : : }
1605 : 0 : mpr->mrs = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO,
1606 : : mrs_n * sizeof(struct mlx5_mempool_mr),
1607 : : RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1608 [ # # ]: 0 : if (!mpr->mrs) {
1609 : 0 : DRV_LOG(ERR, "Cannot allocate mempool %s registration MRs",
1610 : : mp->name);
1611 : 0 : mlx5_free(mpr);
1612 : 0 : return NULL;
1613 : : }
1614 : 0 : mpr->mp = mp;
1615 : 0 : mpr->mrs_n = mrs_n;
1616 : 0 : mpr->is_extmem = is_extmem;
1617 : 0 : return mpr;
1618 : : }
1619 : :
1620 : : /**
1621 : : * Destroy a mempool registration object.
1622 : : *
1623 : : * @param standalone
1624 : : * Whether @p mpr owns its MRs exclusively, i.e. they are not shared.
1625 : : */
1626 : : static void
1627 : 0 : mlx5_mempool_reg_destroy(struct mlx5_mr_share_cache *share_cache,
1628 : : struct mlx5_mempool_reg *mpr, bool standalone)
1629 : : {
1630 [ # # ]: 0 : if (standalone) {
1631 : : unsigned int i;
1632 : :
1633 [ # # ]: 0 : for (i = 0; i < mpr->mrs_n; i++)
1634 : 0 : share_cache->dereg_mr_cb(&mpr->mrs[i].pmd_mr);
1635 : 0 : mlx5_free(mpr->mrs);
1636 : : }
1637 : 0 : mlx5_free(mpr);
1638 : 0 : }
1639 : :
1640 : : /** Find registration object of a mempool. */
1641 : : static struct mlx5_mempool_reg *
1642 : : mlx5_mempool_reg_lookup(struct mlx5_mr_share_cache *share_cache,
1643 : : struct rte_mempool *mp)
1644 : : {
1645 : : struct mlx5_mempool_reg *mpr;
1646 : :
1647 [ # # # # : 0 : LIST_FOREACH(mpr, &share_cache->mempool_reg_list, next)
# # # # ]
1648 [ # # # # : 0 : if (mpr->mp == mp)
# # # # ]
1649 : : break;
1650 : : return mpr;
1651 : : }
1652 : :
1653 : : /** Increment reference counters of MRs used in the registration. */
1654 : : static void
1655 : 0 : mlx5_mempool_reg_attach(struct mlx5_mempool_reg *mpr)
1656 : : {
1657 : : unsigned int i;
1658 : :
1659 [ # # ]: 0 : for (i = 0; i < mpr->mrs_n; i++)
1660 : 0 : rte_atomic_fetch_add_explicit(&mpr->mrs[i].refcnt, 1, rte_memory_order_relaxed);
1661 : 0 : }
1662 : :
1663 : : /**
1664 : : * Decrement reference counters of MRs used in the registration.
1665 : : *
1666 : : * @return True if no more references to @p mpr MRs exist, False otherwise.
1667 : : */
1668 : : static bool
1669 : 0 : mlx5_mempool_reg_detach(struct mlx5_mempool_reg *mpr)
1670 : : {
1671 : : unsigned int i;
1672 : : bool ret = false;
1673 : :
1674 [ # # ]: 0 : for (i = 0; i < mpr->mrs_n; i++)
1675 : 0 : ret |= rte_atomic_fetch_sub_explicit(&mpr->mrs[i].refcnt, 1,
1676 : 0 : rte_memory_order_relaxed) - 1 == 0;
1677 : 0 : return ret;
1678 : : }
1679 : :
1680 : : static int
1681 : 0 : mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
1682 : : void *pd, struct rte_mempool *mp,
1683 : : bool is_extmem)
1684 : : {
1685 : 0 : struct mlx5_range *ranges = NULL;
1686 : : struct mlx5_mempool_reg *mpr, *old_mpr, *new_mpr;
1687 : : unsigned int i, ranges_n;
1688 : : bool share_hugepage, standalone = false;
1689 : : int ret = -1;
1690 : :
1691 : : /* Early check to avoid unnecessary creation of MRs. */
1692 : 0 : rte_rwlock_read_lock(&share_cache->rwlock);
1693 : : old_mpr = mlx5_mempool_reg_lookup(share_cache, mp);
1694 : : rte_rwlock_read_unlock(&share_cache->rwlock);
1695 [ # # # # : 0 : if (old_mpr != NULL && (!is_extmem || old_mpr->is_extmem)) {
# # ]
1696 : 0 : DRV_LOG(DEBUG, "Mempool %s is already registered for PD %p",
1697 : : mp->name, pd);
1698 : 0 : rte_errno = EEXIST;
1699 : 0 : goto exit;
1700 : : }
1701 [ # # ]: 0 : if (mlx5_mempool_reg_analyze(mp, is_extmem, &ranges, &ranges_n,
1702 : : &share_hugepage) < 0) {
1703 : 0 : DRV_LOG(ERR, "Cannot get mempool %s memory ranges", mp->name);
1704 : 0 : rte_errno = ENOMEM;
1705 : 0 : goto exit;
1706 : : }
1707 : 0 : new_mpr = mlx5_mempool_reg_create(mp, ranges_n, is_extmem);
1708 [ # # ]: 0 : if (new_mpr == NULL) {
1709 : 0 : DRV_LOG(ERR,
1710 : : "Cannot create a registration object for mempool %s in PD %p",
1711 : : mp->name, pd);
1712 : 0 : rte_errno = ENOMEM;
1713 : 0 : goto exit;
1714 : : }
1715 : : /*
1716 : : * If the entire mempool fits in a single hugepage, the MR for this
1717 : : * hugepage can be shared across mempools that also fit in it.
1718 : : */
1719 [ # # ]: 0 : if (share_hugepage) {
1720 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
1721 [ # # ]: 0 : LIST_FOREACH(mpr, &share_cache->mempool_reg_list, next) {
1722 [ # # ]: 0 : if (mpr->mrs[0].pmd_mr.addr == (void *)ranges[0].start)
1723 : : break;
1724 : : }
1725 [ # # ]: 0 : if (mpr != NULL) {
1726 : 0 : new_mpr->mrs = mpr->mrs;
1727 : 0 : mlx5_mempool_reg_attach(new_mpr);
1728 [ # # ]: 0 : LIST_INSERT_HEAD(&share_cache->mempool_reg_list,
1729 : : new_mpr, next);
1730 : : }
1731 : : rte_rwlock_write_unlock(&share_cache->rwlock);
1732 [ # # ]: 0 : if (mpr != NULL) {
1733 : 0 : DRV_LOG(DEBUG, "Shared MR %#x in PD %p for mempool %s with mempool %s",
1734 : : mpr->mrs[0].pmd_mr.lkey, pd, mp->name,
1735 : : mpr->mp->name);
1736 : : ret = 0;
1737 : 0 : goto exit;
1738 : : }
1739 : : }
1740 [ # # ]: 0 : for (i = 0; i < ranges_n; i++) {
1741 : 0 : struct mlx5_mempool_mr *mr = &new_mpr->mrs[i];
1742 : 0 : const struct mlx5_range *range = &ranges[i];
1743 : 0 : size_t len = range->end - range->start;
1744 : :
1745 [ # # ]: 0 : if (share_cache->reg_mr_cb(pd, (void *)range->start, len,
1746 : : &mr->pmd_mr) < 0) {
1747 : 0 : DRV_LOG(ERR,
1748 : : "Failed to create an MR in PD %p for address range "
1749 : : "[0x%" PRIxPTR ", 0x%" PRIxPTR "] (%zu bytes) for mempool %s",
1750 : : pd, range->start, range->end, len, mp->name);
1751 : 0 : break;
1752 : : }
1753 : 0 : DRV_LOG(DEBUG,
1754 : : "Created a new MR %#x in PD %p for address range "
1755 : : "[0x%" PRIxPTR ", 0x%" PRIxPTR "] (%zu bytes) for mempool %s",
1756 : : mr->pmd_mr.lkey, pd, range->start, range->end, len,
1757 : : mp->name);
1758 : : }
1759 [ # # ]: 0 : if (i != ranges_n) {
1760 : 0 : mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
1761 : 0 : rte_errno = EINVAL;
1762 : 0 : goto exit;
1763 : : }
1764 : : /* Concurrent registration is not supposed to happen. */
1765 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
1766 : : mpr = mlx5_mempool_reg_lookup(share_cache, mp);
1767 [ # # ]: 0 : if (mpr == old_mpr && old_mpr != NULL) {
1768 [ # # ]: 0 : LIST_REMOVE(old_mpr, next);
1769 : 0 : standalone = mlx5_mempool_reg_detach(mpr);
1770 : : /* No need to flush the cache: old MRs cannot be in use. */
1771 : : mpr = NULL;
1772 : : }
1773 [ # # ]: 0 : if (mpr == NULL) {
1774 : 0 : mlx5_mempool_reg_attach(new_mpr);
1775 [ # # ]: 0 : LIST_INSERT_HEAD(&share_cache->mempool_reg_list, new_mpr, next);
1776 : : ret = 0;
1777 : : }
1778 : : rte_rwlock_write_unlock(&share_cache->rwlock);
1779 [ # # ]: 0 : if (mpr != NULL) {
1780 : 0 : DRV_LOG(DEBUG, "Mempool %s is already registered for PD %p",
1781 : : mp->name, pd);
1782 : 0 : mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
1783 : 0 : rte_errno = EEXIST;
1784 : 0 : goto exit;
1785 [ # # ]: 0 : } else if (old_mpr != NULL) {
1786 : 0 : DRV_LOG(DEBUG, "Mempool %s registration for PD %p updated for external memory",
1787 : : mp->name, pd);
1788 : 0 : mlx5_mempool_reg_destroy(share_cache, old_mpr, standalone);
1789 : : }
1790 : 0 : exit:
1791 : 0 : free(ranges);
1792 : 0 : return ret;
1793 : : }
1794 : :
1795 : : static int
1796 : : mlx5_mr_mempool_register_secondary(struct mlx5_common_device *cdev,
1797 : : struct rte_mempool *mp, bool is_extmem)
1798 : : {
1799 : 0 : return mlx5_mp_req_mempool_reg(cdev, mp, true, is_extmem);
1800 : : }
1801 : :
1802 : : /**
1803 : : * Register the memory of a mempool in the protection domain.
1804 : : *
1805 : : * @param cdev
1806 : : * Pointer to the mlx5 common device.
1807 : : * @param mp
1808 : : * Mempool to register.
1809 : : *
1810 : : * @return
1811 : : * 0 on success, (-1) on failure and rte_errno is set.
1812 : : */
1813 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_mempool_register)
1814 : : int
1815 : 0 : mlx5_mr_mempool_register(struct mlx5_common_device *cdev,
1816 : : struct rte_mempool *mp, bool is_extmem)
1817 : : {
1818 [ # # ]: 0 : if (mp->flags & RTE_MEMPOOL_F_NON_IO)
1819 : : return 0;
1820 [ # # # ]: 0 : switch (rte_eal_process_type()) {
1821 : 0 : case RTE_PROC_PRIMARY:
1822 : 0 : return mlx5_mr_mempool_register_primary(&cdev->mr_scache,
1823 : : cdev->pd, mp,
1824 : : is_extmem);
1825 : 0 : case RTE_PROC_SECONDARY:
1826 : 0 : return mlx5_mr_mempool_register_secondary(cdev, mp, is_extmem);
1827 : : default:
1828 : : return -1;
1829 : : }
1830 : : }
1831 : :
1832 : : static int
1833 : 0 : mlx5_mr_mempool_unregister_primary(struct mlx5_mr_share_cache *share_cache,
1834 : : struct rte_mempool *mp)
1835 : : {
1836 : : struct mlx5_mempool_reg *mpr;
1837 : : bool standalone = false;
1838 : :
1839 : 0 : rte_rwlock_write_lock(&share_cache->rwlock);
1840 [ # # ]: 0 : LIST_FOREACH(mpr, &share_cache->mempool_reg_list, next)
1841 [ # # ]: 0 : if (mpr->mp == mp) {
1842 [ # # ]: 0 : LIST_REMOVE(mpr, next);
1843 : 0 : standalone = mlx5_mempool_reg_detach(mpr);
1844 [ # # ]: 0 : if (standalone)
1845 : : /*
1846 : : * The unlock operation below provides a memory
1847 : : * barrier due to its store-release semantics.
1848 : : */
1849 : 0 : ++share_cache->dev_gen;
1850 : : break;
1851 : : }
1852 : : rte_rwlock_write_unlock(&share_cache->rwlock);
1853 [ # # ]: 0 : if (mpr == NULL) {
1854 : 0 : rte_errno = ENOENT;
1855 : 0 : return -1;
1856 : : }
1857 : 0 : mlx5_mempool_reg_destroy(share_cache, mpr, standalone);
1858 : 0 : return 0;
1859 : : }
1860 : :
1861 : : static int
1862 : : mlx5_mr_mempool_unregister_secondary(struct mlx5_common_device *cdev,
1863 : : struct rte_mempool *mp)
1864 : : {
1865 : 0 : return mlx5_mp_req_mempool_reg(cdev, mp, false, false /* is_extmem */);
1866 : : }
1867 : :
1868 : : /**
1869 : : * Unregister the memory of a mempool from the protection domain.
1870 : : *
1871 : : * @param cdev
1872 : : * Pointer to the mlx5 common device.
1873 : : * @param mp
1874 : : * Mempool to unregister.
1875 : : *
1876 : : * @return
1877 : : * 0 on success, (-1) on failure and rte_errno is set.
1878 : : */
1879 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_mempool_unregister)
1880 : : int
1881 : 0 : mlx5_mr_mempool_unregister(struct mlx5_common_device *cdev,
1882 : : struct rte_mempool *mp)
1883 : : {
1884 [ # # ]: 0 : if (mp->flags & RTE_MEMPOOL_F_NON_IO)
1885 : : return 0;
1886 [ # # # ]: 0 : switch (rte_eal_process_type()) {
1887 : 0 : case RTE_PROC_PRIMARY:
1888 : 0 : return mlx5_mr_mempool_unregister_primary(&cdev->mr_scache, mp);
1889 : : case RTE_PROC_SECONDARY:
1890 : 0 : return mlx5_mr_mempool_unregister_secondary(cdev, mp);
1891 : : default:
1892 : : return -1;
1893 : : }
1894 : : }
1895 : :
1896 : : /**
1897 : : * Lookup a MR key by and address in a registered mempool.
1898 : : *
1899 : : * @param mpr
1900 : : * Mempool registration object.
1901 : : * @param addr
1902 : : * Address within the mempool.
1903 : : * @param entry
1904 : : * Bottom-half cache entry to fill.
1905 : : *
1906 : : * @return
1907 : : * MR key or UINT32_MAX on failure, which can only happen
1908 : : * if the address is not from within the mempool.
1909 : : */
1910 : : static uint32_t
1911 : 0 : mlx5_mempool_reg_addr2mr(struct mlx5_mempool_reg *mpr, uintptr_t addr,
1912 : : struct mr_cache_entry *entry)
1913 : : {
1914 : : uint32_t lkey = UINT32_MAX;
1915 : : unsigned int i;
1916 : :
1917 [ # # ]: 0 : for (i = 0; i < mpr->mrs_n; i++) {
1918 : 0 : const struct mlx5_pmd_mr *mr = &mpr->mrs[i].pmd_mr;
1919 : 0 : uintptr_t mr_start = (uintptr_t)mr->addr;
1920 : 0 : uintptr_t mr_end = mr_start + mr->len;
1921 : :
1922 [ # # ]: 0 : if (mr_start <= addr && addr < mr_end) {
1923 [ # # ]: 0 : lkey = rte_cpu_to_be_32(mr->lkey);
1924 : 0 : entry->start = mr_start;
1925 : 0 : entry->end = mr_end;
1926 : 0 : entry->lkey = lkey;
1927 : 0 : break;
1928 : : }
1929 : : }
1930 : 0 : return lkey;
1931 : : }
1932 : :
1933 : : /**
1934 : : * Update bottom-half cache from the list of mempool registrations.
1935 : : *
1936 : : * @param mr_ctrl
1937 : : * Per-queue MR control handle.
1938 : : * @param entry
1939 : : * Pointer to an entry in the bottom-half cache to update
1940 : : * with the MR lkey looked up.
1941 : : * @param mp
1942 : : * Mempool containing the address.
1943 : : * @param addr
1944 : : * Address to lookup.
1945 : : * @return
1946 : : * MR lkey on success, UINT32_MAX on failure.
1947 : : */
1948 : : static uint32_t
1949 : 0 : mlx5_lookup_mempool_regs(struct mlx5_mr_ctrl *mr_ctrl,
1950 : : struct mr_cache_entry *entry,
1951 : : struct rte_mempool *mp, uintptr_t addr)
1952 : : {
1953 : : struct mlx5_mr_share_cache *share_cache =
1954 : 0 : container_of(mr_ctrl->dev_gen_ptr, struct mlx5_mr_share_cache,
1955 : : dev_gen);
1956 : 0 : struct mlx5_mr_btree *bt = &mr_ctrl->cache_bh;
1957 : : struct mlx5_mempool_reg *mpr;
1958 : : uint32_t lkey = UINT32_MAX;
1959 : :
1960 : : /* If local cache table is full, try to double it. */
1961 [ # # ]: 0 : if (unlikely(bt->len == bt->size))
1962 : 0 : mr_btree_expand(bt, bt->size << 1);
1963 : : /* Look up in mempool registrations. */
1964 : 0 : rte_rwlock_read_lock(&share_cache->rwlock);
1965 : : mpr = mlx5_mempool_reg_lookup(share_cache, mp);
1966 [ # # ]: 0 : if (mpr != NULL)
1967 : 0 : lkey = mlx5_mempool_reg_addr2mr(mpr, addr, entry);
1968 : : rte_rwlock_read_unlock(&share_cache->rwlock);
1969 : : /*
1970 : : * Update local cache. Even if it fails, return the found entry
1971 : : * to update top-half cache. Next time, this entry will be found
1972 : : * in the global cache.
1973 : : */
1974 [ # # ]: 0 : if (lkey != UINT32_MAX)
1975 : 0 : mr_btree_insert(bt, entry);
1976 : 0 : return lkey;
1977 : : }
1978 : :
1979 : : /**
1980 : : * Populate cache with LKeys of all MRs used by the mempool.
1981 : : * It is intended to be used to register Rx mempools in advance.
1982 : : *
1983 : : * @param mr_ctrl
1984 : : * Per-queue MR control handle.
1985 : : * @param mp
1986 : : * Registered memory pool.
1987 : : *
1988 : : * @return
1989 : : * 0 on success, (-1) on failure and rte_errno is set.
1990 : : */
1991 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_mempool_populate_cache)
1992 : : int
1993 : 0 : mlx5_mr_mempool_populate_cache(struct mlx5_mr_ctrl *mr_ctrl,
1994 : : struct rte_mempool *mp)
1995 : : {
1996 : : struct mlx5_mr_share_cache *share_cache =
1997 : 0 : container_of(mr_ctrl->dev_gen_ptr, struct mlx5_mr_share_cache,
1998 : : dev_gen);
1999 : 0 : struct mlx5_mr_btree *bt = &mr_ctrl->cache_bh;
2000 : : struct mlx5_mempool_reg *mpr;
2001 : : unsigned int i;
2002 : :
2003 : : /*
2004 : : * Registration is valid after the lock is released,
2005 : : * because the function is called after the mempool is registered.
2006 : : */
2007 : 0 : rte_rwlock_read_lock(&share_cache->rwlock);
2008 : : mpr = mlx5_mempool_reg_lookup(share_cache, mp);
2009 : : rte_rwlock_read_unlock(&share_cache->rwlock);
2010 [ # # ]: 0 : if (mpr == NULL) {
2011 : 0 : DRV_LOG(ERR, "Mempool %s is not registered", mp->name);
2012 : 0 : rte_errno = ENOENT;
2013 : 0 : return -1;
2014 : : }
2015 [ # # ]: 0 : for (i = 0; i < mpr->mrs_n; i++) {
2016 : 0 : struct mlx5_mempool_mr *mr = &mpr->mrs[i];
2017 : : struct mr_cache_entry entry;
2018 : : uint32_t lkey;
2019 : : uint32_t idx;
2020 : :
2021 : 0 : lkey = mr_btree_lookup(bt, &idx, (uintptr_t)mr->pmd_mr.addr);
2022 [ # # ]: 0 : if (lkey != UINT32_MAX)
2023 : 0 : continue;
2024 [ # # ]: 0 : if (bt->len == bt->size)
2025 : 0 : mr_btree_expand(bt, bt->size << 1);
2026 : 0 : entry.start = (uintptr_t)mr->pmd_mr.addr;
2027 : 0 : entry.end = entry.start + mr->pmd_mr.len;
2028 [ # # ]: 0 : entry.lkey = rte_cpu_to_be_32(mr->pmd_mr.lkey);
2029 [ # # ]: 0 : if (mr_btree_insert(bt, &entry) < 0) {
2030 : 0 : DRV_LOG(ERR, "Cannot insert cache entry for mempool %s MR %08x",
2031 : : mp->name, entry.lkey);
2032 : 0 : rte_errno = EINVAL;
2033 : 0 : return -1;
2034 : : }
2035 : : }
2036 : : return 0;
2037 : : }
2038 : :
2039 : : /**
2040 : : * Bottom-half lookup for the address from the mempool.
2041 : : *
2042 : : * @param mr_ctrl
2043 : : * Per-queue MR control handle.
2044 : : * @param mp
2045 : : * Mempool containing the address.
2046 : : * @param addr
2047 : : * Address to lookup.
2048 : : * @return
2049 : : * MR lkey on success, UINT32_MAX on failure.
2050 : : */
2051 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_mempool2mr_bh)
2052 : : uint32_t
2053 : 0 : mlx5_mr_mempool2mr_bh(struct mlx5_mr_ctrl *mr_ctrl,
2054 : : struct rte_mempool *mp, uintptr_t addr)
2055 : : {
2056 : 0 : struct mr_cache_entry *repl = &mr_ctrl->cache[mr_ctrl->head];
2057 : : uint32_t lkey;
2058 : : uint32_t bh_idx = 0;
2059 : :
2060 : : /* Binary-search MR translation table. */
2061 : : lkey = mr_btree_lookup(&mr_ctrl->cache_bh, &bh_idx, addr);
2062 : : /* Update top-half cache. */
2063 [ # # ]: 0 : if (likely(lkey != UINT32_MAX)) {
2064 : 0 : *repl = (*mr_ctrl->cache_bh.table)[bh_idx];
2065 : : } else {
2066 : 0 : lkey = mlx5_lookup_mempool_regs(mr_ctrl, repl, mp, addr);
2067 : : /* Can only fail if the address is not from the mempool. */
2068 [ # # ]: 0 : if (unlikely(lkey == UINT32_MAX))
2069 : : return UINT32_MAX;
2070 : : }
2071 : : /* Update the most recently used entry. */
2072 : 0 : mr_ctrl->mru = mr_ctrl->head;
2073 : : /* Point to the next victim, the oldest. */
2074 : 0 : mr_ctrl->head = (mr_ctrl->head + 1) % MLX5_MR_CACHE_N;
2075 : 0 : return lkey;
2076 : : }
2077 : :
2078 : : RTE_EXPORT_INTERNAL_SYMBOL(mlx5_mr_mb2mr_bh)
2079 : : uint32_t
2080 : 0 : mlx5_mr_mb2mr_bh(struct mlx5_mr_ctrl *mr_ctrl, struct rte_mbuf *mb)
2081 : : {
2082 : : struct rte_mempool *mp;
2083 : : struct mlx5_mprq_buf *buf;
2084 : : uint32_t lkey;
2085 : 0 : uintptr_t addr = (uintptr_t)mb->buf_addr;
2086 : : struct mlx5_mr_share_cache *share_cache =
2087 : 0 : container_of(mr_ctrl->dev_gen_ptr, struct mlx5_mr_share_cache,
2088 : : dev_gen);
2089 : : struct mlx5_common_device *cdev =
2090 : 0 : container_of(share_cache, struct mlx5_common_device, mr_scache);
2091 : : bool external, mprq, pinned = false;
2092 : :
2093 : : /* Recover MPRQ mempool. */
2094 : 0 : external = RTE_MBUF_HAS_EXTBUF(mb);
2095 [ # # # # ]: 0 : if (external && mb->shinfo->free_cb == mlx5_mprq_buf_free_cb) {
2096 : : mprq = true;
2097 : 0 : buf = mb->shinfo->fcb_opaque;
2098 : 0 : mp = buf->mp;
2099 : : } else {
2100 : : mprq = false;
2101 : : mp = mlx5_mb2mp(mb);
2102 : 0 : pinned = rte_pktmbuf_priv_flags(mp) &
2103 : : RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF;
2104 : : }
2105 [ # # # # ]: 0 : if (!external || mprq || pinned) {
2106 : 0 : lkey = mlx5_mr_mempool2mr_bh(mr_ctrl, mp, addr);
2107 [ # # ]: 0 : if (lkey != UINT32_MAX)
2108 : : return lkey;
2109 : : /* MPRQ is always registered. */
2110 : : MLX5_ASSERT(!mprq);
2111 : : }
2112 : : /* Register pinned external memory if the mempool is not used for Rx. */
2113 [ # # # # ]: 0 : if (cdev->config.mr_mempool_reg_en && pinned) {
2114 [ # # ]: 0 : if (mlx5_mr_mempool_register(cdev, mp, true) < 0)
2115 : : return UINT32_MAX;
2116 : 0 : lkey = mlx5_mr_mempool2mr_bh(mr_ctrl, mp, addr);
2117 : : MLX5_ASSERT(lkey != UINT32_MAX);
2118 : 0 : return lkey;
2119 : : }
2120 : : /* Fallback to generic mechanism in corner cases. */
2121 : 0 : return mlx5_mr_addr2mr_bh(mr_ctrl, addr);
2122 : : }
|