net/qede/base: support doorbell overflow recovery
[dpdk.git] / drivers / net / mlx5 / mlx5_mr.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2016 6WIND S.A.
5  *   Copyright 2016 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /* Verbs header. */
35 /* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
36 #ifdef PEDANTIC
37 #pragma GCC diagnostic ignored "-Wpedantic"
38 #endif
39 #include <infiniband/verbs.h>
40 #ifdef PEDANTIC
41 #pragma GCC diagnostic error "-Wpedantic"
42 #endif
43
44 #include <rte_mempool.h>
45
46 #include "mlx5.h"
47 #include "mlx5_rxtx.h"
48
49 struct mlx5_check_mempool_data {
50         int ret;
51         char *start;
52         char *end;
53 };
54
55 /* Called by mlx5_check_mempool() when iterating the memory chunks. */
56 static void
57 mlx5_check_mempool_cb(struct rte_mempool *mp,
58                       void *opaque, struct rte_mempool_memhdr *memhdr,
59                       unsigned int mem_idx)
60 {
61         struct mlx5_check_mempool_data *data = opaque;
62
63         (void)mp;
64         (void)mem_idx;
65
66         /* It already failed, skip the next chunks. */
67         if (data->ret != 0)
68                 return;
69         /* It is the first chunk. */
70         if (data->start == NULL && data->end == NULL) {
71                 data->start = memhdr->addr;
72                 data->end = data->start + memhdr->len;
73                 return;
74         }
75         if (data->end == memhdr->addr) {
76                 data->end += memhdr->len;
77                 return;
78         }
79         if (data->start == (char *)memhdr->addr + memhdr->len) {
80                 data->start -= memhdr->len;
81                 return;
82         }
83         /* Error, mempool is not virtually contiguous. */
84         data->ret = -1;
85 }
86
87 /**
88  * Check if a mempool can be used: it must be virtually contiguous.
89  *
90  * @param[in] mp
91  *   Pointer to memory pool.
92  * @param[out] start
93  *   Pointer to the start address of the mempool virtual memory area
94  * @param[out] end
95  *   Pointer to the end address of the mempool virtual memory area
96  *
97  * @return
98  *   0 on success (mempool is virtually contiguous), -1 on error.
99  */
100 static int mlx5_check_mempool(struct rte_mempool *mp, uintptr_t *start,
101         uintptr_t *end)
102 {
103         struct mlx5_check_mempool_data data;
104
105         memset(&data, 0, sizeof(data));
106         rte_mempool_mem_iter(mp, mlx5_check_mempool_cb, &data);
107         *start = (uintptr_t)data.start;
108         *end = (uintptr_t)data.end;
109
110         return data.ret;
111 }
112
113 /**
114  * Register mempool as a memory region.
115  *
116  * @param pd
117  *   Pointer to protection domain.
118  * @param mp
119  *   Pointer to memory pool.
120  *
121  * @return
122  *   Memory region pointer, NULL in case of error.
123  */
124 struct ibv_mr *
125 mlx5_mp2mr(struct ibv_pd *pd, struct rte_mempool *mp)
126 {
127         const struct rte_memseg *ms = rte_eal_get_physmem_layout();
128         uintptr_t start;
129         uintptr_t end;
130         unsigned int i;
131
132         if (mlx5_check_mempool(mp, &start, &end) != 0) {
133                 ERROR("mempool %p: not virtually contiguous",
134                       (void *)mp);
135                 return NULL;
136         }
137
138         DEBUG("mempool %p area start=%p end=%p size=%zu",
139               (void *)mp, (void *)start, (void *)end,
140               (size_t)(end - start));
141         /* Round start and end to page boundary if found in memory segments. */
142         for (i = 0; (i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL); ++i) {
143                 uintptr_t addr = (uintptr_t)ms[i].addr;
144                 size_t len = ms[i].len;
145                 unsigned int align = ms[i].hugepage_sz;
146
147                 if ((start > addr) && (start < addr + len))
148                         start = RTE_ALIGN_FLOOR(start, align);
149                 if ((end > addr) && (end < addr + len))
150                         end = RTE_ALIGN_CEIL(end, align);
151         }
152         DEBUG("mempool %p using start=%p end=%p size=%zu for MR",
153               (void *)mp, (void *)start, (void *)end,
154               (size_t)(end - start));
155         return ibv_reg_mr(pd,
156                           (void *)start,
157                           end - start,
158                           IBV_ACCESS_LOCAL_WRITE);
159 }
160
161 /**
162  * Register a Memory Region (MR) <-> Memory Pool (MP) association in
163  * txq->mp2mr[]. If mp2mr[] is full, remove an entry first.
164  *
165  * This function should only be called by txq_mp2mr().
166  *
167  * @param txq
168  *   Pointer to TX queue structure.
169  * @param[in] mp
170  *   Memory Pool for which a Memory Region lkey must be returned.
171  * @param idx
172  *   Index of the next available entry.
173  *
174  * @return
175  *   mr->lkey on success, (uint32_t)-1 on failure.
176  */
177 uint32_t
178 txq_mp2mr_reg(struct txq *txq, struct rte_mempool *mp, unsigned int idx)
179 {
180         struct txq_ctrl *txq_ctrl = container_of(txq, struct txq_ctrl, txq);
181         struct ibv_mr *mr;
182
183         /* Add a new entry, register MR first. */
184         DEBUG("%p: discovered new memory pool \"%s\" (%p)",
185               (void *)txq_ctrl, mp->name, (void *)mp);
186         mr = mlx5_mp2mr(txq_ctrl->priv->pd, mp);
187         if (unlikely(mr == NULL)) {
188                 DEBUG("%p: unable to configure MR, ibv_reg_mr() failed.",
189                       (void *)txq_ctrl);
190                 return (uint32_t)-1;
191         }
192         if (unlikely(idx == RTE_DIM(txq_ctrl->txq.mp2mr))) {
193                 /* Table is full, remove oldest entry. */
194                 DEBUG("%p: MR <-> MP table full, dropping oldest entry.",
195                       (void *)txq_ctrl);
196                 --idx;
197                 claim_zero(ibv_dereg_mr(txq_ctrl->txq.mp2mr[0].mr));
198                 memmove(&txq_ctrl->txq.mp2mr[0], &txq_ctrl->txq.mp2mr[1],
199                         (sizeof(txq_ctrl->txq.mp2mr) -
200                          sizeof(txq_ctrl->txq.mp2mr[0])));
201         }
202         /* Store the new entry. */
203         txq_ctrl->txq.mp2mr[idx].start = (uintptr_t)mr->addr;
204         txq_ctrl->txq.mp2mr[idx].end = (uintptr_t)mr->addr + mr->length;
205         txq_ctrl->txq.mp2mr[idx].mr = mr;
206         txq_ctrl->txq.mp2mr[idx].lkey = rte_cpu_to_be_32(mr->lkey);
207         DEBUG("%p: new MR lkey for MP \"%s\" (%p): 0x%08" PRIu32,
208               (void *)txq_ctrl, mp->name, (void *)mp,
209               txq_ctrl->txq.mp2mr[idx].lkey);
210         return txq_ctrl->txq.mp2mr[idx].lkey;
211 }
212
213 struct txq_mp2mr_mbuf_check_data {
214         int ret;
215 };
216
217 /**
218  * Callback function for rte_mempool_obj_iter() to check whether a given
219  * mempool object looks like a mbuf.
220  *
221  * @param[in] mp
222  *   The mempool pointer
223  * @param[in] arg
224  *   Context data (struct txq_mp2mr_mbuf_check_data). Contains the
225  *   return value.
226  * @param[in] obj
227  *   Object address.
228  * @param index
229  *   Object index, unused.
230  */
231 static void
232 txq_mp2mr_mbuf_check(struct rte_mempool *mp, void *arg, void *obj,
233         uint32_t index __rte_unused)
234 {
235         struct txq_mp2mr_mbuf_check_data *data = arg;
236         struct rte_mbuf *buf = obj;
237
238         /*
239          * Check whether mbuf structure fits element size and whether mempool
240          * pointer is valid.
241          */
242         if (sizeof(*buf) > mp->elt_size || buf->pool != mp)
243                 data->ret = -1;
244 }
245
246 /**
247  * Iterator function for rte_mempool_walk() to register existing mempools and
248  * fill the MP to MR cache of a TX queue.
249  *
250  * @param[in] mp
251  *   Memory Pool to register.
252  * @param *arg
253  *   Pointer to TX queue structure.
254  */
255 void
256 txq_mp2mr_iter(struct rte_mempool *mp, void *arg)
257 {
258         struct txq_ctrl *txq_ctrl = arg;
259         struct txq_mp2mr_mbuf_check_data data = {
260                 .ret = 0,
261         };
262         uintptr_t start;
263         uintptr_t end;
264         unsigned int i;
265
266         /* Register mempool only if the first element looks like a mbuf. */
267         if (rte_mempool_obj_iter(mp, txq_mp2mr_mbuf_check, &data) == 0 ||
268                         data.ret == -1)
269                 return;
270         if (mlx5_check_mempool(mp, &start, &end) != 0) {
271                 ERROR("mempool %p: not virtually contiguous",
272                       (void *)mp);
273                 return;
274         }
275         for (i = 0; (i != RTE_DIM(txq_ctrl->txq.mp2mr)); ++i) {
276                 struct ibv_mr *mr = txq_ctrl->txq.mp2mr[i].mr;
277
278                 if (unlikely(mr == NULL)) {
279                         /* Unknown MP, add a new MR for it. */
280                         break;
281                 }
282                 if (start >= (uintptr_t)mr->addr &&
283                     end <= (uintptr_t)mr->addr + mr->length)
284                         return;
285         }
286         txq_mp2mr_reg(&txq_ctrl->txq, mp, i);
287 }