af_xdp: AF_XDP input plugin
[vpp.git] / src / plugins / af_xdp / device.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2018 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <stdio.h>
19 #include <net/if.h>
20 #include <linux/if_link.h>
21 #include <bpf/libbpf.h>
22 #include <vlib/vlib.h>
23 #include <vlib/unix/unix.h>
24 #include <vlib/pci/pci.h>
25 #include <vppinfra/unix.h>
26 #include <vnet/ethernet/ethernet.h>
27 #include "af_xdp.h"
28
29 af_xdp_main_t af_xdp_main;
30
31 typedef struct
32 {
33   u32 prod;
34   u32 cons;
35 } gdb_af_xdp_pair_t;
36
37 gdb_af_xdp_pair_t
38 gdb_af_xdp_get_prod (const struct xsk_ring_prod *prod)
39 {
40   gdb_af_xdp_pair_t pair = { *prod->producer, *prod->consumer };
41   return pair;
42 }
43
44 gdb_af_xdp_pair_t
45 gdb_af_xdp_get_cons (const struct xsk_ring_cons * cons)
46 {
47   gdb_af_xdp_pair_t pair = { *cons->producer, *cons->consumer };
48   return pair;
49 }
50
51 static clib_error_t *
52 af_xdp_mac_change (vnet_hw_interface_t * hw, const u8 * old, const u8 * new)
53 {
54   af_xdp_main_t *am = &af_xdp_main;
55   af_xdp_device_t *ad = vec_elt_at_index (am->devices, hw->dev_instance);
56   errno_t err = memcpy_s (ad->hwaddr, sizeof (ad->hwaddr), new, 6);
57   if (err)
58     return clib_error_return_code (0, -err, CLIB_ERROR_ERRNO_VALID,
59                                    "mac change failed");
60   return 0;
61 }
62
63 static u32
64 af_xdp_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hw, u32 flags)
65 {
66   af_xdp_main_t *am = &af_xdp_main;
67   af_xdp_device_t *ad = vec_elt_at_index (am->devices, hw->dev_instance);
68
69   switch (flags)
70     {
71     case 0:
72       af_xdp_log (VLIB_LOG_LEVEL_ERR, ad, "set unicast not supported yet");
73       return ~0;
74     case ETHERNET_INTERFACE_FLAG_ACCEPT_ALL:
75       af_xdp_log (VLIB_LOG_LEVEL_ERR, ad,
76                   "set promiscuous not supported yet");
77       return ~0;
78     case ETHERNET_INTERFACE_FLAG_MTU:
79       af_xdp_log (VLIB_LOG_LEVEL_ERR, ad, "set mtu not supported yet");
80       return ~0;
81     }
82
83   af_xdp_log (VLIB_LOG_LEVEL_ERR, ad, "unknown flag %x requested", flags);
84   return ~0;
85 }
86
87 void
88 af_xdp_delete_if (vlib_main_t * vm, af_xdp_device_t * ad)
89 {
90   vnet_main_t *vnm = vnet_get_main ();
91   af_xdp_main_t *axm = &af_xdp_main;
92   struct xsk_socket **xsk;
93   struct xsk_umem **umem;
94   af_xdp_rxq_t *rxq;
95   af_xdp_txq_t *txq;
96
97   if (ad->hw_if_index)
98     {
99       vnet_hw_interface_set_flags (vnm, ad->hw_if_index, 0);
100       vnet_hw_interface_unassign_rx_thread (vnm, ad->hw_if_index, 0);
101       ethernet_delete_interface (vnm, ad->hw_if_index);
102     }
103
104   vec_foreach (rxq, ad->rxqs) clib_file_del_by_index (&file_main,
105                                                       rxq->file_index);
106   vec_foreach (txq, ad->txqs) clib_spinlock_free (&txq->lock);
107   vec_foreach (xsk, ad->xsk) xsk_socket__delete (*xsk);
108   vec_foreach (umem, ad->umem) xsk_umem__delete (*umem);
109
110   if (ad->bpf_obj)
111     {
112       bpf_set_link_xdp_fd (ad->linux_ifindex, -1, 0);
113       bpf_object__unload (ad->bpf_obj);
114     }
115
116   vec_free (ad->xsk);
117   vec_free (ad->umem);
118   vec_free (ad->buffer_template);
119   vec_free (ad->rxqs);
120   vec_free (ad->txqs);
121   clib_error_free (ad->error);
122   pool_put (axm->devices, ad);
123 }
124
125 static int
126 af_xdp_load_program (af_xdp_create_if_args_t * args, af_xdp_device_t * ad)
127 {
128   int fd;
129
130   ad->linux_ifindex = if_nametoindex (ad->linux_ifname);
131   if (!ad->linux_ifindex)
132     {
133       args->rv = VNET_API_ERROR_INVALID_VALUE;
134       args->error =
135         clib_error_return_unix (0, "if_nametoindex(%s) failed",
136                                 ad->linux_ifname);
137       goto err0;
138     }
139
140   if (bpf_prog_load (args->prog, BPF_PROG_TYPE_XDP, &ad->bpf_obj, &fd))
141     {
142       args->rv = VNET_API_ERROR_SYSCALL_ERROR_5;
143       args->error =
144         clib_error_return_unix (0, "bpf_prog_load(%s) failed", args->prog);
145       goto err0;
146     }
147
148 #ifndef XDP_FLAGS_REPLACE
149 #define XDP_FLAGS_REPLACE 0
150 #endif
151   if (bpf_set_link_xdp_fd (ad->linux_ifindex, fd, XDP_FLAGS_REPLACE))
152     {
153       args->rv = VNET_API_ERROR_SYSCALL_ERROR_6;
154       args->error =
155         clib_error_return_unix (0, "bpf_set_link_xdp_fd(%s) failed",
156                                 ad->linux_ifname);
157       goto err1;
158     }
159
160   return 0;
161
162 err1:
163   bpf_object__unload (ad->bpf_obj);
164   ad->bpf_obj = 0;
165 err0:
166   ad->linux_ifindex = ~0;
167   return -1;
168 }
169
170 static int
171 af_xdp_create_queue (vlib_main_t * vm, af_xdp_create_if_args_t * args,
172                      af_xdp_device_t * ad, int qid, int rxq_num, int txq_num)
173 {
174   struct xsk_umem **umem = vec_elt_at_index (ad->umem, qid);
175   struct xsk_socket **xsk = vec_elt_at_index (ad->xsk, qid);
176   af_xdp_rxq_t *rxq = vec_elt_at_index (ad->rxqs, qid);
177   af_xdp_txq_t *txq = vec_elt_at_index (ad->txqs, qid);
178   struct xsk_umem_config umem_config;
179   struct xsk_socket_config sock_config;
180   struct xdp_options opt;
181   socklen_t optlen;
182   /*
183    * fq and cq must always be allocated even if unused
184    * whereas rx and tx indicates whether we want rxq, txq, or both
185    */
186   struct xsk_ring_cons *rx = qid < rxq_num ? &rxq->rx : 0;
187   struct xsk_ring_prod *fq = &rxq->fq;
188   struct xsk_ring_prod *tx = qid < txq_num ? &txq->tx : 0;
189   struct xsk_ring_cons *cq = &txq->cq;
190   int fd;
191
192   memset (&umem_config, 0, sizeof (umem_config));
193   umem_config.fill_size = args->rxq_size;
194   umem_config.comp_size = args->txq_size;
195   umem_config.frame_size =
196     sizeof (vlib_buffer_t) + vlib_buffer_get_default_data_size (vm);
197   umem_config.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
198   if (xsk_umem__create
199       (umem, uword_to_pointer (vm->buffer_main->buffer_mem_start, void *),
200        vm->buffer_main->buffer_mem_size, fq, cq, &umem_config))
201     {
202       args->rv = VNET_API_ERROR_SYSCALL_ERROR_1;
203       args->error = clib_error_return_unix (0, "xsk_umem__create() failed");
204       goto err0;
205     }
206
207   memset (&sock_config, 0, sizeof (sock_config));
208   sock_config.rx_size = args->rxq_size;
209   sock_config.tx_size = args->txq_size;
210   sock_config.bind_flags = XDP_USE_NEED_WAKEUP;
211   switch (args->mode)
212     {
213     case AF_XDP_MODE_AUTO:
214       break;
215     case AF_XDP_MODE_COPY:
216       sock_config.bind_flags |= XDP_COPY;
217       break;
218     case AF_XDP_MODE_ZERO_COPY:
219       sock_config.bind_flags |= XDP_ZEROCOPY;
220       break;
221     }
222   if (xsk_socket__create
223       (xsk, ad->linux_ifname, qid, *umem, rx, tx, &sock_config))
224     {
225       args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
226       args->error =
227         clib_error_return_unix (0,
228                                 "xsk_socket__create() failed (is linux netdev %s up?)",
229                                 ad->linux_ifname);
230       goto err1;
231     }
232
233   fd = xsk_socket__fd (*xsk);
234   optlen = sizeof (opt);
235   if (getsockopt (fd, SOL_XDP, XDP_OPTIONS, &opt, &optlen))
236     {
237       args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
238       args->error =
239         clib_error_return_unix (0, "getsockopt(XDP_OPTIONS) failed");
240       goto err2;
241     }
242   if (opt.flags & XDP_OPTIONS_ZEROCOPY)
243     ad->flags |= AF_XDP_DEVICE_F_ZEROCOPY;
244
245   rxq->xsk_fd = qid < rxq_num ? fd : -1;
246   txq->xsk_fd = qid < txq_num ? fd : -1;
247
248   return 0;
249
250 err2:
251   xsk_socket__delete (*xsk);
252 err1:
253   xsk_umem__delete (*umem);
254 err0:
255   *umem = 0;
256   *xsk = 0;
257   return -1;
258 }
259
260 static int
261 af_xdp_get_numa (const char *ifname)
262 {
263   FILE *fptr;
264   int numa;
265   char *s;
266
267   s = (char *) format (0, "/sys/class/net/%s/device/numa_node%c", ifname, 0);
268   fptr = fopen (s, "rb");
269   vec_free (s);
270
271   if (!fptr)
272     return 0;
273
274   if (fscanf (fptr, "%d\n", &numa) != 1)
275     numa = 0;
276
277   fclose (fptr);
278   return numa;
279 }
280
281 static clib_error_t *
282 af_xdp_device_rxq_read_ready (clib_file_t * f)
283 {
284   vnet_main_t *vnm = vnet_get_main ();
285   const af_xdp_main_t *am = &af_xdp_main;
286   const u32 dev_instance = f->private_data >> 16;
287   const u16 qid = f->private_data & 0xffff;
288   const af_xdp_device_t *ad = vec_elt_at_index (am->devices, dev_instance);
289   vnet_device_input_set_interrupt_pending (vnm, ad->hw_if_index, qid);
290   return 0;
291 }
292
293 void
294 af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
295 {
296   vnet_main_t *vnm = vnet_get_main ();
297   vlib_thread_main_t *tm = vlib_get_thread_main ();
298   af_xdp_main_t *am = &af_xdp_main;
299   af_xdp_device_t *ad;
300   vnet_sw_interface_t *sw;
301   vnet_hw_interface_t *hw;
302   int rxq_num, txq_num, q_num;
303   int i;
304
305   args->rxq_size = args->rxq_size ? args->rxq_size : 2 * VLIB_FRAME_SIZE;
306   args->txq_size = args->txq_size ? args->txq_size : 2 * VLIB_FRAME_SIZE;
307   rxq_num = args->rxq_num ? args->rxq_num : 1;
308   txq_num = tm->n_vlib_mains;
309
310   if (!args->linux_ifname)
311     {
312       args->rv = VNET_API_ERROR_INVALID_VALUE;
313       args->error = clib_error_return (0, "missing host interface");
314       goto err0;
315     }
316
317   if (args->rxq_size < VLIB_FRAME_SIZE || args->txq_size < VLIB_FRAME_SIZE ||
318       args->rxq_size > 65535 || args->txq_size > 65535 ||
319       !is_pow2 (args->rxq_size) || !is_pow2 (args->txq_size))
320     {
321       args->rv = VNET_API_ERROR_INVALID_VALUE;
322       args->error =
323         clib_error_return (0,
324                            "queue size must be a power of two between %i and 65535",
325                            VLIB_FRAME_SIZE);
326       goto err0;
327     }
328
329   pool_get_zero (am->devices, ad);
330
331   ad->linux_ifname = (char *) format (0, "%s", args->linux_ifname);
332   vec_validate (ad->linux_ifname, IFNAMSIZ - 1);        /* libbpf expects ifname to be at least IFNAMSIZ */
333
334   if (args->prog && af_xdp_load_program (args, ad))
335     goto err1;
336
337   q_num = clib_max (rxq_num, txq_num);
338   vec_validate_aligned (ad->rxqs, q_num - 1, CLIB_CACHE_LINE_BYTES);
339   vec_validate_aligned (ad->txqs, q_num - 1, CLIB_CACHE_LINE_BYTES);
340   vec_validate_aligned (ad->umem, q_num - 1, CLIB_CACHE_LINE_BYTES);
341   vec_validate_aligned (ad->xsk, q_num - 1, CLIB_CACHE_LINE_BYTES);
342   ad->txq_num = txq_num;
343   for (i = 0; i < q_num; i++)
344     {
345       if (af_xdp_create_queue (vm, args, ad, i, rxq_num, txq_num))
346         {
347           /*
348            * queue creation failed
349            * it is only a fatal error if we could not create the number of rx
350            * queues requested explicitely by the user
351            * we might create less tx queues than workers but this is ok
352            */
353           af_xdp_txq_t *txq;
354
355           /* fixup vectors length */
356           vec_set_len (ad->umem, i);
357           vec_set_len (ad->xsk, i);
358           vec_set_len (ad->rxqs, i);
359           vec_set_len (ad->txqs, i);
360
361           if (i < rxq_num)
362             goto err1;          /* failed creating requested rxq: fatal error, bailing out */
363
364           /*
365            * we created all rxq but failed some txq: not an error but
366            * initialize lock for shared txq
367            */
368           ad->txq_num = i;
369           vec_foreach (txq, ad->txqs) clib_spinlock_init (&txq->lock);
370           args->rv = 0;
371           clib_error_free (args->error);
372           break;
373         }
374     }
375
376   ad->dev_instance = ad - am->devices;
377   ad->per_interface_next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
378   ad->pool =
379     vlib_buffer_pool_get_default_for_numa (vm,
380                                            af_xdp_get_numa
381                                            (ad->linux_ifname));
382   if (!args->name)
383     ad->name =
384       (char *) format (0, "%s/%d", ad->linux_ifname, ad->dev_instance);
385   else
386     ad->name = (char *) format (0, "%s", args->name);
387
388   ethernet_mac_address_generate (ad->hwaddr);
389
390   /* create interface */
391   if (ethernet_register_interface (vnm, af_xdp_device_class.index,
392                                    ad->dev_instance, ad->hwaddr,
393                                    &ad->hw_if_index, af_xdp_flag_change))
394     {
395       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
396       args->error =
397         clib_error_return (0, "ethernet_register_interface() failed");
398       goto err1;
399     }
400
401   sw = vnet_get_hw_sw_interface (vnm, ad->hw_if_index);
402   hw = vnet_get_hw_interface (vnm, ad->hw_if_index);
403   args->sw_if_index = ad->sw_if_index = sw->sw_if_index;
404   hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE;
405
406   vnet_hw_interface_set_input_node (vnm, ad->hw_if_index,
407                                     af_xdp_input_node.index);
408
409   for (i = 0; i < rxq_num; i++)
410     {
411       af_xdp_rxq_t *rxq = vec_elt_at_index (ad->rxqs, i);
412       clib_file_t f = {
413         .file_descriptor = rxq->xsk_fd,
414         .flags = UNIX_FILE_EVENT_EDGE_TRIGGERED,
415         .private_data = (uword) ad->dev_instance << 16 | (uword) i,
416         .read_function = af_xdp_device_rxq_read_ready,
417         .description =
418           format (0, "%U rxq %d", format_af_xdp_device_name, ad->dev_instance,
419                   i),
420       };
421       rxq->file_index = clib_file_add (&file_main, &f);
422       vnet_hw_interface_assign_rx_thread (vnm, ad->hw_if_index, i, ~0);
423     }
424
425   /* buffer template */
426   vec_validate_aligned (ad->buffer_template, 1, CLIB_CACHE_LINE_BYTES);
427   ad->buffer_template->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
428   ad->buffer_template->ref_count = 1;
429   vnet_buffer (ad->buffer_template)->sw_if_index[VLIB_RX] = ad->sw_if_index;
430   vnet_buffer (ad->buffer_template)->sw_if_index[VLIB_TX] = (u32) ~ 0;
431   ad->buffer_template->buffer_pool_index = ad->pool;
432
433   return;
434
435 err1:
436   af_xdp_delete_if (vm, ad);
437 err0:
438   vlib_log_err (am->log_class, "%U", format_clib_error, args->error);
439 }
440
441 static clib_error_t *
442 af_xdp_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
443 {
444   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
445   af_xdp_main_t *am = &af_xdp_main;
446   af_xdp_device_t *ad = vec_elt_at_index (am->devices, hi->dev_instance);
447   uword is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
448
449   if (ad->flags & AF_XDP_DEVICE_F_ERROR)
450     return clib_error_return (0, "device is in error state");
451
452   if (is_up)
453     {
454       vnet_hw_interface_set_flags (vnm, ad->hw_if_index,
455                                    VNET_HW_INTERFACE_FLAG_LINK_UP);
456       ad->flags |= AF_XDP_DEVICE_F_ADMIN_UP;
457     }
458   else
459     {
460       vnet_hw_interface_set_flags (vnm, ad->hw_if_index, 0);
461       ad->flags &= ~AF_XDP_DEVICE_F_ADMIN_UP;
462     }
463   return 0;
464 }
465
466 static void
467 af_xdp_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
468                                 u32 node_index)
469 {
470   af_xdp_main_t *am = &af_xdp_main;
471   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
472   af_xdp_device_t *ad = pool_elt_at_index (am->devices, hw->dev_instance);
473
474   /* Shut off redirection */
475   if (node_index == ~0)
476     {
477       ad->per_interface_next_index = node_index;
478       return;
479     }
480
481   ad->per_interface_next_index =
482     vlib_node_add_next (vlib_get_main (), af_xdp_input_node.index,
483                         node_index);
484 }
485
486 static char *af_xdp_tx_func_error_strings[] = {
487 #define _(n,s) s,
488   foreach_af_xdp_tx_func_error
489 #undef _
490 };
491
492 static void
493 af_xdp_clear (u32 dev_instance)
494 {
495   af_xdp_main_t *am = &af_xdp_main;
496   af_xdp_device_t *ad = pool_elt_at_index (am->devices, dev_instance);
497   clib_error_free (ad->error);
498 }
499
500 /* *INDENT-OFF* */
501 VNET_DEVICE_CLASS (af_xdp_device_class) =
502 {
503   .name = "AF_XDP interface",
504   .format_device = format_af_xdp_device,
505   .format_device_name = format_af_xdp_device_name,
506   .admin_up_down_function = af_xdp_interface_admin_up_down,
507   .rx_redirect_to_node = af_xdp_set_interface_next_node,
508   .tx_function_n_errors = AF_XDP_TX_N_ERROR,
509   .tx_function_error_strings = af_xdp_tx_func_error_strings,
510   .mac_addr_change_function = af_xdp_mac_change,
511   .clear_counters = af_xdp_clear,
512 };
513 /* *INDENT-ON* */
514
515 clib_error_t *
516 af_xdp_init (vlib_main_t * vm)
517 {
518   af_xdp_main_t *am = &af_xdp_main;
519
520   am->log_class = vlib_log_register_class ("af_xdp", 0);
521
522   return 0;
523 }
524
525 VLIB_INIT_FUNCTION (af_xdp_init);
526
527 /*
528  * fd.io coding-style-patch-verification: ON
529  *
530  * Local Variables:
531  * eval: (c-set-style "gnu")
532  * End:
533  */