virtio: add packet buffering on tx
[vpp.git] / src / vnet / devices / virtio / virtio_buffering.h
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2020 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 #ifndef _VNET_DEVICES_VIRTIO_VIRTIO_BUFFERING_H_
19 #define _VNET_DEVICES_VIRTIO_VIRTIO_BUFFERING_H_
20
21 #define VIRTIO_BUFFERING_DEFAULT_SIZE 1024
22 #define VIRTIO_BUFFERING_TIMEOUT 1e-5
23
24 typedef struct
25 {
26   f64 timeout_ts;
27   u32 *buffers;
28   u32 node_index;
29   u16 size;
30   u16 free_size;
31   u16 front;
32   u16 back;
33   u8 is_enable;
34 } virtio_vring_buffering_t;
35
36 static_always_inline clib_error_t *
37 virtio_vring_buffering_init (virtio_vring_buffering_t ** buffering,
38                              u32 node_index, u16 size)
39 {
40   if (*buffering)
41     return clib_error_return (0, "buffering: already initialized");
42
43   if (!is_pow2 (size))
44     return clib_error_return (0, "buffering: size must be power of 2");
45
46   if (size > 32768)
47     return clib_error_return (0, "buffering: size must be 32768 or lower");
48
49   if (size == 0)
50     size = VIRTIO_BUFFERING_DEFAULT_SIZE;
51
52   virtio_vring_buffering_t *b_temp = 0;
53   b_temp =
54     (virtio_vring_buffering_t *)
55     clib_mem_alloc (sizeof (virtio_vring_buffering_t));
56   if (!b_temp)
57     return clib_error_return (0, "buffering: memory allocation failed");
58
59   clib_memset (b_temp, 0, sizeof (virtio_vring_buffering_t));
60
61   b_temp->node_index = node_index;
62   b_temp->free_size = size;
63   b_temp->size = size;
64
65   vec_validate_aligned (b_temp->buffers, size, CLIB_CACHE_LINE_BYTES);
66   b_temp->is_enable = 1;
67
68   *buffering = b_temp;
69   return 0;
70 }
71
72 static_always_inline void
73 virtio_vring_buffering_buffers_free (vlib_main_t * vm,
74                                      virtio_vring_buffering_t * buffering)
75 {
76   u16 n_buffers = buffering->size - buffering->free_size;
77   if (n_buffers)
78     {
79       vlib_buffer_free_from_ring (vm, buffering->buffers, buffering->front,
80                                   buffering->size, n_buffers);
81       buffering->free_size += n_buffers;
82     }
83 }
84
85 static_always_inline void
86 virtio_vring_buffering_free (vlib_main_t * vm,
87                              virtio_vring_buffering_t * buffering)
88 {
89   if (buffering)
90     {
91       virtio_vring_buffering_buffers_free (vm, buffering);
92       vec_free (buffering->buffers);
93       clib_mem_free (buffering);
94     }
95 }
96
97 static_always_inline u8
98 virtio_vring_buffering_is_enable (virtio_vring_buffering_t * buffering)
99 {
100   if (buffering)
101     return buffering->is_enable;
102
103   return 0;
104 }
105
106 static_always_inline void
107 virtio_vring_buffering_set_is_enable (virtio_vring_buffering_t * buffering,
108                                       u8 is_enable)
109 {
110   if (buffering)
111     buffering->is_enable = is_enable;
112 }
113
114 static_always_inline void
115 virtio_vring_buffering_set_timeout (vlib_main_t * vm,
116                                     virtio_vring_buffering_t * buffering,
117                                     f64 timeout_expire)
118 {
119   if (buffering)
120     buffering->timeout_ts = vlib_time_now (vm) + timeout_expire;
121 }
122
123 static_always_inline u8
124 virtio_vring_buffering_is_timeout (vlib_main_t * vm,
125                                    virtio_vring_buffering_t * buffering)
126 {
127   if (buffering && (buffering->timeout_ts < vlib_time_now (vm)))
128     return 1;
129   return 0;
130 }
131
132 static_always_inline u8
133 virtio_vring_buffering_is_empty (virtio_vring_buffering_t * buffering)
134 {
135   if (buffering->size == buffering->free_size)
136     return 1;
137   return 0;
138 }
139
140 static_always_inline u8
141 virtio_vring_buffering_is_full (virtio_vring_buffering_t * buffering)
142 {
143   if (buffering->free_size == 0)
144     return 1;
145   return 0;
146 }
147
148 static_always_inline u16
149 virtio_vring_n_buffers (virtio_vring_buffering_t * buffering)
150 {
151   return (buffering->size - buffering->free_size);
152 }
153
154 static_always_inline u16
155 virtio_vring_buffering_store_packets (virtio_vring_buffering_t * buffering,
156                                       u32 * bi, u16 n_store)
157 {
158   u16 mask, n_s = 0, i = 0;
159
160   if (!virtio_vring_buffering_is_enable (buffering)
161       || virtio_vring_buffering_is_full (buffering))
162     return 0;
163
164   mask = buffering->size - 1;
165   n_s = clib_min (n_store, buffering->free_size);
166
167   while (i < n_s)
168     {
169       buffering->buffers[buffering->back] = bi[i];
170       buffering->back = (buffering->back + 1) & mask;
171       buffering->free_size--;
172       i++;
173     }
174   return n_s;
175 }
176
177 static_always_inline u32
178 virtio_vring_buffering_read_from_front (virtio_vring_buffering_t * buffering)
179 {
180   u32 bi = ~0;
181   u16 mask = buffering->size - 1;
182   if (virtio_vring_buffering_is_empty (buffering))
183     return bi;
184
185   bi = buffering->buffers[buffering->front];
186   buffering->buffers[buffering->front] = ~0;
187   buffering->front = (buffering->front + 1) & mask;
188   buffering->free_size++;
189   return bi;
190 }
191
192 static_always_inline u32
193 virtio_vring_buffering_read_from_back (virtio_vring_buffering_t * buffering)
194 {
195   u32 bi = ~0;
196   u16 mask = buffering->size - 1;
197   if (virtio_vring_buffering_is_empty (buffering))
198     return bi;
199
200   buffering->back = (buffering->back - 1) & mask;
201   bi = buffering->buffers[buffering->back];
202   buffering->buffers[buffering->back] = ~0;
203   buffering->free_size++;
204   return bi;
205 }
206
207 static_always_inline void
208 virtio_vring_buffering_schedule_node_on_dispatcher (vlib_main_t * vm,
209                                                     virtio_vring_buffering_t *
210                                                     buffering)
211 {
212   if (buffering && virtio_vring_buffering_is_timeout (vm, buffering)
213       && virtio_vring_n_buffers (buffering))
214     {
215       vlib_frame_t *f = vlib_get_frame_to_node (vm, buffering->node_index);
216       u32 *f_to = vlib_frame_vector_args (f);
217       f_to[f->n_vectors] = virtio_vring_buffering_read_from_back (buffering);
218       f->n_vectors++;
219       vlib_put_frame_to_node (vm, buffering->node_index, f);
220       virtio_vring_buffering_set_timeout (vm, buffering,
221                                           VIRTIO_BUFFERING_TIMEOUT);
222     }
223 }
224
225 static_always_inline u8 *
226 virtio_vring_buffering_format (u8 * s, va_list * args)
227 {
228   virtio_vring_buffering_t *buffering =
229     va_arg (*args, virtio_vring_buffering_t *);
230   u32 indent = format_get_indent (s);
231
232   if (!buffering)
233     return s;
234
235   indent += 2;
236
237   if (buffering->is_enable)
238     s = format (s, "packet-buffering: enable\n");
239   else
240     s = format (s, "packet-buffering: disable\n");
241   s =
242     format (s,
243             "%Usize %u n_buffers %u front %u back %u",
244             format_white_space, indent, buffering->size,
245             virtio_vring_n_buffers (buffering), buffering->front,
246             buffering->back);
247
248   return s;
249 }
250
251 #endif /* _VNET_DEVICES_VIRTIO_VIRTIO_BUFFERING_H_ */
252
253 /*
254  * fd.io coding-style-patch-verification: ON
255  *
256  * Local Variables:
257  * eval: (c-set-style "gnu")
258  * End:
259  */