blob: e560c29e5e75603cf116e347cf461eb3e14f5a75 [file] [log] [blame]
Damjan Marion8389fb92017-10-13 18:29:53 +02001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2016 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 <sys/types.h>
19#include <sys/stat.h>
20#include <fcntl.h>
Damjan Marion8389fb92017-10-13 18:29:53 +020021
22#include <vlib/vlib.h>
23#include <vlib/unix/unix.h>
24#include <vnet/ethernet/ethernet.h>
Milan Lenco73e7f422017-12-14 10:04:25 +010025#include <vnet/ip/ip4_packet.h>
26#include <vnet/ip/ip6_packet.h>
Damjan Marion8389fb92017-10-13 18:29:53 +020027#include <vnet/devices/virtio/virtio.h>
28
29#define foreach_virtio_tx_func_error \
30_(NO_FREE_SLOTS, "no free tx slots") \
31_(TRUNC_PACKET, "packet > buffer size -- truncated in tx ring") \
32_(PENDING_MSGS, "pending msgs in tx ring") \
33_(NO_TX_QUEUES, "no tx queues")
34
35typedef enum
36{
Mohsin Kazmid6c15af2018-10-23 18:00:47 +020037#define _(f,s) VIRTIO_TX_ERROR_##f,
Damjan Marion8389fb92017-10-13 18:29:53 +020038 foreach_virtio_tx_func_error
39#undef _
Mohsin Kazmid6c15af2018-10-23 18:00:47 +020040 VIRTIO_TX_N_ERROR,
Damjan Marion8389fb92017-10-13 18:29:53 +020041} virtio_tx_func_error_t;
42
43static char *virtio_tx_func_error_strings[] = {
44#define _(n,s) s,
45 foreach_virtio_tx_func_error
46#undef _
47};
48
Filip Tehlaraee73642019-03-13 05:50:44 -070049#ifndef CLIB_MARCH_VARIANT
Damjan Marion8389fb92017-10-13 18:29:53 +020050u8 *
51format_virtio_device_name (u8 * s, va_list * args)
52{
53 u32 dev_instance = va_arg (*args, u32);
54 virtio_main_t *mm = &virtio_main;
55 virtio_if_t *vif = pool_elt_at_index (mm->interfaces, dev_instance);
56
57 if (vif->type == VIRTIO_IF_TYPE_TAP)
Mohsin Kazmid6c15af2018-10-23 18:00:47 +020058 s = format (s, "tap%u", vif->id);
59 else if (vif->type == VIRTIO_IF_TYPE_PCI)
60 s = format (s, "virtio-%x/%x/%x/%x", vif->pci_addr.domain,
61 vif->pci_addr.bus, vif->pci_addr.slot,
62 vif->pci_addr.function);
Damjan Marion8389fb92017-10-13 18:29:53 +020063 else
Mohsin Kazmid6c15af2018-10-23 18:00:47 +020064 s = format (s, "virtio-%lu", vif->dev_instance);
Damjan Marion8389fb92017-10-13 18:29:53 +020065
66 return s;
67}
Filip Tehlaraee73642019-03-13 05:50:44 -070068#endif /* CLIB_MARCH_VARIANT */
Damjan Marion8389fb92017-10-13 18:29:53 +020069
70static u8 *
71format_virtio_device (u8 * s, va_list * args)
72{
73 u32 dev_instance = va_arg (*args, u32);
74 int verbose = va_arg (*args, int);
75 u32 indent = format_get_indent (s);
76
77 s = format (s, "VIRTIO interface");
78 if (verbose)
79 {
80 s = format (s, "\n%U instance %u", format_white_space, indent + 2,
81 dev_instance);
82 }
83 return s;
84}
85
86static u8 *
87format_virtio_tx_trace (u8 * s, va_list * args)
88{
89 s = format (s, "Unimplemented...");
90 return s;
91}
92
Mohsin Kazmiaea0df32019-05-23 14:32:58 +020093static_always_inline void
94virtio_free_used_device_desc (vlib_main_t * vm, virtio_vring_t * vring)
Damjan Marion8389fb92017-10-13 18:29:53 +020095{
96 u16 used = vring->desc_in_use;
97 u16 sz = vring->size;
98 u16 mask = sz - 1;
99 u16 last = vring->last_used_idx;
100 u16 n_left = vring->used->idx - last;
101
102 if (n_left == 0)
103 return;
104
105 while (n_left)
106 {
107 struct vring_used_elem *e = &vring->used->ring[last & mask];
Mohsin Kazmiaea0df32019-05-23 14:32:58 +0200108 u16 slot, n_buffers;
109 slot = n_buffers = e->id;
Damjan Marion8389fb92017-10-13 18:29:53 +0200110
Mohsin Kazmiaea0df32019-05-23 14:32:58 +0200111 while (e->id == n_buffers)
112 {
113 n_left--;
114 last++;
115 n_buffers++;
116 if (n_left == 0)
117 break;
118 e = &vring->used->ring[last & mask];
119 }
120 vlib_buffer_free_from_ring (vm, vring->buffers, slot,
121 sz, (n_buffers - slot));
122 used -= (n_buffers - slot);
123
124 if (n_left > 0)
125 {
126 slot = e->id;
127
128 vlib_buffer_free (vm, &vring->buffers[slot], 1);
129 used--;
130 last++;
131 n_left--;
132 }
Damjan Marion8389fb92017-10-13 18:29:53 +0200133 }
134 vring->desc_in_use = used;
135 vring->last_used_idx = last;
136}
137
138static_always_inline u16
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200139add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif,
140 virtio_vring_t * vring, u32 bi, u16 avail, u16 next,
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200141 u16 mask, int do_gso)
Damjan Marion8389fb92017-10-13 18:29:53 +0200142{
143 u16 n_added = 0;
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200144 int hdr_sz = vif->virtio_net_hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200145 struct vring_desc *d;
146 d = &vring->desc[next];
147 vlib_buffer_t *b = vlib_get_buffer (vm, bi);
Damjan Marion508cabc2018-02-08 19:49:22 +0100148 struct virtio_net_hdr_v1 *hdr = vlib_buffer_get_current (b) - hdr_sz;
149
Dave Barachb7b92992018-10-17 10:38:51 -0400150 clib_memset (hdr, 0, hdr_sz);
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200151 if (do_gso && (b->flags & VNET_BUFFER_F_GSO))
152 {
153 if (b->flags & VNET_BUFFER_F_IS_IP4)
154 {
155 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
156 hdr->gso_size = vnet_buffer2 (b)->gso_size;
157 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
158 hdr->csum_start = vnet_buffer (b)->l4_hdr_offset; // 0x22;
159 hdr->csum_offset = 0x10;
160 }
161 else
162 {
163 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
164 hdr->gso_size = vnet_buffer2 (b)->gso_size;
165 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
166 hdr->csum_start = vnet_buffer (b)->l4_hdr_offset; // 0x36;
167 hdr->csum_offset = 0x10;
168 }
169 }
Damjan Marion8389fb92017-10-13 18:29:53 +0200170
171 if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
172 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200173 d->addr =
174 ((vif->type == VIRTIO_IF_TYPE_PCI) ? vlib_buffer_get_current_pa (vm,
175 b) :
176 pointer_to_uword (vlib_buffer_get_current (b))) - hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200177 d->len = b->current_length + hdr_sz;
178 d->flags = 0;
179 }
180 else
181 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200182 /*
183 * We are using single vlib_buffer_t for indirect descriptor(s)
184 * chain. Single descriptor is 16 bytes and vlib_buffer_t
185 * has 2048 bytes space. So maximum long chain can have 128
186 * (=2048/16) indirect descriptors.
187 * It can easily support 65535 bytes of Jumbo frames with
188 * each data buffer size of 512 bytes minimum.
189 */
Mohsin Kazmiaea0df32019-05-23 14:32:58 +0200190 u32 indirect_buffer = 0;
191 if (PREDICT_FALSE (vlib_buffer_alloc (vm, &indirect_buffer, 1) == 0))
192 return n_added;
193
194 vlib_buffer_t *indirect_desc = vlib_get_buffer (vm, indirect_buffer);
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200195 indirect_desc->current_data = 0;
Mohsin Kazmiaea0df32019-05-23 14:32:58 +0200196 indirect_desc->flags |= VLIB_BUFFER_NEXT_PRESENT;
197 indirect_desc->next_buffer = bi;
198 bi = indirect_buffer;
Damjan Marion8389fb92017-10-13 18:29:53 +0200199
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200200 struct vring_desc *id =
201 (struct vring_desc *) vlib_buffer_get_current (indirect_desc);
202 u32 count = 1;
203 if (vif->type == VIRTIO_IF_TYPE_PCI)
Damjan Marion8389fb92017-10-13 18:29:53 +0200204 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200205 d->addr = vlib_physmem_get_pa (vm, id);
206 id->addr = vlib_buffer_get_current_pa (vm, b) - hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200207
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200208 /*
209 * If VIRTIO_F_ANY_LAYOUT is not negotiated, then virtio_net_hdr
210 * should be presented in separate descriptor and data will start
211 * from next descriptor.
212 */
213 if (PREDICT_TRUE
214 (vif->features & VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)))
215 id->len = b->current_length + hdr_sz;
216 else
217 {
218 id->len = hdr_sz;
219 id->flags = VRING_DESC_F_NEXT;
220 id->next = count;
221 count++;
222 id++;
223 id->addr = vlib_buffer_get_current_pa (vm, b);
224 id->len = b->current_length;
225 }
226 while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
227 {
228 id->flags = VRING_DESC_F_NEXT;
229 id->next = count;
230 count++;
231 id++;
232 b = vlib_get_buffer (vm, b->next_buffer);
233 id->addr = vlib_buffer_get_current_pa (vm, b);
234 id->len = b->current_length;
235 }
236 }
237 else /* VIRTIO_IF_TYPE_TAP */
238 {
239 d->addr = pointer_to_uword (id);
240 /* first buffer in chain */
241 id->addr = pointer_to_uword (vlib_buffer_get_current (b)) - hdr_sz;
242 id->len = b->current_length + hdr_sz;
243
244 while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
245 {
246 id->flags = VRING_DESC_F_NEXT;
247 id->next = count;
248 count++;
249 id++;
250 b = vlib_get_buffer (vm, b->next_buffer);
251 id->addr = pointer_to_uword (vlib_buffer_get_current (b));
252 id->len = b->current_length;
253 }
254 }
255 id->flags = 0;
256 id->next = 0;
257 d->len = count * sizeof (struct vring_desc);
Damjan Marion8389fb92017-10-13 18:29:53 +0200258 d->flags = VRING_DESC_F_INDIRECT;
259 }
260 vring->buffers[next] = bi;
261 vring->avail->ring[avail & mask] = next;
262 n_added++;
263 return n_added;
264}
265
Damjan Marion8389fb92017-10-13 18:29:53 +0200266static_always_inline uword
267virtio_interface_tx_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200268 vlib_frame_t * frame, virtio_if_t * vif,
269 int do_gso)
Damjan Marion8389fb92017-10-13 18:29:53 +0200270{
Damjan Marion8389fb92017-10-13 18:29:53 +0200271 u16 n_left = frame->n_vectors;
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000272 virtio_vring_t *vring;
273 u16 qid = vm->thread_index % vif->num_txqs;
274 vring = vec_elt_at_index (vif->txq_vrings, qid);
Damjan Marion8389fb92017-10-13 18:29:53 +0200275 u16 used, next, avail;
276 u16 sz = vring->size;
277 u16 mask = sz - 1;
Damjan Mariona3d59862018-11-10 10:23:00 +0100278 u32 *buffers = vlib_frame_vector_args (frame);
Damjan Marion8389fb92017-10-13 18:29:53 +0200279
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000280 clib_spinlock_lock_if_init (&vring->lockp);
Damjan Marion829ee532018-02-16 16:13:32 +0100281
Damjan Marione40231b2018-12-20 10:44:47 +0100282 if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0 &&
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200283 (vring->last_kick_avail_idx != vring->avail->idx))
284 virtio_kick (vm, vring, vif);
Damjan Marione40231b2018-12-20 10:44:47 +0100285
Damjan Marion8389fb92017-10-13 18:29:53 +0200286 /* free consumed buffers */
Mohsin Kazmiaea0df32019-05-23 14:32:58 +0200287 virtio_free_used_device_desc (vm, vring);
Damjan Marion8389fb92017-10-13 18:29:53 +0200288
289 used = vring->desc_in_use;
290 next = vring->desc_next;
291 avail = vring->avail->idx;
292
293 while (n_left && used < sz)
294 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200295 u16 n_added = 0;
296 n_added =
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200297 add_buffer_to_slot (vm, vif, vring, buffers[0], avail, next, mask,
298 do_gso);
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200299 if (!n_added)
300 break;
Damjan Marion8389fb92017-10-13 18:29:53 +0200301 avail += n_added;
302 next = (next + n_added) & mask;
303 used += n_added;
304 buffers++;
305 n_left--;
306 }
307
308 if (n_left != frame->n_vectors)
309 {
310 CLIB_MEMORY_STORE_BARRIER ();
311 vring->avail->idx = avail;
312 vring->desc_next = next;
313 vring->desc_in_use = used;
314 if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0)
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200315 virtio_kick (vm, vring, vif);
Damjan Marion8389fb92017-10-13 18:29:53 +0200316 }
317
Damjan Marion8389fb92017-10-13 18:29:53 +0200318 if (n_left)
319 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200320 vlib_error_count (vm, node->node_index, VIRTIO_TX_ERROR_NO_FREE_SLOTS,
Damjan Marion8389fb92017-10-13 18:29:53 +0200321 n_left);
322 vlib_buffer_free (vm, buffers, n_left);
323 }
324
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000325 clib_spinlock_unlock_if_init (&vring->lockp);
Damjan Marion829ee532018-02-16 16:13:32 +0100326
Damjan Marion8389fb92017-10-13 18:29:53 +0200327 return frame->n_vectors - n_left;
328}
329
Filip Tehlaraee73642019-03-13 05:50:44 -0700330VNET_DEVICE_CLASS_TX_FN (virtio_device_class) (vlib_main_t * vm,
331 vlib_node_runtime_t * node,
332 vlib_frame_t * frame)
Damjan Marion8389fb92017-10-13 18:29:53 +0200333{
334 virtio_main_t *nm = &virtio_main;
335 vnet_interface_output_runtime_t *rund = (void *) node->runtime_data;
336 virtio_if_t *vif = pool_elt_at_index (nm->interfaces, rund->dev_instance);
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200337 vnet_main_t *vnm = vnet_get_main ();
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000338
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200339 if (vnm->interface_main.gso_interface_count > 0)
340 return virtio_interface_tx_inline (vm, node, frame, vif, 1 /* do_gso */ );
341 else
342 return virtio_interface_tx_inline (vm, node, frame, vif,
343 0 /* no do_gso */ );
Damjan Marion8389fb92017-10-13 18:29:53 +0200344}
345
346static void
347virtio_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
348 u32 node_index)
349{
350 virtio_main_t *apm = &virtio_main;
351 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
352 virtio_if_t *vif = pool_elt_at_index (apm->interfaces, hw->dev_instance);
353
354 /* Shut off redirection */
355 if (node_index == ~0)
356 {
357 vif->per_interface_next_index = node_index;
358 return;
359 }
360
361 vif->per_interface_next_index =
362 vlib_node_add_next (vlib_get_main (), virtio_input_node.index,
363 node_index);
364}
365
366static void
367virtio_clear_hw_interface_counters (u32 instance)
368{
369 /* Nothing for now */
370}
371
372static clib_error_t *
373virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid,
374 vnet_hw_interface_rx_mode mode)
375{
376 virtio_main_t *mm = &virtio_main;
377 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
378 virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000379 virtio_vring_t *vring = vec_elt_at_index (vif->rxq_vrings, qid);
Damjan Marion8389fb92017-10-13 18:29:53 +0200380
Mohsin Kazmib74fe322019-01-31 13:50:56 +0000381 if (vif->type == VIRTIO_IF_TYPE_PCI && !(vif->support_int_mode))
382 {
383 vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT;
384 return clib_error_return (0, "interrupt mode is not supported");
385 }
386
Damjan Marion8389fb92017-10-13 18:29:53 +0200387 if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
388 vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT;
389 else
390 vring->avail->flags &= ~VIRTIO_RING_FLAG_MASK_INT;
391
392 return 0;
393}
394
395static clib_error_t *
396virtio_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
397{
398 virtio_main_t *mm = &virtio_main;
399 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
400 virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
Damjan Marion8389fb92017-10-13 18:29:53 +0200401
402 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
403 vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP;
404 else
405 vif->flags &= ~VIRTIO_IF_FLAG_ADMIN_UP;
406
Damjan Marion8389fb92017-10-13 18:29:53 +0200407 return 0;
408}
409
410static clib_error_t *
411virtio_subif_add_del_function (vnet_main_t * vnm,
412 u32 hw_if_index,
413 struct vnet_sw_interface_t *st, int is_add)
414{
415 /* Nothing for now */
416 return 0;
417}
418
419/* *INDENT-OFF* */
420VNET_DEVICE_CLASS (virtio_device_class) = {
421 .name = "virtio",
Damjan Marion8389fb92017-10-13 18:29:53 +0200422 .format_device_name = format_virtio_device_name,
423 .format_device = format_virtio_device,
424 .format_tx_trace = format_virtio_tx_trace,
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200425 .tx_function_n_errors = VIRTIO_TX_N_ERROR,
Damjan Marion8389fb92017-10-13 18:29:53 +0200426 .tx_function_error_strings = virtio_tx_func_error_strings,
427 .rx_redirect_to_node = virtio_set_interface_next_node,
428 .clear_counters = virtio_clear_hw_interface_counters,
429 .admin_up_down_function = virtio_interface_admin_up_down,
430 .subif_add_del_function = virtio_subif_add_del_function,
431 .rx_mode_change_function = virtio_interface_rx_mode_change,
432};
Damjan Marion8389fb92017-10-13 18:29:53 +0200433/* *INDENT-ON* */
434
435/*
436 * fd.io coding-style-patch-verification: ON
437 *
438 * Local Variables:
439 * eval: (c-set-style "gnu")
440 * End:
441 */