f4ef6505a0f4b061ba437f8ddb4216f8141fdd7d
[vpp.git] / src / plugins / rdma / 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 <vlib/vlib.h>
19 #include <vlib/unix/unix.h>
20 #include <vlib/pci/pci.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/devices/devices.h>
23
24 #include <rdma/rdma.h>
25
26 #define foreach_rdma_input_error \
27   _(BUFFER_ALLOC, "buffer alloc error")
28
29 typedef enum
30 {
31 #define _(f,s) RDMA_INPUT_ERROR_##f,
32   foreach_rdma_input_error
33 #undef _
34     RDMA_INPUT_N_ERROR,
35 } rdma_input_error_t;
36
37 static __clib_unused char *rdma_input_error_strings[] = {
38 #define _(n,s) s,
39   foreach_rdma_input_error
40 #undef _
41 };
42
43
44 static_always_inline void
45 ibv_set_recv_wr_and_sge (struct ibv_recv_wr *w, struct ibv_sge *s, u64 va,
46                          u32 data_size, u32 lkey)
47 {
48   s[0].addr = va;
49   s[0].length = data_size;
50   s[0].lkey = lkey;
51   w[0].next = w + 1;
52   w[0].sg_list = s;
53   w[0].num_sge = 1;
54 }
55
56 static_always_inline void
57 rdma_device_input_refill (vlib_main_t * vm, rdma_device_t * rd,
58                           rdma_rxq_t * rxq)
59 {
60   u32 n_alloc, n;
61   struct ibv_recv_wr wr[VLIB_FRAME_SIZE], *w = wr;
62   struct ibv_sge sge[VLIB_FRAME_SIZE], *s = sge;
63   u32 mask = rxq->size - 1;
64   u32 slot = rxq->tail & mask;
65   u32 *bufs = rxq->bufs + slot;
66   u32 data_size = vlib_buffer_get_default_data_size (vm);
67   u32 lkey = rd->lkey;
68
69   /* do not enqueue more packet than ring space */
70   n_alloc = clib_min (VLIB_FRAME_SIZE, rxq->size - (rxq->tail - rxq->head));
71
72   /* do not bother to allocate if too small */
73   if (n_alloc < 16)
74     return;
75
76   /* avoid wrap-around logic in core loop */
77   n_alloc = clib_min (n_alloc, rxq->size - slot);
78
79   n_alloc &= ~7;                /* round to 8 */
80
81   n = vlib_buffer_alloc_to_ring_from_pool (vm, rxq->bufs, slot, rxq->size,
82                                            n_alloc, rd->pool);
83
84   if (PREDICT_FALSE (n != n_alloc))
85     {
86       u32 n_free;
87       if (n < 8)
88         {
89           if (n)
90             vlib_buffer_free_from_ring (vm, rxq->bufs, slot, rxq->size, n);
91           return;
92         }
93
94       /* partial allocation, round and return rest */
95       n_free = n - (n & 7);
96       n -= n_free;
97       if (n_free)
98         vlib_buffer_free_from_ring (vm, rxq->bufs, (slot + n) & mask,
99                                     rxq->size, n_free);
100     }
101
102   n_alloc = n;
103
104   while (n >= 8)
105     {
106       u64 va[8];
107       if (PREDICT_TRUE (n >= 16))
108         {
109           clib_prefetch_store (s + 16);
110           clib_prefetch_store (w + 16);
111         }
112
113       vlib_get_buffers_with_offset (vm, bufs, (void **) va, 8,
114                                     sizeof (vlib_buffer_t));
115
116       ibv_set_recv_wr_and_sge (w++, s++, va[0], data_size, lkey);
117       ibv_set_recv_wr_and_sge (w++, s++, va[1], data_size, lkey);
118       ibv_set_recv_wr_and_sge (w++, s++, va[2], data_size, lkey);
119       ibv_set_recv_wr_and_sge (w++, s++, va[3], data_size, lkey);
120       ibv_set_recv_wr_and_sge (w++, s++, va[4], data_size, lkey);
121       ibv_set_recv_wr_and_sge (w++, s++, va[5], data_size, lkey);
122       ibv_set_recv_wr_and_sge (w++, s++, va[6], data_size, lkey);
123       ibv_set_recv_wr_and_sge (w++, s++, va[7], data_size, lkey);
124
125       bufs += 8;
126       n -= 8;
127     }
128
129   w[-1].next = 0;               /* fix next pointer in WR linked-list last item */
130
131   n = n_alloc;
132   if (ibv_post_wq_recv (rxq->wq, wr, &w) != 0)
133     {
134       n = w - wr;
135       vlib_buffer_free_from_ring (vm, rxq->bufs, slot + n, rxq->size,
136                                   n_alloc - n);
137     }
138
139   rxq->tail += n;
140 }
141
142 static_always_inline void
143 rdma_device_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
144                          const rdma_device_t * rd, u32 n_left, const u32 * bi,
145                          u32 next_index)
146 {
147   u32 n_trace, i;
148
149   if (PREDICT_TRUE (0 == (n_trace = vlib_get_trace_count (vm, node))))
150     return;
151
152   i = 0;
153   while (n_trace && n_left)
154     {
155       vlib_buffer_t *b;
156       rdma_input_trace_t *tr;
157       b = vlib_get_buffer (vm, bi[0]);
158       vlib_trace_buffer (vm, node, next_index, b,
159                          /* follow_chain */ 0);
160       tr = vlib_add_trace (vm, node, b, sizeof (*tr));
161       tr->next_index = next_index;
162       tr->hw_if_index = rd->hw_if_index;
163
164       /* next */
165       n_trace--;
166       n_left--;
167       bi++;
168       i++;
169     }
170   vlib_set_trace_count (vm, node, n_trace);
171 }
172
173 static_always_inline void
174 rdma_device_input_ethernet (vlib_main_t * vm, vlib_node_runtime_t * node,
175                             const rdma_device_t * rd, u32 next_index)
176 {
177   vlib_next_frame_t *nf;
178   vlib_frame_t *f;
179   ethernet_input_frame_t *ef;
180
181   if (PREDICT_FALSE (VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT != next_index))
182     return;
183
184   nf =
185     vlib_node_runtime_get_next_frame (vm, node,
186                                       VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT);
187   f = vlib_get_frame (vm, nf->frame);
188   f->flags = ETH_INPUT_FRAME_F_SINGLE_SW_IF_IDX;
189   /* FIXME: f->flags |= ETH_INPUT_FRAME_F_IP4_CKSUM_OK; */
190
191   ef = vlib_frame_scalar_args (f);
192   ef->sw_if_index = rd->sw_if_index;
193   ef->hw_if_index = rd->hw_if_index;
194 }
195
196 static_always_inline u32
197 rdma_device_input_bufs (vlib_main_t * vm, const rdma_device_t * rd, u32 * bi,
198                         struct ibv_wc * wc, u32 n_left_from,
199                         vlib_buffer_t * bt)
200 {
201   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
202   u32 n_rx_bytes = 0;
203
204   vlib_get_buffers (vm, bi, bufs, n_left_from);
205   ASSERT (bt->buffer_pool_index == bufs[0]->buffer_pool_index);
206
207   while (n_left_from >= 4)
208     {
209       if (PREDICT_TRUE (n_left_from >= 8))
210         {
211           CLIB_PREFETCH (&wc[4 + 0], CLIB_CACHE_LINE_BYTES, LOAD);
212           CLIB_PREFETCH (&wc[4 + 1], CLIB_CACHE_LINE_BYTES, LOAD);
213           CLIB_PREFETCH (&wc[4 + 2], CLIB_CACHE_LINE_BYTES, LOAD);
214           CLIB_PREFETCH (&wc[4 + 3], CLIB_CACHE_LINE_BYTES, LOAD);
215           vlib_prefetch_buffer_header (b[4 + 0], STORE);
216           vlib_prefetch_buffer_header (b[4 + 1], STORE);
217           vlib_prefetch_buffer_header (b[4 + 2], STORE);
218           vlib_prefetch_buffer_header (b[4 + 3], STORE);
219         }
220
221       vlib_buffer_copy_template (b[0], bt);
222       vlib_buffer_copy_template (b[1], bt);
223       vlib_buffer_copy_template (b[2], bt);
224       vlib_buffer_copy_template (b[3], bt);
225
226       n_rx_bytes += b[0]->current_length = wc[0].byte_len;
227       n_rx_bytes += b[1]->current_length = wc[1].byte_len;
228       n_rx_bytes += b[2]->current_length = wc[2].byte_len;
229       n_rx_bytes += b[3]->current_length = wc[3].byte_len;
230
231       b += 4;
232       wc += 4;
233       n_left_from -= 4;
234     }
235
236   while (n_left_from >= 1)
237     {
238       vlib_buffer_copy_template (b[0], bt);
239       n_rx_bytes += b[0]->current_length = wc[0].byte_len;
240
241       b += 1;
242       wc += 1;
243       n_left_from -= 1;
244     }
245
246   return n_rx_bytes;
247 }
248
249 static_always_inline uword
250 rdma_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
251                           vlib_frame_t * frame, rdma_device_t * rd, u16 qid)
252 {
253   rdma_main_t *rm = &rdma_main;
254   vnet_main_t *vnm = vnet_get_main ();
255   rdma_per_thread_data_t *ptd = vec_elt_at_index (rm->per_thread_data,
256                                                   vm->thread_index);
257   rdma_rxq_t *rxq = vec_elt_at_index (rd->rxqs, qid);
258   struct ibv_wc wc[VLIB_FRAME_SIZE];
259   vlib_buffer_t bt;
260   u32 next_index, *to_next, n_left_to_next;
261   u32 n_rx_packets, n_rx_bytes;
262   u32 mask = rxq->size - 1;
263
264   ASSERT (rxq->size >= VLIB_FRAME_SIZE && is_pow2 (rxq->size));
265   ASSERT (rxq->tail - rxq->head <= rxq->size);
266
267   n_rx_packets = ibv_poll_cq (rxq->cq, VLIB_FRAME_SIZE, wc);
268   ASSERT (n_rx_packets <= rxq->tail - rxq->head);
269
270   if (PREDICT_FALSE (n_rx_packets <= 0))
271     goto refill;
272
273   /* init buffer template */
274   vlib_buffer_copy_template (&bt, &ptd->buffer_template);
275   vnet_buffer (&bt)->sw_if_index[VLIB_RX] = rd->sw_if_index;
276   bt.buffer_pool_index = rd->pool;
277
278   /* update buffer template for input feature arcs if any */
279   next_index = rd->per_interface_next_index;
280   if (PREDICT_FALSE (vnet_device_input_have_features (rd->sw_if_index)))
281     vnet_feature_start_device_input_x1 (rd->sw_if_index, &next_index, &bt);
282
283   vlib_get_new_next_frame (vm, node, next_index, to_next, n_left_to_next);
284   ASSERT (n_rx_packets <= n_left_to_next);
285
286   vlib_buffer_copy_indices_from_ring (to_next, rxq->bufs, rxq->head & mask,
287                                       rxq->size, n_rx_packets);
288   n_rx_bytes = rdma_device_input_bufs (vm, rd, to_next, wc, n_rx_packets,
289                                        &bt);
290
291   rdma_device_input_ethernet (vm, node, rd, next_index);
292
293   vlib_put_next_frame (vm, node, next_index, n_left_to_next - n_rx_packets);
294
295   rxq->head += n_rx_packets;
296
297   rdma_device_input_trace (vm, node, rd, n_rx_packets, to_next, next_index);
298
299   vlib_increment_combined_counter
300     (vnm->interface_main.combined_sw_if_counters +
301      VNET_INTERFACE_COUNTER_RX, vm->thread_index,
302      rd->hw_if_index, n_rx_packets, n_rx_bytes);
303
304 refill:
305   rdma_device_input_refill (vm, rd, rxq);
306
307   return n_rx_packets;
308 }
309
310 VLIB_NODE_FN (rdma_input_node) (vlib_main_t * vm,
311                                 vlib_node_runtime_t * node,
312                                 vlib_frame_t * frame)
313 {
314   u32 n_rx = 0;
315   rdma_main_t *rm = &rdma_main;
316   vnet_device_input_runtime_t *rt = (void *) node->runtime_data;
317   vnet_device_and_queue_t *dq;
318
319   foreach_device_and_queue (dq, rt->devices_and_queues)
320   {
321     rdma_device_t *rd;
322     rd = vec_elt_at_index (rm->devices, dq->dev_instance);
323     if (PREDICT_TRUE (rd->flags & RDMA_DEVICE_F_ADMIN_UP))
324       n_rx += rdma_device_input_inline (vm, node, frame, rd, dq->queue_id);
325   }
326   return n_rx;
327 }
328
329 /* *INDENT-OFF* */
330 VLIB_REGISTER_NODE (rdma_input_node) = {
331   .name = "rdma-input",
332   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
333   .sibling_of = "device-input",
334   .format_trace = format_rdma_input_trace,
335   .type = VLIB_NODE_TYPE_INPUT,
336   .state = VLIB_NODE_STATE_DISABLED,
337   .n_errors = RDMA_INPUT_N_ERROR,
338   .error_strings = rdma_input_error_strings,
339 };
340
341 /* *INDENT-ON* */
342
343
344 /*
345  * fd.io coding-style-patch-verification: ON
346  *
347  * Local Variables:
348  * eval: (c-set-style "gnu")
349  * End:
350  */