12231150bSDamjan Marion/*
22231150bSDamjan Marion * Copyright (c) 2015 Cisco and/or its affiliates.
32231150bSDamjan Marion * Licensed under the Apache License, Version 2.0 (the "License");
42231150bSDamjan Marion * you may not use this file except in compliance with the License.
52231150bSDamjan Marion * You may obtain a copy of the License at:
62231150bSDamjan Marion *
72231150bSDamjan Marion *     http://www.apache.org/licenses/LICENSE-2.0
82231150bSDamjan Marion *
92231150bSDamjan Marion * Unless required by applicable law or agreed to in writing, software
102231150bSDamjan Marion * distributed under the License is distributed on an "AS IS" BASIS,
112231150bSDamjan Marion * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122231150bSDamjan Marion * See the License for the specific language governing permissions and
132231150bSDamjan Marion * limitations under the License.
142231150bSDamjan Marion */
152231150bSDamjan Marion
167dc4146eSDamjan Marion#include <vnet/vnet.h>
1751327ac5SDamjan Marion#include <vnet/devices/devices.h>
182231150bSDamjan Marion#include <vnet/feature/feature.h>
197dc4146eSDamjan Marion#include <vnet/ip/ip.h>
207dc4146eSDamjan Marion#include <vnet/ethernet/ethernet.h>
212231150bSDamjan Marion
22b3bb1010SDamjan Marionvnet_device_main_t vnet_device_main;
23b3bb1010SDamjan Marion
2451327ac5SDamjan Marionstatic uword
2551327ac5SDamjan Mariondevice_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
2651327ac5SDamjan Marion		 vlib_frame_t * frame)
2751327ac5SDamjan Marion{
2851327ac5SDamjan Marion  return 0;
2951327ac5SDamjan Marion}
3051327ac5SDamjan Marion
312231150bSDamjan Marion/* *INDENT-OFF* */
3251327ac5SDamjan MarionVLIB_REGISTER_NODE (device_input_node) = {
3351327ac5SDamjan Marion  .function = device_input_fn,
3451327ac5SDamjan Marion  .name = "device-input",
35eb743fadSDamjan Marion  .runtime_data_bytes = sizeof (vnet_device_input_runtime_t),
3651327ac5SDamjan Marion  .type = VLIB_NODE_TYPE_INPUT,
3751327ac5SDamjan Marion  .state = VLIB_NODE_STATE_DISABLED,
3851327ac5SDamjan Marion  .n_next_nodes = VNET_DEVICE_INPUT_N_NEXT_NODES,
3951327ac5SDamjan Marion  .next_nodes = VNET_DEVICE_INPUT_NEXT_NODES,
4051327ac5SDamjan Marion};
4151327ac5SDamjan Marion
427dc4146eSDamjan Marion/* Table defines how much we need to advance current data pointer
437dc4146eSDamjan Marion   in the buffer if we shortcut to l3 nodes */
447dc4146eSDamjan Marion
457dc4146eSDamjan Marionconst u32 __attribute__((aligned (CLIB_CACHE_LINE_BYTES)))
467dc4146eSDamjan Mariondevice_input_next_node_advance[((VNET_DEVICE_INPUT_N_NEXT_NODES /
477dc4146eSDamjan Marion				CLIB_CACHE_LINE_BYTES) +1) * CLIB_CACHE_LINE_BYTES] =
487dc4146eSDamjan Marion{
497dc4146eSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_IP4_INPUT] = sizeof (ethernet_header_t),
50767b2f59SSergio Gonzalez Monroy      [VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT] = sizeof (ethernet_header_t),
517dc4146eSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_IP6_INPUT] = sizeof (ethernet_header_t),
527dc4146eSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_MPLS_INPUT] = sizeof (ethernet_header_t),
537dc4146eSDamjan Marion};
547dc4146eSDamjan Marion
55bd846cdcSDamjan Marionconst u32 __attribute__((aligned (CLIB_CACHE_LINE_BYTES)))
56bd846cdcSDamjan Mariondevice_input_next_node_flags[((VNET_DEVICE_INPUT_N_NEXT_NODES /
57bd846cdcSDamjan Marion				CLIB_CACHE_LINE_BYTES) +1) * CLIB_CACHE_LINE_BYTES] =
58bd846cdcSDamjan Marion{
59bd846cdcSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_IP4_INPUT] = VNET_BUFFER_F_L3_HDR_OFFSET_VALID,
60bd846cdcSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT] = VNET_BUFFER_F_L3_HDR_OFFSET_VALID,
61bd846cdcSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_IP6_INPUT] = VNET_BUFFER_F_L3_HDR_OFFSET_VALID,
62bd846cdcSDamjan Marion      [VNET_DEVICE_INPUT_NEXT_MPLS_INPUT] = VNET_BUFFER_F_L3_HDR_OFFSET_VALID,
63bd846cdcSDamjan Marion};
64bd846cdcSDamjan Marion
659f6186e0SDave BarachVNET_FEATURE_ARC_INIT (device_input, static) =
669f6186e0SDave Barach{
672231150bSDamjan Marion  .arc_name  = "device-input",
6851327ac5SDamjan Marion  .start_nodes = VNET_FEATURES ("device-input"),
69a25def78SDave Barach  .last_in_arc = "ethernet-input",
708b3191e6SDamjan Marion  .arc_index_ptr = &feature_main.device_input_feature_arc_index,
712231150bSDamjan Marion};
722231150bSDamjan Marion
732231150bSDamjan MarionVNET_FEATURE_INIT (l2_patch, static) = {
742231150bSDamjan Marion  .arc_name = "device-input",
752231150bSDamjan Marion  .node_name = "l2-patch",
762231150bSDamjan Marion  .runs_before = VNET_FEATURES ("ethernet-input"),
772231150bSDamjan Marion};
782231150bSDamjan Marion
792231150bSDamjan MarionVNET_FEATURE_INIT (worker_handoff, static) = {
802231150bSDamjan Marion  .arc_name = "device-input",
812231150bSDamjan Marion  .node_name = "worker-handoff",
822231150bSDamjan Marion  .runs_before = VNET_FEATURES ("ethernet-input"),
832231150bSDamjan Marion};
842231150bSDamjan Marion
85f6e3dc47SPavel KotucekVNET_FEATURE_INIT (span_input, static) = {
86f6e3dc47SPavel Kotucek  .arc_name = "device-input",
87f6e3dc47SPavel Kotucek  .node_name = "span-input",
88f6e3dc47SPavel Kotucek  .runs_before = VNET_FEATURES ("ethernet-input"),
89f6e3dc47SPavel Kotucek};
90f6e3dc47SPavel Kotucek
9115ac81c1SPavel KotucekVNET_FEATURE_INIT (p2p_ethernet_node, static) = {
9215ac81c1SPavel Kotucek  .arc_name = "device-input",
9315ac81c1SPavel Kotucek  .node_name = "p2p-ethernet-input",
9415ac81c1SPavel Kotucek  .runs_before = VNET_FEATURES ("ethernet-input"),
9515ac81c1SPavel Kotucek};
9615ac81c1SPavel Kotucek
972231150bSDamjan MarionVNET_FEATURE_INIT (ethernet_input, static) = {
982231150bSDamjan Marion  .arc_name = "device-input",
992231150bSDamjan Marion  .node_name = "ethernet-input",
1002231150bSDamjan Marion  .runs_before = 0, /* not before any other features */
1012231150bSDamjan Marion};
1022231150bSDamjan Marion/* *INDENT-ON* */
1032231150bSDamjan Marion
104eb743fadSDamjan Marionstatic int
105eb743fadSDamjan Marionvnet_device_queue_sort (void *a1, void *a2)
106eb743fadSDamjan Marion{
107eb743fadSDamjan Marion  vnet_device_and_queue_t *dq1 = a1;
108eb743fadSDamjan Marion  vnet_device_and_queue_t *dq2 = a2;
109eb743fadSDamjan Marion
110eb743fadSDamjan Marion  if (dq1->dev_instance > dq2->dev_instance)
111eb743fadSDamjan Marion    return 1;
112eb743fadSDamjan Marion  else if (dq1->dev_instance < dq2->dev_instance)
113eb743fadSDamjan Marion    return -1;
114eb743fadSDamjan Marion  else if (dq1->queue_id > dq2->queue_id)
115eb743fadSDamjan Marion    return 1;
116eb743fadSDamjan Marion  else if (dq1->queue_id < dq2->queue_id)
117eb743fadSDamjan Marion    return -1;
118eb743fadSDamjan Marion  else
119eb743fadSDamjan Marion    return 0;
120eb743fadSDamjan Marion}
121eb743fadSDamjan Marion
122153646e8SDamjan Marionstatic void
123153646e8SDamjan Marionvnet_device_queue_update (vnet_main_t * vnm, vnet_device_input_runtime_t * rt)
124153646e8SDamjan Marion{
125153646e8SDamjan Marion  vnet_device_and_queue_t *dq;
126153646e8SDamjan Marion  vnet_hw_interface_t *hw;
127153646e8SDamjan Marion
128153646e8SDamjan Marion  vec_sort_with_function (rt->devices_and_queues, vnet_device_queue_sort);
129153646e8SDamjan Marion
130153646e8SDamjan Marion  vec_foreach (dq, rt->devices_and_queues)
131153646e8SDamjan Marion  {
132153646e8SDamjan Marion    hw = vnet_get_hw_interface (vnm, dq->hw_if_index);
133153646e8SDamjan Marion    vec_validate (hw->dq_runtime_index_by_queue, dq->queue_id);
134153646e8SDamjan Marion    hw->dq_runtime_index_by_queue[dq->queue_id] = dq - rt->devices_and_queues;
135153646e8SDamjan Marion  }
136153646e8SDamjan Marion}
137153646e8SDamjan Marion
138eb743fadSDamjan Marionvoid
1394403690cSDamjan Marionvnet_hw_interface_assign_rx_thread (vnet_main_t * vnm, u32 hw_if_index,
1404403690cSDamjan Marion				    u16 queue_id, uword thread_index)
141eb743fadSDamjan Marion{
142eb743fadSDamjan Marion  vnet_device_main_t *vdm = &vnet_device_main;
1436f9ac655SDamjan Marion  vlib_main_t *vm, *vm0;
144eb743fadSDamjan Marion  vnet_device_input_runtime_t *rt;
145eb743fadSDamjan Marion  vnet_device_and_queue_t *dq;
146eb743fadSDamjan Marion  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
147eb743fadSDamjan Marion
148eb743fadSDamjan Marion  ASSERT (hw->input_node_index > 0);
149eb743fadSDamjan Marion
150586afd76SDamjan Marion  if (vdm->first_worker_thread_index == 0)
151586afd76SDamjan Marion    thread_index = 0;
152eb743fadSDamjan Marion
153586afd76SDamjan Marion  if (thread_index != 0 &&
154586afd76SDamjan Marion      (thread_index < vdm->first_worker_thread_index ||
155586afd76SDamjan Marion       thread_index > vdm->last_worker_thread_index))
156eb743fadSDamjan Marion    {
157586afd76SDamjan Marion      thread_index = vdm->next_worker_thread_index++;
158586afd76SDamjan Marion      if (vdm->next_worker_thread_index > vdm->last_worker_thread_index)
159586afd76SDamjan Marion	vdm->next_worker_thread_index = vdm->first_worker_thread_index;
160eb743fadSDamjan Marion    }
161eb743fadSDamjan Marion
162586afd76SDamjan Marion  vm = vlib_mains[thread_index];
1636f9ac655SDamjan Marion  vm0 = vlib_get_main ();
1646f9ac655SDamjan Marion
1656f9ac655SDamjan Marion  vlib_worker_thread_barrier_sync (vm0);
1666f9ac655SDamjan Marion
167eb743fadSDamjan Marion  rt = vlib_node_get_runtime_data (vm, hw->input_node_index);
168eb743fadSDamjan Marion
169eb743fadSDamjan Marion  vec_add2 (rt->devices_and_queues, dq, 1);
170eb743fadSDamjan Marion  dq->hw_if_index = hw_if_index;
171eb743fadSDamjan Marion  dq->dev_instance = hw->dev_instance;
172eb743fadSDamjan Marion  dq->queue_id = queue_id;
1734403690cSDamjan Marion  dq->mode = VNET_HW_INTERFACE_RX_MODE_POLLING;
174f3b53643SSteven  rt->enabled_node_state = VLIB_NODE_STATE_POLLING;
175eb743fadSDamjan Marion
176153646e8SDamjan Marion  vnet_device_queue_update (vnm, rt);
177586afd76SDamjan Marion  vec_validate (hw->input_node_thread_index_by_queue, queue_id);
1784403690cSDamjan Marion  vec_validate (hw->rx_mode_by_queue, queue_id);
179586afd76SDamjan Marion  hw->input_node_thread_index_by_queue[queue_id] = thread_index;
1804403690cSDamjan Marion  hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_POLLING;
1816f9ac655SDamjan Marion
1826f9ac655SDamjan Marion  vlib_worker_thread_barrier_release (vm0);
1836f9ac655SDamjan Marion
184153646e8SDamjan Marion  vlib_node_set_state (vm, hw->input_node_index, rt->enabled_node_state);
185eb743fadSDamjan Marion}
186eb743fadSDamjan Marion
187153646e8SDamjan Marionint
1884403690cSDamjan Marionvnet_hw_interface_unassign_rx_thread (vnet_main_t * vnm, u32 hw_if_index,
1894403690cSDamjan Marion				      u16 queue_id)
190eb743fadSDamjan Marion{
1916f9ac655SDamjan Marion  vlib_main_t *vm, *vm0;
192eb743fadSDamjan Marion  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
193eb743fadSDamjan Marion  vnet_device_input_runtime_t *rt;
194eb743fadSDamjan Marion  vnet_device_and_queue_t *dq;
195586afd76SDamjan Marion  uword old_thread_index;
196f3b53643SSteven  vnet_hw_interface_rx_mode mode;
197eb743fadSDamjan Marion
198586afd76SDamjan Marion  if (hw->input_node_thread_index_by_queue == 0)
199eb743fadSDamjan Marion    return VNET_API_ERROR_INVALID_INTERFACE;
200eb743fadSDamjan Marion
201586afd76SDamjan Marion  if (vec_len (hw->input_node_thread_index_by_queue) < queue_id + 1)
202eb743fadSDamjan Marion    return VNET_API_ERROR_INVALID_INTERFACE;
203eb743fadSDamjan Marion
204586afd76SDamjan Marion  old_thread_index = hw->input_node_thread_index_by_queue[queue_id];
205eb743fadSDamjan Marion
206153646e8SDamjan Marion  vm = vlib_mains[old_thread_index];
207153646e8SDamjan Marion
208153646e8SDamjan Marion  rt = vlib_node_get_runtime_data (vm, hw->input_node_index);
209eb743fadSDamjan Marion
210eb743fadSDamjan Marion  vec_foreach (dq, rt->devices_and_queues)
211eb743fadSDamjan Marion    if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id)
212eb743fadSDamjan Marion    {
213f3b53643SSteven      mode = dq->mode;
2146f9ac655SDamjan Marion      goto delete;
215eb743fadSDamjan Marion    }
216eb743fadSDamjan Marion
217eb743fadSDamjan Marion  return VNET_API_ERROR_INVALID_INTERFACE;
218eb743fadSDamjan Marion
2196f9ac655SDamjan Mariondelete:
220153646e8SDamjan Marion
2216f9ac655SDamjan Marion  vm0 = vlib_get_main ();
2226f9ac655SDamjan Marion  vlib_worker_thread_barrier_sync (vm0);
2236f9ac655SDamjan Marion  vec_del1 (rt->devices_and_queues, dq - rt->devices_and_queues);
224153646e8SDamjan Marion  vnet_device_queue_update (vnm, rt);
2254403690cSDamjan Marion  hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_UNKNOWN;
2266f9ac655SDamjan Marion  vlib_worker_thread_barrier_release (vm0);
227153646e8SDamjan Marion
228153646e8SDamjan Marion  if (vec_len (rt->devices_and_queues) == 0)
229153646e8SDamjan Marion    vlib_node_set_state (vm, hw->input_node_index, VLIB_NODE_STATE_DISABLED);
230f3b53643SSteven  else if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
231f3b53643SSteven    {
232f3b53643SSteven      /*
233f3b53643SSteven       * if the deleted interface is polling, we may need to set the node state
234f3b53643SSteven       * to interrupt if there is no more polling interface for this device's
235f3b53643SSteven       * corresponding thread. This is because mixed interfaces
236f3b53643SSteven       * (polling and interrupt), assigned to the same thread, set the
237f3b53643SSteven       * thread to polling prior to the deletion.
238f3b53643SSteven       */
239f3b53643SSteven      vec_foreach (dq, rt->devices_and_queues)
240f3b53643SSteven      {
241f3b53643SSteven	if (dq->mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
242f3b53643SSteven	  return 0;
243f3b53643SSteven      }
244f3b53643SSteven      rt->enabled_node_state = VLIB_NODE_STATE_INTERRUPT;
245f3b53643SSteven      vlib_node_set_state (vm, hw->input_node_index, rt->enabled_node_state);
246f3b53643SSteven    }
247153646e8SDamjan Marion
248153646e8SDamjan Marion  return 0;
249153646e8SDamjan Marion}
250153646e8SDamjan Marion
251153646e8SDamjan Marion
252153646e8SDamjan Marionint
2534403690cSDamjan Marionvnet_hw_interface_set_rx_mode (vnet_main_t * vnm, u32 hw_if_index,
2544403690cSDamjan Marion			       u16 queue_id, vnet_hw_interface_rx_mode mode)
255153646e8SDamjan Marion{
256153646e8SDamjan Marion  vlib_main_t *vm;
257153646e8SDamjan Marion  uword thread_index;
258153646e8SDamjan Marion  vnet_device_and_queue_t *dq;
259153646e8SDamjan Marion  vlib_node_state_t enabled_node_state;
2604403690cSDamjan Marion  ASSERT (mode < VNET_HW_INTERFACE_NUM_RX_MODES);
261153646e8SDamjan Marion  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
262153646e8SDamjan Marion  vnet_device_input_runtime_t *rt;
263153646e8SDamjan Marion  int is_polling = 0;
264153646e8SDamjan Marion
2654e53a0d0SDamjan Marion  if (mode == VNET_HW_INTERFACE_RX_MODE_DEFAULT)
2664e53a0d0SDamjan Marion    mode = hw->default_rx_mode;
2674e53a0d0SDamjan Marion
2684403690cSDamjan Marion  if (hw->input_node_thread_index_by_queue == 0 || hw->rx_mode_by_queue == 0)
269153646e8SDamjan Marion    return VNET_API_ERROR_INVALID_INTERFACE;
270153646e8SDamjan Marion
2714403690cSDamjan Marion  if (hw->rx_mode_by_queue[queue_id] == mode)
2724403690cSDamjan Marion    return 0;
2734403690cSDamjan Marion
2744403690cSDamjan Marion  if (mode != VNET_HW_INTERFACE_RX_MODE_POLLING &&
2754403690cSDamjan Marion      (hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE) == 0)
2764403690cSDamjan Marion    return VNET_API_ERROR_UNSUPPORTED;
2774403690cSDamjan Marion
278bd8a611cSSteven  if ((vec_len (hw->input_node_thread_index_by_queue) < queue_id + 1) ||
279bd8a611cSSteven      (vec_len (hw->rx_mode_by_queue) < queue_id + 1))
280bd8a611cSSteven    return VNET_API_ERROR_INVALID_QUEUE;
281bd8a611cSSteven
282e3a395c8SSteven  hw->rx_mode_by_queue[queue_id] = mode;
283153646e8SDamjan Marion  thread_index = hw->input_node_thread_index_by_queue[queue_id];
284153646e8SDamjan Marion  vm = vlib_mains[thread_index];
285153646e8SDamjan Marion
286153646e8SDamjan Marion  rt = vlib_node_get_runtime_data (vm, hw->input_node_index);
287153646e8SDamjan Marion
288153646e8SDamjan Marion  vec_foreach (dq, rt->devices_and_queues)
289153646e8SDamjan Marion  {
290153646e8SDamjan Marion    if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id)
291153646e8SDamjan Marion      dq->mode = mode;
2924403690cSDamjan Marion    if (dq->mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
293153646e8SDamjan Marion      is_polling = 1;
294153646e8SDamjan Marion  }
295153646e8SDamjan Marion
296153646e8SDamjan Marion  if (is_polling)
297153646e8SDamjan Marion    enabled_node_state = VLIB_NODE_STATE_POLLING;
298153646e8SDamjan Marion  else
299153646e8SDamjan Marion    enabled_node_state = VLIB_NODE_STATE_INTERRUPT;
300153646e8SDamjan Marion
301153646e8SDamjan Marion  if (rt->enabled_node_state != enabled_node_state)
302153646e8SDamjan Marion    {
303153646e8SDamjan Marion      rt->enabled_node_state = enabled_node_state;
304153646e8SDamjan Marion      if (vlib_node_get_state (vm, hw->input_node_index) !=
305153646e8SDamjan Marion	  VLIB_NODE_STATE_DISABLED)
306153646e8SDamjan Marion	vlib_node_set_state (vm, hw->input_node_index, enabled_node_state);
307153646e8SDamjan Marion    }
308eb743fadSDamjan Marion
309eb743fadSDamjan Marion  return 0;
310eb743fadSDamjan Marion}
311eb743fadSDamjan Marion
312153646e8SDamjan Marionint
3134403690cSDamjan Marionvnet_hw_interface_get_rx_mode (vnet_main_t * vnm, u32 hw_if_index,
3144403690cSDamjan Marion			       u16 queue_id, vnet_hw_interface_rx_mode * mode)
315153646e8SDamjan Marion{
316153646e8SDamjan Marion  vlib_main_t *vm;
317153646e8SDamjan Marion  uword thread_index;
318153646e8SDamjan Marion  vnet_device_and_queue_t *dq;
319153646e8SDamjan Marion  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
320153646e8SDamjan Marion  vnet_device_input_runtime_t *rt;
321153646e8SDamjan Marion
322153646e8SDamjan Marion  if (hw->input_node_thread_index_by_queue == 0)
323153646e8SDamjan Marion    return VNET_API_ERROR_INVALID_INTERFACE;
324153646e8SDamjan Marion
325bd8a611cSSteven  if ((vec_len (hw->input_node_thread_index_by_queue) < queue_id + 1) ||
326bd8a611cSSteven      (vec_len (hw->rx_mode_by_queue) < queue_id + 1))
327bd8a611cSSteven    return VNET_API_ERROR_INVALID_QUEUE;
328bd8a611cSSteven
329153646e8SDamjan Marion  thread_index = hw->input_node_thread_index_by_queue[queue_id];
330153646e8SDamjan Marion  vm = vlib_mains[thread_index];
331153646e8SDamjan Marion
332153646e8SDamjan Marion  rt = vlib_node_get_runtime_data (vm, hw->input_node_index);
333153646e8SDamjan Marion
334153646e8SDamjan Marion  vec_foreach (dq, rt->devices_and_queues)
335153646e8SDamjan Marion    if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id)
336153646e8SDamjan Marion    {
337153646e8SDamjan Marion      *mode = dq->mode;
338153646e8SDamjan Marion      return 0;
339153646e8SDamjan Marion    }
340153646e8SDamjan Marion
341153646e8SDamjan Marion  return VNET_API_ERROR_INVALID_INTERFACE;
342153646e8SDamjan Marion}
343153646e8SDamjan Marion
344eb743fadSDamjan Marion
345eb743fadSDamjan Marion
346b3bb1010SDamjan Marionstatic clib_error_t *
347b3bb1010SDamjan Marionvnet_device_init (vlib_main_t * vm)
348b3bb1010SDamjan Marion{
349b3bb1010SDamjan Marion  vnet_device_main_t *vdm = &vnet_device_main;
350b3bb1010SDamjan Marion  vlib_thread_main_t *tm = vlib_get_thread_main ();
351eb743fadSDamjan Marion  vlib_thread_registration_t *tr;
352eb743fadSDamjan Marion  uword *p;
353b3bb1010SDamjan Marion
354b3bb1010SDamjan Marion  vec_validate_aligned (vdm->workers, tm->n_vlib_mains - 1,
355b3bb1010SDamjan Marion			CLIB_CACHE_LINE_BYTES);
356eb743fadSDamjan Marion
357eb743fadSDamjan Marion  p = hash_get_mem (tm->thread_registrations_by_name, "workers");
358eb743fadSDamjan Marion  tr = p ? (vlib_thread_registration_t *) p[0] : 0;
359eb743fadSDamjan Marion  if (tr && tr->count > 0)
360eb743fadSDamjan Marion    {
361586afd76SDamjan Marion      vdm->first_worker_thread_index = tr->first_index;
362586afd76SDamjan Marion      vdm->next_worker_thread_index = tr->first_index;
363586afd76SDamjan Marion      vdm->last_worker_thread_index = tr->first_index + tr->count - 1;
364eb743fadSDamjan Marion    }
365b3bb1010SDamjan Marion  return 0;
366b3bb1010SDamjan Marion}
367b3bb1010SDamjan Marion
368b3bb1010SDamjan MarionVLIB_INIT_FUNCTION (vnet_device_init);
369eb743fadSDamjan Marion
3702231150bSDamjan Marion/*
3712231150bSDamjan Marion * fd.io coding-style-patch-verification: ON
3722231150bSDamjan Marion *
3732231150bSDamjan Marion * Local Variables:
3742231150bSDamjan Marion * eval: (c-set-style "gnu")
3752231150bSDamjan Marion * End:
3762231150bSDamjan Marion */
377