buffers: reinitialize metadata, add additional validation
[vpp.git] / src / plugins / dpdk / buffer.c
1 /*
2  * Copyright (c) 2017-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <unistd.h>
17 #include <errno.h>
18
19 #include <rte_config.h>
20 #include <rte_mbuf.h>
21 #include <rte_ethdev.h>
22 #include <rte_vfio.h>
23
24 #include <vlib/vlib.h>
25 #include <dpdk/buffer.h>
26
27 STATIC_ASSERT (VLIB_BUFFER_PRE_DATA_SIZE == RTE_PKTMBUF_HEADROOM,
28                "VLIB_BUFFER_PRE_DATA_SIZE must be equal to RTE_PKTMBUF_HEADROOM");
29
30
31 typedef struct
32 {
33   /* must be first */
34   struct rte_pktmbuf_pool_private mbp_priv;
35   u8 buffer_pool_index;
36 } dpdk_mempool_private_t;
37
38 #ifndef CLIB_MARCH_VARIANT
39 struct rte_mempool **dpdk_mempool_by_buffer_pool_index = 0;
40 struct rte_mempool **dpdk_no_cache_mempool_by_buffer_pool_index = 0;
41
42 clib_error_t *
43 dpdk_buffer_pool_init (vlib_main_t * vm, vlib_buffer_pool_t * bp)
44 {
45   uword buffer_mem_start = vm->buffer_main->buffer_mem_start;
46   struct rte_mempool *mp, *nmp;
47   dpdk_mempool_private_t priv;
48   enum rte_iova_mode iova_mode;
49   u32 *bi;
50   u8 *name = 0;
51
52   u32 elt_size =
53     sizeof (struct rte_mbuf) + sizeof (vlib_buffer_t) + bp->data_size;
54
55   /* create empty mempools */
56   vec_validate_aligned (dpdk_mempool_by_buffer_pool_index, bp->index,
57                         CLIB_CACHE_LINE_BYTES);
58   vec_validate_aligned (dpdk_no_cache_mempool_by_buffer_pool_index, bp->index,
59                         CLIB_CACHE_LINE_BYTES);
60
61   /* normal mempool */
62   name = format (name, "vpp pool %u%c", bp->index, 0);
63   mp = rte_mempool_create_empty ((char *) name, vec_len (bp->buffers),
64                                  elt_size, 512, sizeof (priv),
65                                  bp->numa_node, 0);
66   vec_reset_length (name);
67
68   /* non-cached mempool */
69   name = format (name, "vpp pool %u (no cache)%c", bp->index, 0);
70   nmp = rte_mempool_create_empty ((char *) name, vec_len (bp->buffers),
71                                   elt_size, 0, sizeof (priv),
72                                   bp->numa_node, 0);
73   vec_free (name);
74
75   dpdk_mempool_by_buffer_pool_index[bp->index] = mp;
76   dpdk_no_cache_mempool_by_buffer_pool_index[bp->index] = nmp;
77
78   rte_mempool_set_ops_byname (mp, "vpp", NULL);
79   rte_mempool_set_ops_byname (nmp, "vpp-no-cache", NULL);
80
81   /* Call the mempool priv initializer */
82   priv.mbp_priv.mbuf_data_room_size = VLIB_BUFFER_PRE_DATA_SIZE +
83     VLIB_BUFFER_DATA_SIZE;
84   priv.mbp_priv.mbuf_priv_size = VLIB_BUFFER_HDR_SIZE;
85   priv.buffer_pool_index = bp->index;
86   rte_pktmbuf_pool_init (mp, &priv);
87   rte_pktmbuf_pool_init (nmp, &priv);
88
89   iova_mode = rte_eal_iova_mode ();
90
91   /* populate mempool object buffer header */
92   /* *INDENT-OFF* */
93   vec_foreach (bi, bp->buffers)
94     {
95       struct rte_mempool_objhdr *hdr;
96       vlib_buffer_t *b = vlib_get_buffer (vm, *bi);
97       struct rte_mbuf *mb = rte_mbuf_from_vlib_buffer (b);
98       hdr = (struct rte_mempool_objhdr *) RTE_PTR_SUB (mb, sizeof (*hdr));
99       hdr->mp = mp;
100       hdr->iova = (iova_mode == RTE_IOVA_VA) ?
101         pointer_to_uword (mb) : vlib_physmem_get_pa (vm, mb);
102       STAILQ_INSERT_TAIL (&mp->elt_list, hdr, next);
103       STAILQ_INSERT_TAIL (&nmp->elt_list, hdr, next);
104       mp->populated_size++;
105       nmp->populated_size++;
106     }
107   /* *INDENT-ON* */
108
109   /* call the object initializers */
110   rte_mempool_obj_iter (mp, rte_pktmbuf_init, 0);
111
112   /* *INDENT-OFF* */
113   vec_foreach (bi, bp->buffers)
114     {
115       vlib_buffer_t *b;
116       b = vlib_buffer_ptr_from_index (buffer_mem_start, *bi, 0);
117       vlib_buffer_copy_template (b, &bp->buffer_template);
118     }
119   /* *INDENT-ON* */
120
121   /* map DMA pages if at least one physical device exists */
122   if (rte_eth_dev_count_avail ())
123     {
124       uword i;
125       size_t page_sz;
126       vlib_physmem_map_t *pm;
127
128       pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
129       page_sz = 1ULL << pm->log2_page_size;
130
131       for (i = 0; i < pm->n_pages; i++)
132         {
133           char *va = ((char *) pm->base) + i * page_sz;
134           uword pa = (iova_mode == RTE_IOVA_VA) ?
135             pointer_to_uword (va) : pm->page_table[i];
136
137           if (rte_vfio_dma_map (pointer_to_uword (va), pa, page_sz))
138             break;
139         }
140     }
141
142   return 0;
143 }
144
145 static int
146 dpdk_ops_vpp_alloc (struct rte_mempool *mp)
147 {
148   clib_warning ("");
149   return 0;
150 }
151
152 static void
153 dpdk_ops_vpp_free (struct rte_mempool *mp)
154 {
155   clib_warning ("");
156 }
157
158 #endif
159
160 static_always_inline void
161 dpdk_ops_vpp_enqueue_one (vlib_buffer_t * bt, void *obj)
162 {
163   /* Only non-replicated packets (b->ref_count == 1) expected */
164
165   struct rte_mbuf *mb = obj;
166   vlib_buffer_t *b = vlib_buffer_from_rte_mbuf (mb);
167   ASSERT (b->ref_count == 1);
168   ASSERT (b->buffer_pool_index == bt->buffer_pool_index);
169   vlib_buffer_copy_template (b, bt);
170 }
171
172 int
173 CLIB_MULTIARCH_FN (dpdk_ops_vpp_enqueue) (struct rte_mempool * mp,
174                                           void *const *obj_table, unsigned n)
175 {
176   const int batch_size = 32;
177   vlib_main_t *vm = vlib_get_main ();
178   vlib_buffer_t bt;
179   dpdk_mempool_private_t *privp = rte_mempool_get_priv (mp);
180   u8 buffer_pool_index = privp->buffer_pool_index;
181   vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
182   u32 bufs[batch_size];
183   u32 n_left = n;
184   void *const *obj = obj_table;
185
186   vlib_buffer_copy_template (&bt, &bp->buffer_template);
187
188   while (n_left >= 4)
189     {
190       dpdk_ops_vpp_enqueue_one (&bt, obj[0]);
191       dpdk_ops_vpp_enqueue_one (&bt, obj[1]);
192       dpdk_ops_vpp_enqueue_one (&bt, obj[2]);
193       dpdk_ops_vpp_enqueue_one (&bt, obj[3]);
194       obj += 4;
195       n_left -= 4;
196     }
197
198   while (n_left)
199     {
200       dpdk_ops_vpp_enqueue_one (&bt, obj[0]);
201       obj += 1;
202       n_left -= 1;
203     }
204
205   while (n >= batch_size)
206     {
207       vlib_get_buffer_indices_with_offset (vm, (void **) obj_table, bufs,
208                                            batch_size,
209                                            sizeof (struct rte_mbuf));
210       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, batch_size);
211       n -= batch_size;
212       obj_table += batch_size;
213     }
214
215   if (n)
216     {
217       vlib_get_buffer_indices_with_offset (vm, (void **) obj_table, bufs,
218                                            n, sizeof (struct rte_mbuf));
219       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, batch_size);
220     }
221
222   return 0;
223 }
224
225 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_enqueue);
226
227 static_always_inline void
228 dpdk_ops_vpp_enqueue_no_cache_one (vlib_main_t * vm, struct rte_mempool *old,
229                                    struct rte_mempool *new, void *obj,
230                                    vlib_buffer_t * bt)
231 {
232   struct rte_mbuf *mb = obj;
233   vlib_buffer_t *b = vlib_buffer_from_rte_mbuf (mb);
234
235   if (clib_atomic_sub_fetch (&b->ref_count, 1) == 0)
236     {
237       u32 bi = vlib_get_buffer_index (vm, b);
238       mb->pool = new;
239       vlib_buffer_copy_template (b, bt);
240       vlib_buffer_pool_put (vm, bt->buffer_pool_index, &bi, 1);
241       return;
242     }
243 }
244
245 int
246 CLIB_MULTIARCH_FN (dpdk_ops_vpp_enqueue_no_cache) (struct rte_mempool * cmp,
247                                                    void *const *obj_table,
248                                                    unsigned n)
249 {
250   vlib_main_t *vm = vlib_get_main ();
251   vlib_buffer_t bt;
252   dpdk_mempool_private_t *privp = rte_mempool_get_priv (cmp);
253   struct rte_mempool *mp;
254   mp = dpdk_mempool_by_buffer_pool_index[privp->buffer_pool_index];
255   u8 buffer_pool_index = privp->buffer_pool_index;
256   vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
257   vlib_buffer_copy_template (&bt, &bp->buffer_template);
258
259   while (n >= 4)
260     {
261       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[0], &bt);
262       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[1], &bt);
263       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[2], &bt);
264       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[3], &bt);
265       obj_table += 4;
266       n -= 4;
267     }
268
269   while (n)
270     {
271       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[0], &bt);
272       obj_table += 1;
273       n -= 1;
274     }
275
276   return 0;
277 }
278
279 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_enqueue_no_cache);
280
281 int
282 CLIB_MULTIARCH_FN (dpdk_ops_vpp_dequeue) (struct rte_mempool * mp,
283                                           void **obj_table, unsigned n)
284 {
285   const int batch_size = 32;
286   vlib_main_t *vm = vlib_get_main ();
287   u32 bufs[batch_size], total = 0, n_alloc = 0;
288   dpdk_mempool_private_t *privp = rte_mempool_get_priv (mp);
289   u8 buffer_pool_index = privp->buffer_pool_index;
290   void **obj = obj_table;
291
292   while (n >= batch_size)
293     {
294       n_alloc = vlib_buffer_alloc_from_pool (vm, bufs, batch_size,
295                                              buffer_pool_index);
296       if (n_alloc != batch_size)
297         goto alloc_fail;
298
299       vlib_get_buffers_with_offset (vm, bufs, obj, batch_size,
300                                     -(i32) sizeof (struct rte_mbuf));
301       total += batch_size;
302       obj += batch_size;
303       n -= batch_size;
304     }
305
306   if (n)
307     {
308       n_alloc = vlib_buffer_alloc_from_pool (vm, bufs, n, buffer_pool_index);
309
310       if (n_alloc != n)
311         goto alloc_fail;
312
313       vlib_get_buffers_with_offset (vm, bufs, obj, n,
314                                     -(i32) sizeof (struct rte_mbuf));
315     }
316
317   return 0;
318
319 alloc_fail:
320   /* dpdk doesn't support partial alloc, so we need to return what we
321      already got */
322   if (n_alloc)
323     vlib_buffer_pool_put (vm, buffer_pool_index, bufs, n_alloc);
324   obj = obj_table;
325   while (total)
326     {
327       vlib_get_buffer_indices_with_offset (vm, obj, bufs, batch_size,
328                                            sizeof (struct rte_mbuf));
329       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, batch_size);
330
331       obj += batch_size;
332       total -= batch_size;
333     }
334   return -ENOENT;
335 }
336
337 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_dequeue);
338
339 #ifndef CLIB_MARCH_VARIANT
340
341 static int
342 dpdk_ops_vpp_dequeue_no_cache (struct rte_mempool *mp, void **obj_table,
343                                unsigned n)
344 {
345   clib_error ("bug");
346   return 0;
347 }
348
349 static unsigned
350 dpdk_ops_vpp_get_count (const struct rte_mempool *mp)
351 {
352   clib_warning ("");
353   return 0;
354 }
355
356 static unsigned
357 dpdk_ops_vpp_get_count_no_cache (const struct rte_mempool *mp)
358 {
359   dpdk_mempool_private_t *privp;
360   struct rte_mempool *cmp;
361   privp = rte_mempool_get_priv ((struct rte_mempool *) mp);
362   cmp = dpdk_no_cache_mempool_by_buffer_pool_index[privp->buffer_pool_index];
363   return dpdk_ops_vpp_get_count (cmp);
364 }
365
366 clib_error_t *
367 dpdk_buffer_pools_create (vlib_main_t * vm)
368 {
369   clib_error_t *err;
370   vlib_buffer_pool_t *bp;
371
372   struct rte_mempool_ops ops = { };
373
374   strncpy (ops.name, "vpp", 4);
375   ops.alloc = dpdk_ops_vpp_alloc;
376   ops.free = dpdk_ops_vpp_free;
377   ops.get_count = dpdk_ops_vpp_get_count;
378   ops.enqueue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_enqueue);
379   ops.dequeue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_dequeue);
380   rte_mempool_register_ops (&ops);
381
382   strncpy (ops.name, "vpp-no-cache", 13);
383   ops.get_count = dpdk_ops_vpp_get_count_no_cache;
384   ops.enqueue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_enqueue_no_cache);
385   ops.dequeue = dpdk_ops_vpp_dequeue_no_cache;
386   rte_mempool_register_ops (&ops);
387
388   /* *INDENT-OFF* */
389   vec_foreach (bp, vm->buffer_main->buffer_pools)
390     if (bp->start && (err = dpdk_buffer_pool_init (vm, bp)))
391       return err;
392   /* *INDENT-ON* */
393   return 0;
394 }
395
396 VLIB_BUFFER_SET_EXT_HDR_SIZE (sizeof (struct rte_mempool_objhdr) +
397                               sizeof (struct rte_mbuf));
398
399 #endif
400
401 /** @endcond */
402 /*
403  * fd.io coding-style-patch-verification: ON
404  *
405  * Local Variables:
406  * eval: (c-set-style "gnu")
407  * End:
408  */