net/ice/base: fix null pointer dereferences for parser
[dpdk.git] / drivers / net / mlx5 / mlx5_mr.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016 6WIND S.A.
3  * Copyright 2016 Mellanox Technologies, Ltd
4  */
5
6 #include <rte_eal_memconfig.h>
7 #include <rte_mempool.h>
8 #include <rte_malloc.h>
9 #include <rte_rwlock.h>
10
11 #include <mlx5_common_mp.h>
12 #include <mlx5_common_mr.h>
13
14 #include "mlx5.h"
15 #include "mlx5_mr.h"
16 #include "mlx5_rxtx.h"
17 #include "mlx5_rx.h"
18 #include "mlx5_tx.h"
19
20 struct mr_find_contig_memsegs_data {
21         uintptr_t addr;
22         uintptr_t start;
23         uintptr_t end;
24         const struct rte_memseg_list *msl;
25 };
26
27 struct mr_update_mp_data {
28         struct rte_eth_dev *dev;
29         struct mlx5_mr_ctrl *mr_ctrl;
30         int ret;
31 };
32
33 /**
34  * Callback for memory event. This can be called from both primary and secondary
35  * process.
36  *
37  * @param event_type
38  *   Memory event type.
39  * @param addr
40  *   Address of memory.
41  * @param len
42  *   Size of memory.
43  */
44 void
45 mlx5_mr_mem_event_cb(enum rte_mem_event event_type, const void *addr,
46                      size_t len, void *arg __rte_unused)
47 {
48         struct mlx5_dev_ctx_shared *sh;
49         struct mlx5_dev_list *dev_list = &mlx5_shared_data->mem_event_cb_list;
50
51         /* Must be called from the primary process. */
52         MLX5_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
53         switch (event_type) {
54         case RTE_MEM_EVENT_FREE:
55                 rte_rwlock_write_lock(&mlx5_shared_data->mem_event_rwlock);
56                 /* Iterate all the existing mlx5 devices. */
57                 LIST_FOREACH(sh, dev_list, mem_event_cb)
58                         mlx5_free_mr_by_addr(&sh->share_cache,
59                                              sh->ibdev_name, addr, len);
60                 rte_rwlock_write_unlock(&mlx5_shared_data->mem_event_rwlock);
61                 break;
62         case RTE_MEM_EVENT_ALLOC:
63         default:
64                 break;
65         }
66 }
67
68 /**
69  * Bottom-half of LKey search on Tx.
70  *
71  * @param txq
72  *   Pointer to Tx queue structure.
73  * @param addr
74  *   Search key.
75  *
76  * @return
77  *   Searched LKey on success, UINT32_MAX on no match.
78  */
79 static uint32_t
80 mlx5_tx_addr2mr_bh(struct mlx5_txq_data *txq, uintptr_t addr)
81 {
82         struct mlx5_txq_ctrl *txq_ctrl =
83                 container_of(txq, struct mlx5_txq_ctrl, txq);
84         struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
85         struct mlx5_priv *priv = txq_ctrl->priv;
86
87         return mlx5_mr_addr2mr_bh(priv->sh->pd, &priv->mp_id,
88                                   &priv->sh->share_cache, mr_ctrl, addr,
89                                   priv->config.mr_ext_memseg_en);
90 }
91
92 /**
93  * Bottom-half of LKey search on Tx. If it can't be searched in the memseg
94  * list, register the mempool of the mbuf as externally allocated memory.
95  *
96  * @param txq
97  *   Pointer to Tx queue structure.
98  * @param mb
99  *   Pointer to mbuf.
100  *
101  * @return
102  *   Searched LKey on success, UINT32_MAX on no match.
103  */
104 uint32_t
105 mlx5_tx_mb2mr_bh(struct mlx5_txq_data *txq, struct rte_mbuf *mb)
106 {
107         struct mlx5_txq_ctrl *txq_ctrl =
108                 container_of(txq, struct mlx5_txq_ctrl, txq);
109         struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
110         struct mlx5_priv *priv = txq_ctrl->priv;
111         uintptr_t addr = (uintptr_t)mb->buf_addr;
112         uint32_t lkey;
113
114         if (priv->config.mr_mempool_reg_en) {
115                 struct rte_mempool *mp = NULL;
116                 struct mlx5_mprq_buf *buf;
117
118                 if (!RTE_MBUF_HAS_EXTBUF(mb)) {
119                         mp = mlx5_mb2mp(mb);
120                 } else if (mb->shinfo->free_cb == mlx5_mprq_buf_free_cb) {
121                         /* Recover MPRQ mempool. */
122                         buf = mb->shinfo->fcb_opaque;
123                         mp = buf->mp;
124                 }
125                 if (mp != NULL) {
126                         lkey = mlx5_mr_mempool2mr_bh(&priv->sh->share_cache,
127                                                      mr_ctrl, mp, addr);
128                         /*
129                          * Lookup can only fail on invalid input, e.g. "addr"
130                          * is not from "mp" or "mp" has RTE_MEMPOOL_F_NON_IO set.
131                          */
132                         if (lkey != UINT32_MAX)
133                                 return lkey;
134                 }
135                 /* Fallback for generic mechanism in corner cases. */
136         }
137         lkey = mlx5_tx_addr2mr_bh(txq, addr);
138         if (lkey == UINT32_MAX && rte_errno == ENXIO) {
139                 /* Mempool may have externally allocated memory. */
140                 return mlx5_tx_update_ext_mp(txq, addr, mlx5_mb2mp(mb));
141         }
142         return lkey;
143 }
144
145 /**
146  * Called during rte_mempool_mem_iter() by mlx5_mr_update_ext_mp().
147  *
148  * Externally allocated chunk is registered and a MR is created for the chunk.
149  * The MR object is added to the global list. If memseg list of a MR object
150  * (mr->msl) is null, the MR object can be regarded as externally allocated
151  * memory.
152  *
153  * Once external memory is registered, it should be static. If the memory is
154  * freed and the virtual address range has different physical memory mapped
155  * again, it may cause crash on device due to the wrong translation entry. PMD
156  * can't track the free event of the external memory for now.
157  */
158 static void
159 mlx5_mr_update_ext_mp_cb(struct rte_mempool *mp, void *opaque,
160                          struct rte_mempool_memhdr *memhdr,
161                          unsigned mem_idx __rte_unused)
162 {
163         struct mr_update_mp_data *data = opaque;
164         struct rte_eth_dev *dev = data->dev;
165         struct mlx5_priv *priv = dev->data->dev_private;
166         struct mlx5_dev_ctx_shared *sh = priv->sh;
167         struct mlx5_mr_ctrl *mr_ctrl = data->mr_ctrl;
168         struct mlx5_mr *mr = NULL;
169         uintptr_t addr = (uintptr_t)memhdr->addr;
170         size_t len = memhdr->len;
171         struct mr_cache_entry entry;
172         uint32_t lkey;
173
174         MLX5_ASSERT(rte_eal_process_type() == RTE_PROC_PRIMARY);
175         /* If already registered, it should return. */
176         rte_rwlock_read_lock(&sh->share_cache.rwlock);
177         lkey = mlx5_mr_lookup_cache(&sh->share_cache, &entry, addr);
178         rte_rwlock_read_unlock(&sh->share_cache.rwlock);
179         if (lkey != UINT32_MAX)
180                 return;
181         DRV_LOG(DEBUG, "port %u register MR for chunk #%d of mempool (%s)",
182                 dev->data->port_id, mem_idx, mp->name);
183         mr = mlx5_create_mr_ext(sh->pd, addr, len, mp->socket_id,
184                                 sh->share_cache.reg_mr_cb);
185         if (!mr) {
186                 DRV_LOG(WARNING,
187                         "port %u unable to allocate a new MR of"
188                         " mempool (%s).",
189                         dev->data->port_id, mp->name);
190                 data->ret = -1;
191                 return;
192         }
193         rte_rwlock_write_lock(&sh->share_cache.rwlock);
194         LIST_INSERT_HEAD(&sh->share_cache.mr_list, mr, mr);
195         /* Insert to the global cache table. */
196         mlx5_mr_insert_cache(&sh->share_cache, mr);
197         rte_rwlock_write_unlock(&sh->share_cache.rwlock);
198         /* Insert to the local cache table */
199         mlx5_mr_addr2mr_bh(sh->pd, &priv->mp_id, &sh->share_cache,
200                            mr_ctrl, addr, priv->config.mr_ext_memseg_en);
201 }
202
203 /**
204  * Finds the first ethdev that match the device.
205  * The existence of multiple ethdev per pci device is only with representors.
206  * On such case, it is enough to get only one of the ports as they all share
207  * the same ibv context.
208  *
209  * @param dev
210  *   Pointer to the device.
211  *
212  * @return
213  *   Pointer to the ethdev if found, NULL otherwise.
214  */
215 static struct rte_eth_dev *
216 dev_to_eth_dev(struct rte_device *dev)
217 {
218         uint16_t port_id;
219
220         port_id = rte_eth_find_next_of(0, dev);
221         if (port_id == RTE_MAX_ETHPORTS)
222                 return NULL;
223         return &rte_eth_devices[port_id];
224 }
225
226 /**
227  * Callback to DMA map external memory to a device.
228  *
229  * @param rte_dev
230  *   Pointer to the generic device.
231  * @param addr
232  *   Starting virtual address of memory to be mapped.
233  * @param iova
234  *   Starting IOVA address of memory to be mapped.
235  * @param len
236  *   Length of memory segment being mapped.
237  *
238  * @return
239  *   0 on success, negative value on error.
240  */
241 int
242 mlx5_net_dma_map(struct rte_device *rte_dev, void *addr,
243                  uint64_t iova __rte_unused, size_t len)
244 {
245         struct rte_eth_dev *dev;
246         struct mlx5_mr *mr;
247         struct mlx5_priv *priv;
248         struct mlx5_dev_ctx_shared *sh;
249
250         dev = dev_to_eth_dev(rte_dev);
251         if (!dev) {
252                 DRV_LOG(WARNING, "unable to find matching ethdev "
253                                  "to device %s", rte_dev->name);
254                 rte_errno = ENODEV;
255                 return -1;
256         }
257         priv = dev->data->dev_private;
258         sh = priv->sh;
259         mr = mlx5_create_mr_ext(sh->pd, (uintptr_t)addr, len, SOCKET_ID_ANY,
260                                 sh->share_cache.reg_mr_cb);
261         if (!mr) {
262                 DRV_LOG(WARNING,
263                         "port %u unable to dma map", dev->data->port_id);
264                 rte_errno = EINVAL;
265                 return -1;
266         }
267         rte_rwlock_write_lock(&sh->share_cache.rwlock);
268         LIST_INSERT_HEAD(&sh->share_cache.mr_list, mr, mr);
269         /* Insert to the global cache table. */
270         mlx5_mr_insert_cache(&sh->share_cache, mr);
271         rte_rwlock_write_unlock(&sh->share_cache.rwlock);
272         return 0;
273 }
274
275 /**
276  * Callback to DMA unmap external memory to a device.
277  *
278  * @param rte_dev
279  *   Pointer to the generic device.
280  * @param addr
281  *   Starting virtual address of memory to be unmapped.
282  * @param iova
283  *   Starting IOVA address of memory to be unmapped.
284  * @param len
285  *   Length of memory segment being unmapped.
286  *
287  * @return
288  *   0 on success, negative value on error.
289  */
290 int
291 mlx5_net_dma_unmap(struct rte_device *rte_dev, void *addr,
292                    uint64_t iova __rte_unused, size_t len __rte_unused)
293 {
294         struct rte_eth_dev *dev;
295         struct mlx5_priv *priv;
296         struct mlx5_dev_ctx_shared *sh;
297         struct mlx5_mr *mr;
298         struct mr_cache_entry entry;
299
300         dev = dev_to_eth_dev(rte_dev);
301         if (!dev) {
302                 DRV_LOG(WARNING, "unable to find matching ethdev to device %s",
303                         rte_dev->name);
304                 rte_errno = ENODEV;
305                 return -1;
306         }
307         priv = dev->data->dev_private;
308         sh = priv->sh;
309         rte_rwlock_write_lock(&sh->share_cache.rwlock);
310         mr = mlx5_mr_lookup_list(&sh->share_cache, &entry, (uintptr_t)addr);
311         if (!mr) {
312                 rte_rwlock_write_unlock(&sh->share_cache.rwlock);
313                 DRV_LOG(WARNING, "address 0x%" PRIxPTR " wasn't registered to device %s",
314                         (uintptr_t)addr, rte_dev->name);
315                 rte_errno = EINVAL;
316                 return -1;
317         }
318         LIST_REMOVE(mr, mr);
319         DRV_LOG(DEBUG, "port %u remove MR(%p) from list", dev->data->port_id,
320               (void *)mr);
321         mlx5_mr_free(mr, sh->share_cache.dereg_mr_cb);
322         mlx5_mr_rebuild_cache(&sh->share_cache);
323         /*
324          * No explicit wmb is needed after updating dev_gen due to
325          * store-release ordering in unlock that provides the
326          * implicit barrier at the software visible level.
327          */
328         ++sh->share_cache.dev_gen;
329         DRV_LOG(DEBUG, "broadcasting local cache flush, gen=%d",
330               sh->share_cache.dev_gen);
331         rte_rwlock_write_unlock(&sh->share_cache.rwlock);
332         return 0;
333 }
334
335 /**
336  * Register MR for entire memory chunks in a Mempool having externally allocated
337  * memory and fill in local cache.
338  *
339  * @param dev
340  *   Pointer to Ethernet device.
341  * @param mr_ctrl
342  *   Pointer to per-queue MR control structure.
343  * @param mp
344  *   Pointer to registering Mempool.
345  *
346  * @return
347  *   0 on success, -1 on failure.
348  */
349 static uint32_t
350 mlx5_mr_update_ext_mp(struct rte_eth_dev *dev, struct mlx5_mr_ctrl *mr_ctrl,
351                       struct rte_mempool *mp)
352 {
353         struct mr_update_mp_data data = {
354                 .dev = dev,
355                 .mr_ctrl = mr_ctrl,
356                 .ret = 0,
357         };
358
359         rte_mempool_mem_iter(mp, mlx5_mr_update_ext_mp_cb, &data);
360         return data.ret;
361 }
362
363 /**
364  * Register MR entire memory chunks in a Mempool having externally allocated
365  * memory and search LKey of the address to return.
366  *
367  * @param dev
368  *   Pointer to Ethernet device.
369  * @param addr
370  *   Search key.
371  * @param mp
372  *   Pointer to registering Mempool where addr belongs.
373  *
374  * @return
375  *   LKey for address on success, UINT32_MAX on failure.
376  */
377 uint32_t
378 mlx5_tx_update_ext_mp(struct mlx5_txq_data *txq, uintptr_t addr,
379                       struct rte_mempool *mp)
380 {
381         struct mlx5_txq_ctrl *txq_ctrl =
382                 container_of(txq, struct mlx5_txq_ctrl, txq);
383         struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
384         struct mlx5_priv *priv = txq_ctrl->priv;
385
386         if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
387                 DRV_LOG(WARNING,
388                         "port %u using address (%p) from unregistered mempool"
389                         " having externally allocated memory"
390                         " in secondary process, please create mempool"
391                         " prior to rte_eth_dev_start()",
392                         PORT_ID(priv), (void *)addr);
393                 return UINT32_MAX;
394         }
395         mlx5_mr_update_ext_mp(ETH_DEV(priv), mr_ctrl, mp);
396         return mlx5_tx_addr2mr_bh(txq, addr);
397 }