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