af_xdp: AF_XDP input plugin
[vpp.git] / src / plugins / af_xdp / input.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 <poll.h>
19 #include <vlib/vlib.h>
20 #include <vlib/unix/unix.h>
21 #include <vlib/pci/pci.h>
22 #include <vnet/ethernet/ethernet.h>
23 #include <vnet/devices/devices.h>
24 #include "af_xdp.h"
25
26 #define foreach_af_xdp_input_error \
27   _(POLL_REQUIRED, "poll required") \
28   _(POLL_FAILURES, "poll failures")
29
30 typedef enum
31 {
32 #define _(f,s) AF_XDP_INPUT_ERROR_##f,
33   foreach_af_xdp_input_error
34 #undef _
35     AF_XDP_INPUT_N_ERROR,
36 } af_xdp_input_error_t;
37
38 static __clib_unused char *af_xdp_input_error_strings[] = {
39 #define _(n,s) s,
40   foreach_af_xdp_input_error
41 #undef _
42 };
43
44 static_always_inline void
45 af_xdp_device_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
46                            u32 n_left, const u32 * bi, u32 next_index,
47                            u32 hw_if_index)
48 {
49   u32 n_trace;
50
51   if (PREDICT_TRUE (0 == (n_trace = vlib_get_trace_count (vm, node))))
52     return;
53
54   while (n_trace && n_left)
55     {
56       vlib_buffer_t *b;
57       af_xdp_input_trace_t *tr;
58       b = vlib_get_buffer (vm, bi[0]);
59       vlib_trace_buffer (vm, node, next_index, b,
60                          /* follow_chain */ 0);
61       tr = vlib_add_trace (vm, node, b, sizeof (*tr));
62       tr->next_index = next_index;
63       tr->hw_if_index = hw_if_index;
64
65       n_trace--;
66       n_left--;
67       bi++;
68     }
69
70   vlib_set_trace_count (vm, node, n_trace);
71 }
72
73 static_always_inline void
74 af_xdp_device_input_refill_db (vlib_main_t * vm,
75                                const vlib_node_runtime_t * node,
76                                af_xdp_device_t * ad, af_xdp_rxq_t * rxq,
77                                const u32 n_alloc)
78 {
79   int ret;
80
81   xsk_ring_prod__submit (&rxq->fq, n_alloc);
82
83   if (!xsk_ring_prod__needs_wakeup (&rxq->fq))
84     return;
85
86   vlib_error_count (vm, node->node_index, AF_XDP_INPUT_ERROR_POLL_REQUIRED,
87                     1);
88
89   struct pollfd fd = {.fd = rxq->xsk_fd,.events = POLLIN };
90   ret = poll (&fd, 1, 0);
91   if (PREDICT_TRUE (ret >= 0))
92     return;
93
94   /* something bad is happening */
95   vlib_error_count (vm, node->node_index, AF_XDP_INPUT_ERROR_POLL_FAILURES,
96                     1);
97   af_xdp_device_error (ad, "poll() failed");
98 }
99
100 static_always_inline void
101 af_xdp_device_input_refill (vlib_main_t * vm,
102                             const vlib_node_runtime_t * node,
103                             af_xdp_device_t * ad, af_xdp_rxq_t * rxq,
104                             const int copy)
105 {
106   __u64 *fill;
107   const u32 size = rxq->fq.size;
108   const u32 mask = size - 1;
109   u32 bis[VLIB_FRAME_SIZE], *bi = bis;
110   u32 n_alloc, n, n_wrap;
111   u32 idx;
112
113   ASSERT (mask == rxq->fq.mask);
114
115   /* do not enqueue more packet than ring space */
116   n_alloc = xsk_prod_nb_free (&rxq->fq, 16);
117   /* do not bother to allocate if too small */
118   if (n_alloc < 16)
119     return;
120
121   n_alloc = clib_min (n_alloc, ARRAY_LEN (bis));
122   n_alloc = vlib_buffer_alloc_from_pool (vm, bis, n_alloc, ad->pool);
123   n = xsk_ring_prod__reserve (&rxq->fq, n_alloc, &idx);
124   ASSERT (n == n_alloc);
125
126   fill = xsk_ring_prod__fill_addr (&rxq->fq, idx);
127   n = clib_min (n_alloc, size - (idx & mask));
128   n_wrap = n_alloc - n;
129
130   /*
131    * Note about headroom: for some reasons, there seem to be a discrepency
132    * between 0-copy and copy mode:
133    *   - 0-copy: XDP_PACKET_HEADROOM will be added to the user headroom
134    *   - copy: nothing is added to the user headroom
135    * We privileged 0-copy and set headroom to 0. As XDP_PACKET_HEADROOM ==
136    * sizeof(vlib_buffer_t), data will correctly point to vlib_buffer_t->data.
137    * In copy mode, we have to add sizeof(vlib_buffer_t) to desc offset during
138    * refill.
139    */
140   STATIC_ASSERT (sizeof (vlib_buffer_t) == XDP_PACKET_HEADROOM, "wrong size");
141 #define bi2addr(bi) \
142   (((bi) << CLIB_LOG2_CACHE_LINE_BYTES) + (copy ? sizeof(vlib_buffer_t) : 0))
143
144 wrap_around:
145
146   while (n >= 8)
147     {
148 #ifdef CLIB_HAVE_VEC256
149       u64x4 b0 = u32x4_extend_to_u64x4 (*(u32x4u *) (bi + 0));
150       u64x4 b1 = u32x4_extend_to_u64x4 (*(u32x4u *) (bi + 4));
151       *(u64x4u *) (fill + 0) = bi2addr (b0);
152       *(u64x4u *) (fill + 4) = bi2addr (b1);
153 #else
154       fill[0] = bi2addr (bi[0]);
155       fill[1] = bi2addr (bi[1]);
156       fill[2] = bi2addr (bi[2]);
157       fill[3] = bi2addr (bi[3]);
158       fill[4] = bi2addr (bi[4]);
159       fill[5] = bi2addr (bi[5]);
160       fill[6] = bi2addr (bi[6]);
161       fill[7] = bi2addr (bi[7]);
162 #endif
163       fill += 8;
164       bi += 8;
165       n -= 8;
166     }
167
168   while (n >= 1)
169     {
170       fill[0] = bi2addr (bi[0]);
171       fill += 1;
172       bi += 1;
173       n -= 1;
174     }
175
176   if (n_wrap)
177     {
178       fill = xsk_ring_prod__fill_addr (&rxq->fq, 0);
179       n = n_wrap;
180       n_wrap = 0;
181       goto wrap_around;
182     }
183
184   af_xdp_device_input_refill_db (vm, node, ad, rxq, n_alloc);
185 }
186
187 static_always_inline void
188 af_xdp_device_input_ethernet (vlib_main_t * vm, vlib_node_runtime_t * node,
189                               const u32 next_index, const u32 sw_if_index,
190                               const u32 hw_if_index)
191 {
192   vlib_next_frame_t *nf;
193   vlib_frame_t *f;
194   ethernet_input_frame_t *ef;
195
196   if (PREDICT_FALSE (VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT != next_index))
197     return;
198
199   nf =
200     vlib_node_runtime_get_next_frame (vm, node,
201                                       VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT);
202   f = vlib_get_frame (vm, nf->frame);
203   f->flags = ETH_INPUT_FRAME_F_SINGLE_SW_IF_IDX;
204
205   ef = vlib_frame_scalar_args (f);
206   ef->sw_if_index = sw_if_index;
207   ef->hw_if_index = hw_if_index;
208 }
209
210 static_always_inline u32
211 af_xdp_device_input_bufs (vlib_main_t * vm, const af_xdp_device_t * ad,
212                           af_xdp_rxq_t * rxq, u32 * bis, const u32 n_rx,
213                           vlib_buffer_t * bt, u32 idx, const int copy)
214 {
215   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
216   u16 lens[VLIB_FRAME_SIZE], *len = lens;
217   const u32 mask = rxq->rx.mask;
218   u32 n = n_rx, *bi = bis, bytes = 0;
219
220 #define addr2bi(addr) \
221   (((addr) - (copy ? sizeof(vlib_buffer_t) : 0)) >> CLIB_LOG2_CACHE_LINE_BYTES)
222
223   while (n >= 1)
224     {
225       const struct xdp_desc *desc = xsk_ring_cons__rx_desc (&rxq->rx, idx);
226       bi[0] = addr2bi (xsk_umem__extract_addr (desc->addr));
227       ASSERT (vlib_buffer_is_known (vm, bi[0]) ==
228               VLIB_BUFFER_KNOWN_ALLOCATED);
229       len[0] = desc->len;
230       idx = (idx + 1) & mask;
231       bi += 1;
232       len += 1;
233       n -= 1;
234     }
235
236   vlib_get_buffers (vm, bis, bufs, n_rx);
237
238   n = n_rx;
239   len = lens;
240
241   while (n >= 8)
242     {
243       vlib_prefetch_buffer_header (b[4], LOAD);
244       vlib_buffer_copy_template (b[0], bt);
245       bytes += b[0]->current_length = len[0];
246
247       vlib_prefetch_buffer_header (b[5], LOAD);
248       vlib_buffer_copy_template (b[1], bt);
249       bytes += b[1]->current_length = len[1];
250
251       vlib_prefetch_buffer_header (b[6], LOAD);
252       vlib_buffer_copy_template (b[2], bt);
253       bytes += b[2]->current_length = len[2];
254
255       vlib_prefetch_buffer_header (b[7], LOAD);
256       vlib_buffer_copy_template (b[3], bt);
257       bytes += b[3]->current_length = len[3];
258
259       b += 4;
260       len += 4;
261       n -= 4;
262     }
263
264   while (n >= 1)
265     {
266       vlib_buffer_copy_template (b[0], bt);
267       bytes += b[0]->current_length = len[0];
268       b += 1;
269       len += 1;
270       n -= 1;
271     }
272
273   xsk_ring_cons__release (&rxq->rx, n_rx);
274   return bytes;
275 }
276
277 static_always_inline uword
278 af_xdp_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
279                             vlib_frame_t * frame, af_xdp_device_t * ad,
280                             u16 qid, const int copy)
281 {
282   vnet_main_t *vnm = vnet_get_main ();
283   af_xdp_rxq_t *rxq = vec_elt_at_index (ad->rxqs, qid);
284   vlib_buffer_t bt;
285   u32 next_index, *to_next, n_left_to_next;
286   u32 n_rx_packets, n_rx_bytes;
287   u32 idx;
288
289   n_rx_packets = xsk_ring_cons__peek (&rxq->rx, VLIB_FRAME_SIZE, &idx);
290
291   if (PREDICT_FALSE (0 == n_rx_packets))
292     goto refill;
293
294   vlib_buffer_copy_template (&bt, ad->buffer_template);
295   next_index = ad->per_interface_next_index;
296   if (PREDICT_FALSE (vnet_device_input_have_features (ad->sw_if_index)))
297     vnet_feature_start_device_input_x1 (ad->sw_if_index, &next_index, &bt);
298
299   vlib_get_new_next_frame (vm, node, next_index, to_next, n_left_to_next);
300
301   n_rx_bytes =
302     af_xdp_device_input_bufs (vm, ad, rxq, to_next, n_rx_packets, &bt, idx,
303                               copy);
304   af_xdp_device_input_ethernet (vm, node, next_index, ad->sw_if_index,
305                                 ad->hw_if_index);
306
307   vlib_put_next_frame (vm, node, next_index, n_left_to_next - n_rx_packets);
308
309   af_xdp_device_input_trace (vm, node, n_rx_packets, to_next, next_index,
310                              ad->hw_if_index);
311
312   vlib_increment_combined_counter
313     (vnm->interface_main.combined_sw_if_counters +
314      VNET_INTERFACE_COUNTER_RX, vm->thread_index,
315      ad->hw_if_index, n_rx_packets, n_rx_bytes);
316
317 refill:
318   af_xdp_device_input_refill (vm, node, ad, rxq, copy);
319
320   return n_rx_packets;
321 }
322
323 VLIB_NODE_FN (af_xdp_input_node) (vlib_main_t * vm,
324                                   vlib_node_runtime_t * node,
325                                   vlib_frame_t * frame)
326 {
327   u32 n_rx = 0;
328   af_xdp_main_t *am = &af_xdp_main;
329   vnet_device_input_runtime_t *rt = (void *) node->runtime_data;
330   vnet_device_and_queue_t *dq;
331
332   foreach_device_and_queue (dq, rt->devices_and_queues)
333   {
334     af_xdp_device_t *ad;
335     ad = vec_elt_at_index (am->devices, dq->dev_instance);
336     if ((ad->flags & AF_XDP_DEVICE_F_ADMIN_UP) == 0)
337       continue;
338     if (PREDICT_TRUE (ad->flags & AF_XDP_DEVICE_F_ZEROCOPY))
339       n_rx += af_xdp_device_input_inline (vm, node, frame, ad, dq->queue_id,
340                                           /* copy */ 0);
341     else
342       n_rx += af_xdp_device_input_inline (vm, node, frame, ad, dq->queue_id,
343                                           /* copy */ 1);
344   }
345   return n_rx;
346 }
347
348 /* *INDENT-OFF* */
349 VLIB_REGISTER_NODE (af_xdp_input_node) = {
350   .name = "af_xdp-input",
351   .sibling_of = "device-input",
352   .format_trace = format_af_xdp_input_trace,
353   .type = VLIB_NODE_TYPE_INPUT,
354   .state = VLIB_NODE_STATE_DISABLED,
355   .n_errors = AF_XDP_INPUT_N_ERROR,
356   .error_strings = af_xdp_input_error_strings,
357   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
358 };
359 /* *INDENT-ON* */
360
361 /*
362  * fd.io coding-style-patch-verification: ON
363  *
364  * Local Variables:
365  * eval: (c-set-style "gnu")
366  * End:
367  */