common/mlx5: share MR management
[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_rxtx.h"
16 #include "mlx5_rx.h"
17 #include "mlx5_tx.h"
18
19 /**
20  * Bottom-half of LKey search on Tx.
21  *
22  * @param txq
23  *   Pointer to Tx queue structure.
24  * @param addr
25  *   Search key.
26  *
27  * @return
28  *   Searched LKey on success, UINT32_MAX on no match.
29  */
30 static uint32_t
31 mlx5_tx_addr2mr_bh(struct mlx5_txq_data *txq, uintptr_t addr)
32 {
33         struct mlx5_txq_ctrl *txq_ctrl =
34                 container_of(txq, struct mlx5_txq_ctrl, txq);
35         struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
36         struct mlx5_priv *priv = txq_ctrl->priv;
37
38         return mlx5_mr_addr2mr_bh(priv->sh->cdev->pd, &priv->mp_id,
39                                   &priv->sh->cdev->mr_scache, mr_ctrl, addr,
40                                   priv->sh->cdev->config.mr_ext_memseg_en);
41 }
42
43 /**
44  * Bottom-half of LKey search on Tx. If it can't be searched in the memseg
45  * list, register the mempool of the mbuf as externally allocated memory.
46  *
47  * @param txq
48  *   Pointer to Tx queue structure.
49  * @param mb
50  *   Pointer to mbuf.
51  *
52  * @return
53  *   Searched LKey on success, UINT32_MAX on no match.
54  */
55 uint32_t
56 mlx5_tx_mb2mr_bh(struct mlx5_txq_data *txq, struct rte_mbuf *mb)
57 {
58         struct mlx5_txq_ctrl *txq_ctrl =
59                 container_of(txq, struct mlx5_txq_ctrl, txq);
60         struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
61         struct mlx5_priv *priv = txq_ctrl->priv;
62         uintptr_t addr = (uintptr_t)mb->buf_addr;
63         uint32_t lkey;
64
65         if (priv->sh->cdev->config.mr_mempool_reg_en) {
66                 struct rte_mempool *mp = NULL;
67                 struct mlx5_mprq_buf *buf;
68
69                 if (!RTE_MBUF_HAS_EXTBUF(mb)) {
70                         mp = mlx5_mb2mp(mb);
71                 } else if (mb->shinfo->free_cb == mlx5_mprq_buf_free_cb) {
72                         /* Recover MPRQ mempool. */
73                         buf = mb->shinfo->fcb_opaque;
74                         mp = buf->mp;
75                 }
76                 if (mp != NULL) {
77                         lkey = mlx5_mr_mempool2mr_bh(&priv->sh->cdev->mr_scache,
78                                                      mr_ctrl, mp, addr);
79                         /*
80                          * Lookup can only fail on invalid input, e.g. "addr"
81                          * is not from "mp" or "mp" has RTE_MEMPOOL_F_NON_IO set.
82                          */
83                         if (lkey != UINT32_MAX)
84                                 return lkey;
85                 }
86                 /* Fallback for generic mechanism in corner cases. */
87         }
88         return mlx5_tx_addr2mr_bh(txq, addr);
89 }
90
91 /**
92  * Finds the first ethdev that match the device.
93  * The existence of multiple ethdev per pci device is only with representors.
94  * On such case, it is enough to get only one of the ports as they all share
95  * the same ibv context.
96  *
97  * @param dev
98  *   Pointer to the device.
99  *
100  * @return
101  *   Pointer to the ethdev if found, NULL otherwise.
102  */
103 static struct rte_eth_dev *
104 dev_to_eth_dev(struct rte_device *dev)
105 {
106         uint16_t port_id;
107
108         port_id = rte_eth_find_next_of(0, dev);
109         if (port_id == RTE_MAX_ETHPORTS)
110                 return NULL;
111         return &rte_eth_devices[port_id];
112 }
113
114 /**
115  * Callback to DMA map external memory to a device.
116  *
117  * @param rte_dev
118  *   Pointer to the generic device.
119  * @param addr
120  *   Starting virtual address of memory to be mapped.
121  * @param iova
122  *   Starting IOVA address of memory to be mapped.
123  * @param len
124  *   Length of memory segment being mapped.
125  *
126  * @return
127  *   0 on success, negative value on error.
128  */
129 int
130 mlx5_net_dma_map(struct rte_device *rte_dev, void *addr,
131                  uint64_t iova __rte_unused, size_t len)
132 {
133         struct rte_eth_dev *dev;
134         struct mlx5_mr *mr;
135         struct mlx5_priv *priv;
136         struct mlx5_common_device *cdev;
137
138         dev = dev_to_eth_dev(rte_dev);
139         if (!dev) {
140                 DRV_LOG(WARNING, "unable to find matching ethdev "
141                                  "to device %s", rte_dev->name);
142                 rte_errno = ENODEV;
143                 return -1;
144         }
145         priv = dev->data->dev_private;
146         cdev = priv->sh->cdev;
147         mr = mlx5_create_mr_ext(cdev->pd, (uintptr_t)addr, len,
148                                 SOCKET_ID_ANY, cdev->mr_scache.reg_mr_cb);
149         if (!mr) {
150                 DRV_LOG(WARNING,
151                         "port %u unable to dma map", dev->data->port_id);
152                 rte_errno = EINVAL;
153                 return -1;
154         }
155         rte_rwlock_write_lock(&cdev->mr_scache.rwlock);
156         LIST_INSERT_HEAD(&cdev->mr_scache.mr_list, mr, mr);
157         /* Insert to the global cache table. */
158         mlx5_mr_insert_cache(&cdev->mr_scache, mr);
159         rte_rwlock_write_unlock(&cdev->mr_scache.rwlock);
160         return 0;
161 }
162
163 /**
164  * Callback to DMA unmap external memory to a device.
165  *
166  * @param rte_dev
167  *   Pointer to the generic device.
168  * @param addr
169  *   Starting virtual address of memory to be unmapped.
170  * @param iova
171  *   Starting IOVA address of memory to be unmapped.
172  * @param len
173  *   Length of memory segment being unmapped.
174  *
175  * @return
176  *   0 on success, negative value on error.
177  */
178 int
179 mlx5_net_dma_unmap(struct rte_device *rte_dev, void *addr,
180                    uint64_t iova __rte_unused, size_t len __rte_unused)
181 {
182         struct rte_eth_dev *dev;
183         struct mlx5_priv *priv;
184         struct mlx5_common_device *cdev;
185         struct mlx5_mr *mr;
186         struct mr_cache_entry entry;
187
188         dev = dev_to_eth_dev(rte_dev);
189         if (!dev) {
190                 DRV_LOG(WARNING, "unable to find matching ethdev to device %s",
191                         rte_dev->name);
192                 rte_errno = ENODEV;
193                 return -1;
194         }
195         priv = dev->data->dev_private;
196         cdev = priv->sh->cdev;
197         rte_rwlock_write_lock(&cdev->mr_scache.rwlock);
198         mr = mlx5_mr_lookup_list(&cdev->mr_scache, &entry, (uintptr_t)addr);
199         if (!mr) {
200                 rte_rwlock_write_unlock(&cdev->mr_scache.rwlock);
201                 DRV_LOG(WARNING, "address 0x%" PRIxPTR " wasn't registered to device %s",
202                         (uintptr_t)addr, rte_dev->name);
203                 rte_errno = EINVAL;
204                 return -1;
205         }
206         LIST_REMOVE(mr, mr);
207         DRV_LOG(DEBUG, "port %u remove MR(%p) from list", dev->data->port_id,
208               (void *)mr);
209         mlx5_mr_free(mr, cdev->mr_scache.dereg_mr_cb);
210         mlx5_mr_rebuild_cache(&cdev->mr_scache);
211         /*
212          * No explicit wmb is needed after updating dev_gen due to
213          * store-release ordering in unlock that provides the
214          * implicit barrier at the software visible level.
215          */
216         ++cdev->mr_scache.dev_gen;
217         DRV_LOG(DEBUG, "broadcasting local cache flush, gen=%d",
218               cdev->mr_scache.dev_gen);
219         rte_rwlock_write_unlock(&cdev->mr_scache.rwlock);
220         return 0;
221 }