Cleanup of handoff code
[vpp.git] / src / vnet / handoff.c
1
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 #include <vnet/vnet.h>
18 #include <vppinfra/xxhash.h>
19 #include <vlib/threads.h>
20 #include <vnet/handoff.h>
21 #include <vnet/feature/feature.h>
22
23 typedef struct
24 {
25   uword *workers_bitmap;
26   u32 *workers;
27 } per_inteface_handoff_data_t;
28
29 typedef struct
30 {
31   u32 cached_next_index;
32   u32 num_workers;
33   u32 first_worker_index;
34
35   per_inteface_handoff_data_t *if_data;
36
37   /* Worker handoff index */
38   u32 frame_queue_index;
39
40     u64 (*hash_fn) (ethernet_header_t *);
41 } handoff_main_t;
42
43 handoff_main_t handoff_main;
44
45 typedef struct
46 {
47   u32 sw_if_index;
48   u32 next_worker_index;
49   u32 buffer_index;
50 } worker_handoff_trace_t;
51
52 /* packet trace format function */
53 static u8 *
54 format_worker_handoff_trace (u8 * s, va_list * args)
55 {
56   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
57   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
58   worker_handoff_trace_t *t = va_arg (*args, worker_handoff_trace_t *);
59
60   s =
61     format (s, "worker-handoff: sw_if_index %d, next_worker %d, buffer 0x%x",
62             t->sw_if_index, t->next_worker_index, t->buffer_index);
63   return s;
64 }
65
66 vlib_node_registration_t handoff_node;
67
68 static uword
69 worker_handoff_node_fn (vlib_main_t * vm,
70                         vlib_node_runtime_t * node, vlib_frame_t * frame)
71 {
72   handoff_main_t *hm = &handoff_main;
73   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
74   u32 n_left_from, *from;
75   u16 thread_indices[VLIB_FRAME_SIZE], *ti;
76
77   from = vlib_frame_vector_args (frame);
78   n_left_from = frame->n_vectors;
79   vlib_get_buffers (vm, from, bufs, n_left_from);
80
81   b = bufs;
82   ti = thread_indices;
83
84   while (n_left_from > 0)
85     {
86       u32 sw_if_index0;
87       u32 hash;
88       u64 hash_key;
89       per_inteface_handoff_data_t *ihd0;
90       u32 index0;
91
92
93       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
94       ASSERT (hm->if_data);
95       ihd0 = vec_elt_at_index (hm->if_data, sw_if_index0);
96
97       /*
98        * Force unknown traffic onto worker 0,
99        * and into ethernet-input. $$$$ add more hashes.
100        */
101
102       /* Compute ingress LB hash */
103       hash_key = hm->hash_fn ((ethernet_header_t *)
104                               vlib_buffer_get_current (b[0]));
105       hash = (u32) clib_xxhash (hash_key);
106
107       /* if input node did not specify next index, then packet
108          should go to eternet-input */
109
110       if (PREDICT_TRUE (is_pow2 (vec_len (ihd0->workers))))
111         index0 = hash & (vec_len (ihd0->workers) - 1);
112       else
113         index0 = hash % vec_len (ihd0->workers);
114
115       ti[0] = hm->first_worker_index + ihd0->workers[index0];
116
117       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
118                          && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
119         {
120           worker_handoff_trace_t *t =
121             vlib_add_trace (vm, node, b[0], sizeof (*t));
122           t->sw_if_index = sw_if_index0;
123           t->next_worker_index = ti[0];
124           t->buffer_index = vlib_get_buffer_index (vm, b[0]);
125         }
126
127       /* next */
128       n_left_from -= 1;
129       ti += 1;
130       b += 1;
131     }
132
133   vlib_buffer_enqueue_to_thread (vm, hm->frame_queue_index, from,
134                                  thread_indices, frame->n_vectors);
135   return frame->n_vectors;
136 }
137
138 /* *INDENT-OFF* */
139 VLIB_REGISTER_NODE (worker_handoff_node) = {
140   .function = worker_handoff_node_fn,
141   .name = "worker-handoff",
142   .vector_size = sizeof (u32),
143   .format_trace = format_worker_handoff_trace,
144   .type = VLIB_NODE_TYPE_INTERNAL,
145
146   .n_next_nodes = 1,
147   .next_nodes = {
148     [0] = "error-drop",
149   },
150 };
151
152 VLIB_NODE_FUNCTION_MULTIARCH (worker_handoff_node, worker_handoff_node_fn)
153 /* *INDENT-ON* */
154
155 int
156 interface_handoff_enable_disable (vlib_main_t * vm, u32 sw_if_index,
157                                   uword * bitmap, int enable_disable)
158 {
159   handoff_main_t *hm = &handoff_main;
160   vnet_sw_interface_t *sw;
161   vnet_main_t *vnm = vnet_get_main ();
162   per_inteface_handoff_data_t *d;
163   int i, rv = 0;
164
165   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
166     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
167
168   sw = vnet_get_sw_interface (vnm, sw_if_index);
169   if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
170     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
171
172   if (clib_bitmap_last_set (bitmap) >= hm->num_workers)
173     return VNET_API_ERROR_INVALID_WORKER;
174
175   if (hm->frame_queue_index == ~0)
176     {
177       vlib_node_t *n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
178       hm->frame_queue_index = vlib_frame_queue_main_init (n->index, 0);
179     }
180
181   vec_validate (hm->if_data, sw_if_index);
182   d = vec_elt_at_index (hm->if_data, sw_if_index);
183
184   vec_free (d->workers);
185   vec_free (d->workers_bitmap);
186
187   if (enable_disable)
188     {
189       d->workers_bitmap = bitmap;
190       /* *INDENT-OFF* */
191       clib_bitmap_foreach (i, bitmap,
192         ({
193           vec_add1(d->workers, i);
194         }));
195       /* *INDENT-ON* */
196     }
197
198   vnet_feature_enable_disable ("device-input", "worker-handoff",
199                                sw_if_index, enable_disable, 0, 0);
200   return rv;
201 }
202
203 static clib_error_t *
204 set_interface_handoff_command_fn (vlib_main_t * vm,
205                                   unformat_input_t * input,
206                                   vlib_cli_command_t * cmd)
207 {
208   handoff_main_t *hm = &handoff_main;
209   u32 sw_if_index = ~0;
210   int enable_disable = 1;
211   uword *bitmap = 0;
212   u32 sym = ~0;
213
214   int rv = 0;
215
216   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
217     {
218       if (unformat (input, "disable"))
219         enable_disable = 0;
220       else if (unformat (input, "workers %U", unformat_bitmap_list, &bitmap))
221         ;
222       else if (unformat (input, "%U", unformat_vnet_sw_interface,
223                          vnet_get_main (), &sw_if_index))
224         ;
225       else if (unformat (input, "symmetrical"))
226         sym = 1;
227       else if (unformat (input, "asymmetrical"))
228         sym = 0;
229       else
230         break;
231     }
232
233   if (sw_if_index == ~0)
234     return clib_error_return (0, "Please specify an interface...");
235
236   if (bitmap == 0)
237     return clib_error_return (0, "Please specify list of workers...");
238
239   rv =
240     interface_handoff_enable_disable (vm, sw_if_index, bitmap,
241                                       enable_disable);
242
243   switch (rv)
244     {
245     case 0:
246       break;
247
248     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
249       return clib_error_return (0, "Invalid interface");
250       break;
251
252     case VNET_API_ERROR_INVALID_WORKER:
253       return clib_error_return (0, "Invalid worker(s)");
254       break;
255
256     case VNET_API_ERROR_UNIMPLEMENTED:
257       return clib_error_return (0,
258                                 "Device driver doesn't support redirection");
259       break;
260
261     default:
262       return clib_error_return (0, "unknown return value %d", rv);
263     }
264
265   if (sym == 1)
266     hm->hash_fn = eth_get_sym_key;
267   else if (sym == 0)
268     hm->hash_fn = eth_get_key;
269
270   return 0;
271 }
272
273 /* *INDENT-OFF* */
274 VLIB_CLI_COMMAND (set_interface_handoff_command, static) = {
275   .path = "set interface handoff",
276   .short_help =
277   "set interface handoff <interface-name> workers <workers-list> [symmetrical|asymmetrical]",
278   .function = set_interface_handoff_command_fn,
279 };
280 /* *INDENT-ON* */
281
282 clib_error_t *
283 handoff_init (vlib_main_t * vm)
284 {
285   handoff_main_t *hm = &handoff_main;
286   vlib_thread_main_t *tm = vlib_get_thread_main ();
287   clib_error_t *error;
288   uword *p;
289
290   if ((error = vlib_call_init_function (vm, threads_init)))
291     return error;
292
293   vlib_thread_registration_t *tr;
294   /* Only the standard vnet worker threads are supported */
295   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
296   if (p)
297     {
298       tr = (vlib_thread_registration_t *) p[0];
299       if (tr)
300         {
301           hm->num_workers = tr->count;
302           hm->first_worker_index = tr->first_index;
303         }
304     }
305
306   hm->hash_fn = eth_get_key;
307   hm->frame_queue_index = ~0;
308
309   return 0;
310 }
311
312 VLIB_INIT_FUNCTION (handoff_init);
313
314 /*
315  * fd.io coding-style-patch-verification: ON
316  *
317  * Local Variables:
318  * eval: (c-set-style "gnu")
319  * End:
320  */