d7a791613863a156fdead2e04c04a6a8138b0110
[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 extern struct rte_mbuf *dpdk_mbuf_template_by_pool_index;
31 #ifndef CLIB_MARCH_VARIANT
32 struct rte_mempool **dpdk_mempool_by_buffer_pool_index = 0;
33 struct rte_mempool **dpdk_no_cache_mempool_by_buffer_pool_index = 0;
34 struct rte_mbuf *dpdk_mbuf_template_by_pool_index = 0;
35
36 clib_error_t *
37 dpdk_buffer_pool_init (vlib_main_t * vm, vlib_buffer_pool_t * bp)
38 {
39   uword buffer_mem_start = vm->buffer_main->buffer_mem_start;
40   struct rte_mempool *mp, *nmp;
41   struct rte_pktmbuf_pool_private priv;
42   enum rte_iova_mode iova_mode;
43   u32 i;
44   u8 *name = 0;
45
46   u32 elt_size =
47     sizeof (struct rte_mbuf) + sizeof (vlib_buffer_t) + bp->data_size;
48
49   /* create empty mempools */
50   vec_validate_aligned (dpdk_mempool_by_buffer_pool_index, bp->index,
51                         CLIB_CACHE_LINE_BYTES);
52   vec_validate_aligned (dpdk_no_cache_mempool_by_buffer_pool_index, bp->index,
53                         CLIB_CACHE_LINE_BYTES);
54
55   /* normal mempool */
56   name = format (name, "vpp pool %u%c", bp->index, 0);
57   mp = rte_mempool_create_empty ((char *) name, bp->n_buffers,
58                                  elt_size, 512, sizeof (priv),
59                                  bp->numa_node, 0);
60   if (!mp)
61     {
62       vec_free (name);
63       return clib_error_return (0,
64                                 "failed to create normal mempool for numa node %u",
65                                 bp->index);
66     }
67   vec_reset_length (name);
68
69   /* non-cached mempool */
70   name = format (name, "vpp pool %u (no cache)%c", bp->index, 0);
71   nmp = rte_mempool_create_empty ((char *) name, bp->n_buffers,
72                                   elt_size, 0, sizeof (priv),
73                                   bp->numa_node, 0);
74   if (!nmp)
75     {
76       rte_mempool_free (mp);
77       vec_free (name);
78       return clib_error_return (0,
79                                 "failed to create non-cache mempool for numa nude %u",
80                                 bp->index);
81     }
82   vec_free (name);
83
84   dpdk_mempool_by_buffer_pool_index[bp->index] = mp;
85   dpdk_no_cache_mempool_by_buffer_pool_index[bp->index] = nmp;
86
87   mp->pool_id = nmp->pool_id = bp->index;
88
89   rte_mempool_set_ops_byname (mp, "vpp", NULL);
90   rte_mempool_set_ops_byname (nmp, "vpp-no-cache", NULL);
91
92   /* Call the mempool priv initializer */
93   priv.mbuf_data_room_size = VLIB_BUFFER_PRE_DATA_SIZE +
94     vlib_buffer_get_default_data_size (vm);
95   priv.mbuf_priv_size = VLIB_BUFFER_HDR_SIZE;
96   rte_pktmbuf_pool_init (mp, &priv);
97   rte_pktmbuf_pool_init (nmp, &priv);
98
99   iova_mode = rte_eal_iova_mode ();
100
101   /* populate mempool object buffer header */
102   for (i = 0; i < bp->n_buffers; i++)
103     {
104       struct rte_mempool_objhdr *hdr;
105       vlib_buffer_t *b = vlib_get_buffer (vm, bp->buffers[i]);
106       struct rte_mbuf *mb = rte_mbuf_from_vlib_buffer (b);
107       hdr = (struct rte_mempool_objhdr *) RTE_PTR_SUB (mb, sizeof (*hdr));
108       hdr->mp = mp;
109       hdr->iova = (iova_mode == RTE_IOVA_VA) ?
110         pointer_to_uword (mb) : vlib_physmem_get_pa (vm, mb);
111       STAILQ_INSERT_TAIL (&mp->elt_list, hdr, next);
112       STAILQ_INSERT_TAIL (&nmp->elt_list, hdr, next);
113       mp->populated_size++;
114       nmp->populated_size++;
115     }
116
117   /* call the object initializers */
118   rte_mempool_obj_iter (mp, rte_pktmbuf_init, 0);
119
120   /* create mbuf header tempate from the first buffer in the pool */
121   vec_validate_aligned (dpdk_mbuf_template_by_pool_index, bp->index,
122                         CLIB_CACHE_LINE_BYTES);
123   clib_memcpy (vec_elt_at_index (dpdk_mbuf_template_by_pool_index, bp->index),
124                rte_mbuf_from_vlib_buffer (vlib_buffer_ptr_from_index
125                                           (buffer_mem_start, *bp->buffers,
126                                            0)), sizeof (struct rte_mbuf));
127
128   for (i = 0; i < bp->n_buffers; i++)
129     {
130       vlib_buffer_t *b;
131       b = vlib_buffer_ptr_from_index (buffer_mem_start, bp->buffers[i], 0);
132       vlib_buffer_copy_template (b, &bp->buffer_template);
133     }
134
135   /* map DMA pages if at least one physical device exists */
136   if (rte_eth_dev_count_avail ())
137     {
138       uword i;
139       size_t page_sz;
140       vlib_physmem_map_t *pm;
141       int do_vfio_map = 1;
142
143       pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
144       page_sz = 1ULL << pm->log2_page_size;
145
146       for (i = 0; i < pm->n_pages; i++)
147         {
148           char *va = ((char *) pm->base) + i * page_sz;
149           uword pa = (iova_mode == RTE_IOVA_VA) ?
150             pointer_to_uword (va) : pm->page_table[i];
151
152           if (do_vfio_map &&
153               rte_vfio_dma_map (pointer_to_uword (va), pa, page_sz))
154             do_vfio_map = 0;
155
156           struct rte_mempool_memhdr *memhdr;
157           memhdr = clib_mem_alloc (sizeof (*memhdr));
158           memhdr->mp = mp;
159           memhdr->addr = va;
160           memhdr->iova = pa;
161           memhdr->len = page_sz;
162           memhdr->free_cb = 0;
163           memhdr->opaque = 0;
164
165           STAILQ_INSERT_TAIL (&mp->mem_list, memhdr, next);
166           mp->nb_mem_chunks++;
167         }
168     }
169
170   return 0;
171 }
172
173 static int
174 dpdk_ops_vpp_alloc (struct rte_mempool *mp)
175 {
176   clib_warning ("");
177   return 0;
178 }
179
180 static void
181 dpdk_ops_vpp_free (struct rte_mempool *mp)
182 {
183   clib_warning ("");
184 }
185
186 #endif
187
188 static_always_inline void
189 dpdk_ops_vpp_enqueue_one (vlib_buffer_t * bt, void *obj)
190 {
191   /* Only non-replicated packets (b->ref_count == 1) expected */
192
193   struct rte_mbuf *mb = obj;
194   vlib_buffer_t *b = vlib_buffer_from_rte_mbuf (mb);
195   ASSERT (b->ref_count == 1);
196   ASSERT (b->buffer_pool_index == bt->buffer_pool_index);
197   vlib_buffer_copy_template (b, bt);
198 }
199
200 int
201 CLIB_MULTIARCH_FN (dpdk_ops_vpp_enqueue) (struct rte_mempool * mp,
202                                           void *const *obj_table, unsigned n)
203 {
204   const int batch_size = 32;
205   vlib_main_t *vm = vlib_get_main ();
206   vlib_buffer_t bt;
207   u8 buffer_pool_index = mp->pool_id;
208   vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
209   u32 bufs[batch_size];
210   u32 n_left = n;
211   void *const *obj = obj_table;
212
213   vlib_buffer_copy_template (&bt, &bp->buffer_template);
214
215   while (n_left >= 4)
216     {
217       dpdk_ops_vpp_enqueue_one (&bt, obj[0]);
218       dpdk_ops_vpp_enqueue_one (&bt, obj[1]);
219       dpdk_ops_vpp_enqueue_one (&bt, obj[2]);
220       dpdk_ops_vpp_enqueue_one (&bt, obj[3]);
221       obj += 4;
222       n_left -= 4;
223     }
224
225   while (n_left)
226     {
227       dpdk_ops_vpp_enqueue_one (&bt, obj[0]);
228       obj += 1;
229       n_left -= 1;
230     }
231
232   while (n >= batch_size)
233     {
234       vlib_get_buffer_indices_with_offset (vm, (void **) obj_table, bufs,
235                                            batch_size,
236                                            sizeof (struct rte_mbuf));
237       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, batch_size);
238       n -= batch_size;
239       obj_table += batch_size;
240     }
241
242   if (n)
243     {
244       vlib_get_buffer_indices_with_offset (vm, (void **) obj_table, bufs,
245                                            n, sizeof (struct rte_mbuf));
246       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, n);
247     }
248
249   return 0;
250 }
251
252 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_enqueue);
253
254 static_always_inline void
255 dpdk_ops_vpp_enqueue_no_cache_one (vlib_main_t * vm, struct rte_mempool *old,
256                                    struct rte_mempool *new, void *obj,
257                                    vlib_buffer_t * bt)
258 {
259   struct rte_mbuf *mb = obj;
260   vlib_buffer_t *b = vlib_buffer_from_rte_mbuf (mb);
261
262   if (clib_atomic_sub_fetch (&b->ref_count, 1) == 0)
263     {
264       u32 bi = vlib_get_buffer_index (vm, b);
265       vlib_buffer_copy_template (b, bt);
266       vlib_buffer_pool_put (vm, bt->buffer_pool_index, &bi, 1);
267       return;
268     }
269 }
270
271 int
272 CLIB_MULTIARCH_FN (dpdk_ops_vpp_enqueue_no_cache) (struct rte_mempool * cmp,
273                                                    void *const *obj_table,
274                                                    unsigned n)
275 {
276   vlib_main_t *vm = vlib_get_main ();
277   vlib_buffer_t bt;
278   struct rte_mempool *mp;
279   mp = dpdk_mempool_by_buffer_pool_index[cmp->pool_id];
280   u8 buffer_pool_index = cmp->pool_id;
281   vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
282   vlib_buffer_copy_template (&bt, &bp->buffer_template);
283
284   while (n >= 4)
285     {
286       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[0], &bt);
287       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[1], &bt);
288       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[2], &bt);
289       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[3], &bt);
290       obj_table += 4;
291       n -= 4;
292     }
293
294   while (n)
295     {
296       dpdk_ops_vpp_enqueue_no_cache_one (vm, cmp, mp, obj_table[0], &bt);
297       obj_table += 1;
298       n -= 1;
299     }
300
301   return 0;
302 }
303
304 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_enqueue_no_cache);
305
306 static_always_inline void
307 dpdk_mbuf_init_from_template (struct rte_mbuf **mba, struct rte_mbuf *mt,
308                               int count)
309 {
310   /* Assumptions about rte_mbuf layout */
311   STATIC_ASSERT_OFFSET_OF (struct rte_mbuf, buf_addr, 0);
312   STATIC_ASSERT_OFFSET_OF (struct rte_mbuf, buf_iova, 8);
313   STATIC_ASSERT_SIZEOF_ELT (struct rte_mbuf, buf_iova, 8);
314   STATIC_ASSERT_SIZEOF_ELT (struct rte_mbuf, buf_iova, 8);
315   STATIC_ASSERT_SIZEOF (struct rte_mbuf, 128);
316
317   while (count--)
318     {
319       struct rte_mbuf *mb = mba[0];
320       int i;
321       /* bytes 0 .. 15 hold buf_addr and buf_iova which we need to preserve */
322       /* copy bytes 16 .. 31 */
323       *((u8x16 *) mb + 1) = *((u8x16 *) mt + 1);
324
325       /* copy bytes 32 .. 127 */
326 #ifdef CLIB_HAVE_VEC256
327       for (i = 1; i < 4; i++)
328         *((u8x32 *) mb + i) = *((u8x32 *) mt + i);
329 #else
330       for (i = 2; i < 8; i++)
331         *((u8x16 *) mb + i) = *((u8x16 *) mt + i);
332 #endif
333       mba++;
334     }
335 }
336
337 int
338 CLIB_MULTIARCH_FN (dpdk_ops_vpp_dequeue) (struct rte_mempool * mp,
339                                           void **obj_table, unsigned n)
340 {
341   const int batch_size = 32;
342   vlib_main_t *vm = vlib_get_main ();
343   u32 bufs[batch_size], total = 0, n_alloc = 0;
344   u8 buffer_pool_index = mp->pool_id;
345   void **obj = obj_table;
346   struct rte_mbuf t = dpdk_mbuf_template_by_pool_index[buffer_pool_index];
347
348   while (n >= batch_size)
349     {
350       n_alloc = vlib_buffer_alloc_from_pool (vm, bufs, batch_size,
351                                              buffer_pool_index);
352       if (n_alloc != batch_size)
353         goto alloc_fail;
354
355       vlib_get_buffers_with_offset (vm, bufs, obj, batch_size,
356                                     -(i32) sizeof (struct rte_mbuf));
357       dpdk_mbuf_init_from_template ((struct rte_mbuf **) obj, &t, batch_size);
358       total += batch_size;
359       obj += batch_size;
360       n -= batch_size;
361     }
362
363   if (n)
364     {
365       n_alloc = vlib_buffer_alloc_from_pool (vm, bufs, n, buffer_pool_index);
366
367       if (n_alloc != n)
368         goto alloc_fail;
369
370       vlib_get_buffers_with_offset (vm, bufs, obj, n,
371                                     -(i32) sizeof (struct rte_mbuf));
372       dpdk_mbuf_init_from_template ((struct rte_mbuf **) obj, &t, n);
373     }
374
375   return 0;
376
377 alloc_fail:
378   /* dpdk doesn't support partial alloc, so we need to return what we
379      already got */
380   if (n_alloc)
381     vlib_buffer_pool_put (vm, buffer_pool_index, bufs, n_alloc);
382   obj = obj_table;
383   while (total)
384     {
385       vlib_get_buffer_indices_with_offset (vm, obj, bufs, batch_size,
386                                            sizeof (struct rte_mbuf));
387       vlib_buffer_pool_put (vm, buffer_pool_index, bufs, batch_size);
388
389       obj += batch_size;
390       total -= batch_size;
391     }
392   return -ENOENT;
393 }
394
395 CLIB_MARCH_FN_REGISTRATION (dpdk_ops_vpp_dequeue);
396
397 #ifndef CLIB_MARCH_VARIANT
398
399 static int
400 dpdk_ops_vpp_dequeue_no_cache (struct rte_mempool *mp, void **obj_table,
401                                unsigned n)
402 {
403   clib_error ("bug");
404   return 0;
405 }
406
407 static unsigned
408 dpdk_ops_vpp_get_count (const struct rte_mempool *mp)
409 {
410   clib_warning ("");
411   return 0;
412 }
413
414 static unsigned
415 dpdk_ops_vpp_get_count_no_cache (const struct rte_mempool *mp)
416 {
417   struct rte_mempool *cmp;
418   cmp = dpdk_no_cache_mempool_by_buffer_pool_index[mp->pool_id];
419   return dpdk_ops_vpp_get_count (cmp);
420 }
421
422 clib_error_t *
423 dpdk_buffer_pools_create (vlib_main_t * vm)
424 {
425   clib_error_t *err;
426   vlib_buffer_pool_t *bp;
427
428   struct rte_mempool_ops ops = { };
429
430   strncpy (ops.name, "vpp", 4);
431   ops.alloc = dpdk_ops_vpp_alloc;
432   ops.free = dpdk_ops_vpp_free;
433   ops.get_count = dpdk_ops_vpp_get_count;
434   ops.enqueue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_enqueue);
435   ops.dequeue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_dequeue);
436   rte_mempool_register_ops (&ops);
437
438   strncpy (ops.name, "vpp-no-cache", 13);
439   ops.get_count = dpdk_ops_vpp_get_count_no_cache;
440   ops.enqueue = CLIB_MARCH_FN_POINTER (dpdk_ops_vpp_enqueue_no_cache);
441   ops.dequeue = dpdk_ops_vpp_dequeue_no_cache;
442   rte_mempool_register_ops (&ops);
443
444   /* *INDENT-OFF* */
445   vec_foreach (bp, vm->buffer_main->buffer_pools)
446     if (bp->start && (err = dpdk_buffer_pool_init (vm, bp)))
447       return err;
448   /* *INDENT-ON* */
449   return 0;
450 }
451
452 VLIB_BUFFER_SET_EXT_HDR_SIZE (sizeof (struct rte_mempool_objhdr) +
453                               sizeof (struct rte_mbuf));
454
455 #endif
456
457 /** @endcond */
458 /*
459  * fd.io coding-style-patch-verification: ON
460  *
461  * Local Variables:
462  * eval: (c-set-style "gnu")
463  * End:
464  */