Use thread local storage for thread index
[vpp.git] / src / vnet / devices / devices.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/vnet.h>
17 #include <vnet/devices/devices.h>
18 #include <vnet/feature/feature.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ethernet/ethernet.h>
21
22 vnet_device_main_t vnet_device_main;
23
24 static uword
25 device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
26                  vlib_frame_t * frame)
27 {
28   return 0;
29 }
30
31 /* *INDENT-OFF* */
32 VLIB_REGISTER_NODE (device_input_node) = {
33   .function = device_input_fn,
34   .name = "device-input",
35   .runtime_data_bytes = sizeof (vnet_device_input_runtime_t),
36   .type = VLIB_NODE_TYPE_INPUT,
37   .state = VLIB_NODE_STATE_DISABLED,
38   .n_next_nodes = VNET_DEVICE_INPUT_N_NEXT_NODES,
39   .next_nodes = VNET_DEVICE_INPUT_NEXT_NODES,
40 };
41
42 /* Table defines how much we need to advance current data pointer
43    in the buffer if we shortcut to l3 nodes */
44
45 const u32 __attribute__((aligned (CLIB_CACHE_LINE_BYTES)))
46 device_input_next_node_advance[((VNET_DEVICE_INPUT_N_NEXT_NODES /
47                                 CLIB_CACHE_LINE_BYTES) +1) * CLIB_CACHE_LINE_BYTES] =
48 {
49       [VNET_DEVICE_INPUT_NEXT_IP4_INPUT] = sizeof (ethernet_header_t),
50       [VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT] = sizeof (ethernet_header_t),
51       [VNET_DEVICE_INPUT_NEXT_IP6_INPUT] = sizeof (ethernet_header_t),
52       [VNET_DEVICE_INPUT_NEXT_MPLS_INPUT] = sizeof (ethernet_header_t),
53 };
54
55 VNET_FEATURE_ARC_INIT (device_input, static) =
56 {
57   .arc_name  = "device-input",
58   .start_nodes = VNET_FEATURES ("device-input"),
59   .arc_index_ptr = &feature_main.device_input_feature_arc_index,
60 };
61
62 VNET_FEATURE_INIT (l2_patch, static) = {
63   .arc_name = "device-input",
64   .node_name = "l2-patch",
65   .runs_before = VNET_FEATURES ("ethernet-input"),
66 };
67
68 VNET_FEATURE_INIT (worker_handoff, static) = {
69   .arc_name = "device-input",
70   .node_name = "worker-handoff",
71   .runs_before = VNET_FEATURES ("ethernet-input"),
72 };
73
74 VNET_FEATURE_INIT (span_input, static) = {
75   .arc_name = "device-input",
76   .node_name = "span-input",
77   .runs_before = VNET_FEATURES ("ethernet-input"),
78 };
79
80 VNET_FEATURE_INIT (ethernet_input, static) = {
81   .arc_name = "device-input",
82   .node_name = "ethernet-input",
83   .runs_before = 0, /* not before any other features */
84 };
85 /* *INDENT-ON* */
86
87 static int
88 vnet_device_queue_sort (void *a1, void *a2)
89 {
90   vnet_device_and_queue_t *dq1 = a1;
91   vnet_device_and_queue_t *dq2 = a2;
92
93   if (dq1->dev_instance > dq2->dev_instance)
94     return 1;
95   else if (dq1->dev_instance < dq2->dev_instance)
96     return -1;
97   else if (dq1->queue_id > dq2->queue_id)
98     return 1;
99   else if (dq1->queue_id < dq2->queue_id)
100     return -1;
101   else
102     return 0;
103 }
104
105 void
106 vnet_device_input_assign_thread (u32 hw_if_index,
107                                  u16 queue_id, uword thread_index)
108 {
109   vnet_main_t *vnm = vnet_get_main ();
110   vnet_device_main_t *vdm = &vnet_device_main;
111   vlib_main_t *vm;
112   vnet_device_input_runtime_t *rt;
113   vnet_device_and_queue_t *dq;
114   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
115
116   ASSERT (hw->input_node_index > 0);
117
118   if (vdm->first_worker_thread_index == 0)
119     thread_index = 0;
120
121   if (thread_index != 0 &&
122       (thread_index < vdm->first_worker_thread_index ||
123        thread_index > vdm->last_worker_thread_index))
124     {
125       thread_index = vdm->next_worker_thread_index++;
126       if (vdm->next_worker_thread_index > vdm->last_worker_thread_index)
127         vdm->next_worker_thread_index = vdm->first_worker_thread_index;
128     }
129
130   vm = vlib_mains[thread_index];
131   rt = vlib_node_get_runtime_data (vm, hw->input_node_index);
132
133   vec_add2 (rt->devices_and_queues, dq, 1);
134   dq->hw_if_index = hw_if_index;
135   dq->dev_instance = hw->dev_instance;
136   dq->queue_id = queue_id;
137
138   vec_sort_with_function (rt->devices_and_queues, vnet_device_queue_sort);
139   vec_validate (hw->input_node_thread_index_by_queue, queue_id);
140   hw->input_node_thread_index_by_queue[queue_id] = thread_index;
141 }
142
143 static int
144 vnet_device_input_unassign_thread (u32 hw_if_index, u16 queue_id,
145                                    uword thread_index)
146 {
147   vnet_main_t *vnm = vnet_get_main ();
148   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
149   vnet_device_input_runtime_t *rt;
150   vnet_device_and_queue_t *dq;
151   uword old_thread_index;
152
153   if (hw->input_node_thread_index_by_queue == 0)
154     return VNET_API_ERROR_INVALID_INTERFACE;
155
156   if (vec_len (hw->input_node_thread_index_by_queue) < queue_id + 1)
157     return VNET_API_ERROR_INVALID_INTERFACE;
158
159   old_thread_index = hw->input_node_thread_index_by_queue[queue_id];
160
161   if (old_thread_index == thread_index)
162     return 0;
163
164   rt =
165     vlib_node_get_runtime_data (vlib_mains[old_thread_index],
166                                 hw->input_node_index);
167
168   vec_foreach (dq, rt->devices_and_queues)
169     if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id)
170     {
171       vec_del1 (rt->devices_and_queues, dq - rt->devices_and_queues);
172       goto deleted;
173     }
174
175   return VNET_API_ERROR_INVALID_INTERFACE;
176
177 deleted:
178   vec_sort_with_function (rt->devices_and_queues, vnet_device_queue_sort);
179
180   return 0;
181 }
182
183 static clib_error_t *
184 show_device_placement_fn (vlib_main_t * vm, unformat_input_t * input,
185                           vlib_cli_command_t * cmd)
186 {
187   u8 *s = 0;
188   vnet_main_t *vnm = vnet_get_main ();
189   vnet_device_input_runtime_t *rt;
190   vnet_device_and_queue_t *dq;
191   vlib_node_t *pn = vlib_get_node_by_name (vm, (u8 *) "device-input");
192   uword si;
193   int index = 0;
194
195   /* *INDENT-OFF* */
196   foreach_vlib_main (({
197     clib_bitmap_foreach (si, pn->sibling_bitmap,
198       ({
199         rt = vlib_node_get_runtime_data (this_vlib_main, si);
200
201         if (vec_len (rt->devices_and_queues))
202           s = format (s, "  node %U:\n", format_vlib_node_name, vm, si);
203
204         vec_foreach (dq, rt->devices_and_queues)
205           {
206             s = format (s, "    %U queue %u\n",
207                         format_vnet_sw_if_index_name, vnm, dq->hw_if_index,
208                         dq->queue_id);
209           }
210       }));
211     if (vec_len (s) > 0)
212       {
213         vlib_cli_output(vm, "Thread %u (%v):\n%v", index,
214                         vlib_worker_threads[index].name, s);
215         vec_reset_length (s);
216       }
217     index++;
218   }));
219   /* *INDENT-ON* */
220
221   vec_free (s);
222   return 0;
223 }
224
225 /* *INDENT-OFF* */
226 VLIB_CLI_COMMAND (memif_delete_command, static) = {
227   .path = "show interface placement",
228   .short_help = "show interface placement",
229   .function = show_device_placement_fn,
230 };
231 /* *INDENT-ON* */
232
233 static clib_error_t *
234 set_device_placement (vlib_main_t * vm, unformat_input_t * input,
235                       vlib_cli_command_t * cmd)
236 {
237   clib_error_t *error = 0;
238   unformat_input_t _line_input, *line_input = &_line_input;
239   vnet_main_t *vnm = vnet_get_main ();
240   vnet_device_main_t *vdm = &vnet_device_main;
241   u32 hw_if_index = (u32) ~ 0;
242   u32 queue_id = (u32) 0;
243   u32 thread_index = (u32) ~ 0;
244   int rv;
245
246   if (!unformat_user (input, unformat_line_input, line_input))
247     return 0;
248
249   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
250     {
251       if (unformat
252           (line_input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index))
253         ;
254       else if (unformat (line_input, "queue %d", &queue_id))
255         ;
256       else if (unformat (line_input, "main", &thread_index))
257         thread_index = 0;
258       else if (unformat (line_input, "worker %d", &thread_index))
259         thread_index += vdm->first_worker_thread_index;
260       else
261         {
262           error = clib_error_return (0, "parse error: '%U'",
263                                      format_unformat_error, line_input);
264           unformat_free (line_input);
265           return error;
266         }
267     }
268
269   unformat_free (line_input);
270
271   if (hw_if_index == (u32) ~ 0)
272     return clib_error_return (0, "please specify valid interface name");
273
274   if (thread_index > vdm->last_worker_thread_index)
275     return clib_error_return (0,
276                               "please specify valid worker thread or main");
277
278   rv =
279     vnet_device_input_unassign_thread (hw_if_index, queue_id, thread_index);
280
281   if (rv)
282     return clib_error_return (0, "not found");
283
284   vnet_device_input_assign_thread (hw_if_index, queue_id, thread_index);
285
286   return 0;
287 }
288
289 /*?
290  * This command is used to assign a given interface, and optionally a
291  * given queue, to a different thread. If the '<em>queue</em>' is not provided,
292  * it defaults to 0.
293  *
294  * @cliexpar
295  * Example of how to display the interface placement:
296  * @cliexstart{show interface placement}
297  * Thread 1 (vpp_wk_0):
298  *   GigabitEthernet0/8/0 queue 0
299  *   GigabitEthernet0/9/0 queue 0
300  * Thread 2 (vpp_wk_1):
301  *   GigabitEthernet0/8/0 queue 1
302  *   GigabitEthernet0/9/0 queue 1
303  * @cliexend
304  * Example of how to assign a interface and queue to a thread:
305  * @cliexcmd{set interface placement GigabitEthernet0/8/0 queue 1 thread 1}
306 ?*/
307 /* *INDENT-OFF* */
308 VLIB_CLI_COMMAND (cmd_set_dpdk_if_placement,static) = {
309     .path = "set interface placement",
310     .short_help = "set interface placement <interface> [queue <n>] [thread <n> | main]",
311     .function = set_device_placement,
312 };
313 /* *INDENT-ON* */
314
315 static clib_error_t *
316 vnet_device_init (vlib_main_t * vm)
317 {
318   vnet_device_main_t *vdm = &vnet_device_main;
319   vlib_thread_main_t *tm = vlib_get_thread_main ();
320   vlib_thread_registration_t *tr;
321   uword *p;
322
323   vec_validate_aligned (vdm->workers, tm->n_vlib_mains - 1,
324                         CLIB_CACHE_LINE_BYTES);
325
326   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
327   tr = p ? (vlib_thread_registration_t *) p[0] : 0;
328   if (tr && tr->count > 0)
329     {
330       vdm->first_worker_thread_index = tr->first_index;
331       vdm->next_worker_thread_index = tr->first_index;
332       vdm->last_worker_thread_index = tr->first_index + tr->count - 1;
333     }
334   return 0;
335 }
336
337 VLIB_INIT_FUNCTION (vnet_device_init);
338
339 /*
340  * fd.io coding-style-patch-verification: ON
341  *
342  * Local Variables:
343  * eval: (c-set-style "gnu")
344  * End:
345  */