blob: 04cf9ec49c8964ff9d489652c1c7134d0aef36c6 [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
Filip Tehlaraee73642019-03-13 05:50:44 -070093#ifndef CLIB_MARCH_VARIANT
Stevena624dbe2018-01-09 11:13:29 -080094inline void
Damjan Marion8389fb92017-10-13 18:29:53 +020095virtio_free_used_desc (vlib_main_t * vm, virtio_vring_t * vring)
96{
97 u16 used = vring->desc_in_use;
98 u16 sz = vring->size;
99 u16 mask = sz - 1;
100 u16 last = vring->last_used_idx;
101 u16 n_left = vring->used->idx - last;
102
103 if (n_left == 0)
104 return;
105
106 while (n_left)
107 {
108 struct vring_used_elem *e = &vring->used->ring[last & mask];
109 u16 slot = e->id;
Damjan Marion8389fb92017-10-13 18:29:53 +0200110
111 vlib_buffer_free (vm, &vring->buffers[slot], 1);
112 used--;
113 last++;
114 n_left--;
115 }
116 vring->desc_in_use = used;
117 vring->last_used_idx = last;
118}
Filip Tehlaraee73642019-03-13 05:50:44 -0700119#endif /* CLIB_MARCH_VARIANT */
Damjan Marion8389fb92017-10-13 18:29:53 +0200120
121static_always_inline u16
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200122add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif,
123 virtio_vring_t * vring, u32 bi, u16 avail, u16 next,
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200124 u16 mask, int do_gso)
Damjan Marion8389fb92017-10-13 18:29:53 +0200125{
126 u16 n_added = 0;
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200127 int hdr_sz = vif->virtio_net_hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200128 struct vring_desc *d;
129 d = &vring->desc[next];
130 vlib_buffer_t *b = vlib_get_buffer (vm, bi);
Damjan Marion508cabc2018-02-08 19:49:22 +0100131 struct virtio_net_hdr_v1 *hdr = vlib_buffer_get_current (b) - hdr_sz;
132
Dave Barachb7b92992018-10-17 10:38:51 -0400133 clib_memset (hdr, 0, hdr_sz);
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200134 if (do_gso && (b->flags & VNET_BUFFER_F_GSO))
135 {
136 if (b->flags & VNET_BUFFER_F_IS_IP4)
137 {
138 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
139 hdr->gso_size = vnet_buffer2 (b)->gso_size;
140 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
141 hdr->csum_start = vnet_buffer (b)->l4_hdr_offset; // 0x22;
142 hdr->csum_offset = 0x10;
143 }
144 else
145 {
146 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
147 hdr->gso_size = vnet_buffer2 (b)->gso_size;
148 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
149 hdr->csum_start = vnet_buffer (b)->l4_hdr_offset; // 0x36;
150 hdr->csum_offset = 0x10;
151 }
152 }
Damjan Marion8389fb92017-10-13 18:29:53 +0200153
154 if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
155 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200156 d->addr =
157 ((vif->type == VIRTIO_IF_TYPE_PCI) ? vlib_buffer_get_current_pa (vm,
158 b) :
159 pointer_to_uword (vlib_buffer_get_current (b))) - hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200160 d->len = b->current_length + hdr_sz;
161 d->flags = 0;
162 }
163 else
164 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200165 /*
166 * We are using single vlib_buffer_t for indirect descriptor(s)
167 * chain. Single descriptor is 16 bytes and vlib_buffer_t
168 * has 2048 bytes space. So maximum long chain can have 128
169 * (=2048/16) indirect descriptors.
170 * It can easily support 65535 bytes of Jumbo frames with
171 * each data buffer size of 512 bytes minimum.
172 */
173 vlib_buffer_t *indirect_desc =
174 vlib_get_buffer (vm, vring->indirect_buffers[next]);
175 indirect_desc->current_data = 0;
Damjan Marion8389fb92017-10-13 18:29:53 +0200176
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200177 struct vring_desc *id =
178 (struct vring_desc *) vlib_buffer_get_current (indirect_desc);
179 u32 count = 1;
180 if (vif->type == VIRTIO_IF_TYPE_PCI)
Damjan Marion8389fb92017-10-13 18:29:53 +0200181 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200182 d->addr = vlib_physmem_get_pa (vm, id);
183 id->addr = vlib_buffer_get_current_pa (vm, b) - hdr_sz;
Damjan Marion8389fb92017-10-13 18:29:53 +0200184
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200185 /*
186 * If VIRTIO_F_ANY_LAYOUT is not negotiated, then virtio_net_hdr
187 * should be presented in separate descriptor and data will start
188 * from next descriptor.
189 */
190 if (PREDICT_TRUE
191 (vif->features & VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)))
192 id->len = b->current_length + hdr_sz;
193 else
194 {
195 id->len = hdr_sz;
196 id->flags = VRING_DESC_F_NEXT;
197 id->next = count;
198 count++;
199 id++;
200 id->addr = vlib_buffer_get_current_pa (vm, b);
201 id->len = b->current_length;
202 }
203 while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
204 {
205 id->flags = VRING_DESC_F_NEXT;
206 id->next = count;
207 count++;
208 id++;
209 b = vlib_get_buffer (vm, b->next_buffer);
210 id->addr = vlib_buffer_get_current_pa (vm, b);
211 id->len = b->current_length;
212 }
213 }
214 else /* VIRTIO_IF_TYPE_TAP */
215 {
216 d->addr = pointer_to_uword (id);
217 /* first buffer in chain */
218 id->addr = pointer_to_uword (vlib_buffer_get_current (b)) - hdr_sz;
219 id->len = b->current_length + hdr_sz;
220
221 while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
222 {
223 id->flags = VRING_DESC_F_NEXT;
224 id->next = count;
225 count++;
226 id++;
227 b = vlib_get_buffer (vm, b->next_buffer);
228 id->addr = pointer_to_uword (vlib_buffer_get_current (b));
229 id->len = b->current_length;
230 }
231 }
232 id->flags = 0;
233 id->next = 0;
234 d->len = count * sizeof (struct vring_desc);
Damjan Marion8389fb92017-10-13 18:29:53 +0200235 d->flags = VRING_DESC_F_INDIRECT;
236 }
237 vring->buffers[next] = bi;
238 vring->avail->ring[avail & mask] = next;
239 n_added++;
240 return n_added;
241}
242
Damjan Marion8389fb92017-10-13 18:29:53 +0200243static_always_inline uword
244virtio_interface_tx_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200245 vlib_frame_t * frame, virtio_if_t * vif,
246 int do_gso)
Damjan Marion8389fb92017-10-13 18:29:53 +0200247{
Damjan Marion8389fb92017-10-13 18:29:53 +0200248 u16 n_left = frame->n_vectors;
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000249 virtio_vring_t *vring;
250 u16 qid = vm->thread_index % vif->num_txqs;
251 vring = vec_elt_at_index (vif->txq_vrings, qid);
Damjan Marion8389fb92017-10-13 18:29:53 +0200252 u16 used, next, avail;
253 u16 sz = vring->size;
254 u16 mask = sz - 1;
Damjan Mariona3d59862018-11-10 10:23:00 +0100255 u32 *buffers = vlib_frame_vector_args (frame);
Damjan Marion8389fb92017-10-13 18:29:53 +0200256
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000257 clib_spinlock_lock_if_init (&vring->lockp);
Damjan Marion829ee532018-02-16 16:13:32 +0100258
Damjan Marione40231b2018-12-20 10:44:47 +0100259 if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0 &&
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200260 (vring->last_kick_avail_idx != vring->avail->idx))
261 virtio_kick (vm, vring, vif);
Damjan Marione40231b2018-12-20 10:44:47 +0100262
Damjan Marion8389fb92017-10-13 18:29:53 +0200263 /* free consumed buffers */
264 virtio_free_used_desc (vm, vring);
265
266 used = vring->desc_in_use;
267 next = vring->desc_next;
268 avail = vring->avail->idx;
269
270 while (n_left && used < sz)
271 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200272 u16 n_added = 0;
273 n_added =
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200274 add_buffer_to_slot (vm, vif, vring, buffers[0], avail, next, mask,
275 do_gso);
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200276 if (!n_added)
277 break;
Damjan Marion8389fb92017-10-13 18:29:53 +0200278 avail += n_added;
279 next = (next + n_added) & mask;
280 used += n_added;
281 buffers++;
282 n_left--;
283 }
284
285 if (n_left != frame->n_vectors)
286 {
287 CLIB_MEMORY_STORE_BARRIER ();
288 vring->avail->idx = avail;
289 vring->desc_next = next;
290 vring->desc_in_use = used;
291 if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0)
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200292 virtio_kick (vm, vring, vif);
Damjan Marion8389fb92017-10-13 18:29:53 +0200293 }
294
Damjan Marion8389fb92017-10-13 18:29:53 +0200295 if (n_left)
296 {
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200297 vlib_error_count (vm, node->node_index, VIRTIO_TX_ERROR_NO_FREE_SLOTS,
Damjan Marion8389fb92017-10-13 18:29:53 +0200298 n_left);
299 vlib_buffer_free (vm, buffers, n_left);
300 }
301
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000302 clib_spinlock_unlock_if_init (&vring->lockp);
Damjan Marion829ee532018-02-16 16:13:32 +0100303
Damjan Marion8389fb92017-10-13 18:29:53 +0200304 return frame->n_vectors - n_left;
305}
306
Filip Tehlaraee73642019-03-13 05:50:44 -0700307VNET_DEVICE_CLASS_TX_FN (virtio_device_class) (vlib_main_t * vm,
308 vlib_node_runtime_t * node,
309 vlib_frame_t * frame)
Damjan Marion8389fb92017-10-13 18:29:53 +0200310{
311 virtio_main_t *nm = &virtio_main;
312 vnet_interface_output_runtime_t *rund = (void *) node->runtime_data;
313 virtio_if_t *vif = pool_elt_at_index (nm->interfaces, rund->dev_instance);
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200314 vnet_main_t *vnm = vnet_get_main ();
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000315
Andrew Yourtchenko6a7cff72018-10-12 16:09:22 +0200316 if (vnm->interface_main.gso_interface_count > 0)
317 return virtio_interface_tx_inline (vm, node, frame, vif, 1 /* do_gso */ );
318 else
319 return virtio_interface_tx_inline (vm, node, frame, vif,
320 0 /* no do_gso */ );
Damjan Marion8389fb92017-10-13 18:29:53 +0200321}
322
323static void
324virtio_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
325 u32 node_index)
326{
327 virtio_main_t *apm = &virtio_main;
328 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
329 virtio_if_t *vif = pool_elt_at_index (apm->interfaces, hw->dev_instance);
330
331 /* Shut off redirection */
332 if (node_index == ~0)
333 {
334 vif->per_interface_next_index = node_index;
335 return;
336 }
337
338 vif->per_interface_next_index =
339 vlib_node_add_next (vlib_get_main (), virtio_input_node.index,
340 node_index);
341}
342
343static void
344virtio_clear_hw_interface_counters (u32 instance)
345{
346 /* Nothing for now */
347}
348
349static clib_error_t *
350virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid,
351 vnet_hw_interface_rx_mode mode)
352{
353 virtio_main_t *mm = &virtio_main;
354 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
355 virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
Mohsin Kazmi09a3bc52019-04-02 11:45:08 +0000356 virtio_vring_t *vring = vec_elt_at_index (vif->rxq_vrings, qid);
Damjan Marion8389fb92017-10-13 18:29:53 +0200357
Mohsin Kazmib74fe322019-01-31 13:50:56 +0000358 if (vif->type == VIRTIO_IF_TYPE_PCI && !(vif->support_int_mode))
359 {
360 vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT;
361 return clib_error_return (0, "interrupt mode is not supported");
362 }
363
Damjan Marion8389fb92017-10-13 18:29:53 +0200364 if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
365 vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT;
366 else
367 vring->avail->flags &= ~VIRTIO_RING_FLAG_MASK_INT;
368
369 return 0;
370}
371
372static clib_error_t *
373virtio_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
374{
375 virtio_main_t *mm = &virtio_main;
376 vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
377 virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
Damjan Marion8389fb92017-10-13 18:29:53 +0200378
379 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
380 vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP;
381 else
382 vif->flags &= ~VIRTIO_IF_FLAG_ADMIN_UP;
383
Damjan Marion8389fb92017-10-13 18:29:53 +0200384 return 0;
385}
386
387static clib_error_t *
388virtio_subif_add_del_function (vnet_main_t * vnm,
389 u32 hw_if_index,
390 struct vnet_sw_interface_t *st, int is_add)
391{
392 /* Nothing for now */
393 return 0;
394}
395
396/* *INDENT-OFF* */
397VNET_DEVICE_CLASS (virtio_device_class) = {
398 .name = "virtio",
Damjan Marion8389fb92017-10-13 18:29:53 +0200399 .format_device_name = format_virtio_device_name,
400 .format_device = format_virtio_device,
401 .format_tx_trace = format_virtio_tx_trace,
Mohsin Kazmid6c15af2018-10-23 18:00:47 +0200402 .tx_function_n_errors = VIRTIO_TX_N_ERROR,
Damjan Marion8389fb92017-10-13 18:29:53 +0200403 .tx_function_error_strings = virtio_tx_func_error_strings,
404 .rx_redirect_to_node = virtio_set_interface_next_node,
405 .clear_counters = virtio_clear_hw_interface_counters,
406 .admin_up_down_function = virtio_interface_admin_up_down,
407 .subif_add_del_function = virtio_subif_add_del_function,
408 .rx_mode_change_function = virtio_interface_rx_mode_change,
409};
Damjan Marion8389fb92017-10-13 18:29:53 +0200410/* *INDENT-ON* */
411
412/*
413 * fd.io coding-style-patch-verification: ON
414 *
415 * Local Variables:
416 * eval: (c-set-style "gnu")
417 * End:
418 */