Add support for multiple microarchitectures in single binary
[vpp.git] / vnet / vnet / devices / dpdk / threads.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 #include <vnet/vnet.h>
16 #include <vppinfra/vec.h>
17 #include <vppinfra/error.h>
18 #include <vppinfra/format.h>
19 #include <signal.h>
20
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/devices/dpdk/dpdk.h>
23 #include <vnet/devices/dpdk/threads.h>
24
25 #include <vlibmemory/api.h>
26 #include <vlibmemory/vl_memory_msg_enum.h> /* enumerate all vlib messages */
27
28 #define vl_typedefs             /* define message structures */
29 #include <vlibmemory/vl_memory_api_h.h> 
30 #undef vl_typedefs
31
32 /* instantiate all the print functions we know about */
33 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
34 #define vl_printfun
35 #include <vlibmemory/vl_memory_api_h.h> 
36 #undef vl_printfun
37
38
39 /*
40  * Check the frame queue to see if any frames are available.
41  * If so, pull the packets off the frames and put them to 
42  * the handoff node.
43  */
44 static inline int vlib_frame_queue_dequeue_internal (vlib_main_t *vm)
45 {
46   u32 thread_id = vm->cpu_index;
47   vlib_frame_queue_t *fq = vlib_frame_queues[thread_id];
48   vlib_frame_queue_elt_t *elt;
49   u32 * from, * to;
50   vlib_frame_t * f;
51   int msg_type;
52   int processed = 0;
53   u32 n_left_to_node;
54   u32 vectors = 0;
55
56   ASSERT (fq);
57   ASSERT(vm == vlib_mains[thread_id]);
58
59   /*
60    * Gather trace data for frame queues
61    */
62   if (PREDICT_FALSE(fq->trace))
63     {
64       frame_queue_trace_t *fqt;
65       frame_queue_nelt_counter_t *fqh;
66       u32 elix;
67    
68       fqt = &dpdk_main.frame_queue_traces[thread_id];
69
70       fqt->nelts = fq->nelts;
71       fqt->head = fq->head;
72       fqt->head_hint = fq->head_hint;
73       fqt->tail = fq->tail;
74       fqt->threshold = fq->vector_threshold;
75       fqt->n_in_use = fqt->tail - fqt->head;
76       if (fqt->n_in_use >= fqt->nelts){
77         // if beyond max then use max
78         fqt->n_in_use = fqt->nelts-1;
79       }
80
81       /* Record the number of elements in use in the histogram */
82       fqh = &dpdk_main.frame_queue_histogram[thread_id];
83       fqh->count[ fqt->n_in_use ]++;
84
85       /* Record a snapshot of the elements in use */
86       for (elix=0; elix<fqt->nelts; elix++) {
87         elt = fq->elts + ((fq->head+1 + elix) & (fq->nelts-1));
88         if (1 || elt->valid) 
89           {
90             fqt->n_vectors[elix] = elt->n_vectors;
91           }
92       }
93       fqt->written = 1;
94     }
95
96   while (1)
97     {
98       if (fq->head == fq->tail)
99         {
100           fq->head_hint = fq->head;
101           return processed;
102         }
103       
104       elt = fq->elts + ((fq->head+1) & (fq->nelts-1));
105       
106       if (!elt->valid)
107         {
108           fq->head_hint = fq->head;
109           return processed;
110         }
111
112       from = elt->buffer_index;
113       msg_type = elt->msg_type;
114
115       ASSERT (msg_type == VLIB_FRAME_QUEUE_ELT_DISPATCH_FRAME);
116       ASSERT (elt->n_vectors <= VLIB_FRAME_SIZE);
117
118       f = vlib_get_frame_to_node 
119           (vm, 1 ? handoff_dispatch_node.index : ethernet_input_node.index);
120
121       to = vlib_frame_vector_args (f);
122
123       n_left_to_node = elt->n_vectors;
124
125       while (n_left_to_node >= 4)
126         {
127           to[0] = from[0];
128           to[1] = from[1];
129           to[2] = from[2];
130           to[3] = from[3];
131           to += 4;
132           from += 4;
133           n_left_to_node -= 4;
134         }
135
136       while (n_left_to_node > 0)
137         {
138           to[0] = from[0];
139           to++;
140           from++;
141           n_left_to_node--;
142         }
143
144       vectors += elt->n_vectors;
145       f->n_vectors = elt->n_vectors;
146       vlib_put_frame_to_node 
147           (vm, 1 ? handoff_dispatch_node.index : ethernet_input_node.index, f);
148
149       elt->valid = 0;
150       elt->n_vectors = 0;
151       elt->msg_type = 0xfefefefe;
152       CLIB_MEMORY_BARRIER();
153       fq->head++;
154       processed++;
155
156       /* 
157        * Limit the number of packets pushed into the graph
158        */
159       if (vectors >= fq->vector_threshold)
160         {
161           fq->head_hint = fq->head;
162           return processed;
163         }
164     }
165   ASSERT(0);
166   return processed;
167 }
168
169 int dpdk_frame_queue_dequeue (vlib_main_t *vm) 
170 {
171   return vlib_frame_queue_dequeue_internal (vm);
172 }
173
174 /*
175  * dpdk_worker_thread - Contains the main loop of a worker thread.
176  *
177  * w
178  *     Information for the current thread
179  * io_name
180  *     The name of thread performing dpdk device IO (if any). If there are no
181  *     instances of that thread, then the current thread will do dpdk device
182  *     polling. Ports will be divided among instances of the current thread.
183  * callback
184  *     If not null, this function will be called once during each main loop.
185  */
186 static_always_inline void
187 dpdk_worker_thread_internal (vlib_main_t *vm,
188                              dpdk_worker_thread_callback_t callback,
189                              int have_io_threads)
190 {
191   vlib_node_main_t * nm = &vm->node_main;
192   u64 cpu_time_now = clib_cpu_time_now ();
193
194   while (1)
195     {
196       vlib_worker_thread_barrier_check ();
197
198       vlib_frame_queue_dequeue_internal (vm);
199
200       /* Invoke callback if supplied */
201       if (PREDICT_FALSE(callback != NULL))
202           callback(vm);
203
204       if (!have_io_threads)
205         {
206           vlib_node_runtime_t * n;
207           vec_foreach (n, nm->nodes_by_type[VLIB_NODE_TYPE_INPUT])
208             {
209               cpu_time_now = dispatch_node (vm, n, VLIB_NODE_TYPE_INPUT,
210                                             VLIB_NODE_STATE_POLLING, /* frame */ 0,
211                                             cpu_time_now);
212             }
213
214         }
215
216       if (_vec_len (nm->pending_frames))
217         {
218           int i;
219           cpu_time_now = clib_cpu_time_now ();
220           for (i = 0; i < _vec_len (nm->pending_frames); i++) {
221             vlib_pending_frame_t *p;
222
223             p = nm->pending_frames + i;
224
225             cpu_time_now = dispatch_pending_node (vm, p, cpu_time_now);
226           }
227           _vec_len (nm->pending_frames) = 0;
228         }
229       vlib_increment_main_loop_counter (vm);
230
231       /* Record time stamp in case there are no enabled nodes and above
232          calls do not update time stamp. */
233       cpu_time_now = clib_cpu_time_now ();
234     }
235 }
236
237 void dpdk_worker_thread (vlib_worker_thread_t * w,
238                          char *io_name,
239                          dpdk_worker_thread_callback_t callback)
240 {
241   vlib_main_t *vm;
242   uword * p;
243   vlib_thread_main_t * tm = vlib_get_thread_main();
244   vlib_thread_registration_t * tr;
245   dpdk_main_t * dm = &dpdk_main;
246
247   vm = vlib_get_main();
248
249   ASSERT(vm->cpu_index == os_get_cpu_number());
250
251   clib_time_init (&vm->clib_time);
252   clib_mem_set_heap (w->thread_mheap);
253
254   /* Wait until the dpdk init sequence is complete */
255   while (dm->io_thread_release == 0)
256     vlib_worker_thread_barrier_check ();
257
258   /* any I/O threads? */
259   p = hash_get_mem (tm->thread_registrations_by_name, io_name);
260   tr = (vlib_thread_registration_t *)p[0];
261
262   if (tr && tr->count > 0)
263     dpdk_worker_thread_internal(vm, callback, /* have_io_threads */ 1);
264   else
265     dpdk_worker_thread_internal(vm, callback, /* have_io_threads */ 0);
266 }
267
268 void dpdk_worker_thread_fn (void * arg)
269 {
270   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
271   vlib_worker_thread_init (w);
272   dpdk_worker_thread (w, "io", 0);
273 }
274
275 #if VIRL == 0
276 VLIB_REGISTER_THREAD (worker_thread_reg, static) = {
277   .name = "workers",
278   .short_name = "wk",
279   .function = dpdk_worker_thread_fn,
280 };
281 #endif
282
283 void dpdk_io_thread_fn (void * arg)
284 {
285   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
286   vlib_worker_thread_init (w);
287   dpdk_io_thread (w, 0, 0, "workers", 0);
288 }
289
290 #if VIRL == 0
291 VLIB_REGISTER_THREAD (io_thread_reg, static) = {
292   .name = "io",
293   .short_name = "io",
294   .function = dpdk_io_thread_fn,
295 };
296 #endif
297
298 static clib_error_t *
299 dpdk_thread_init (vlib_main_t *vm)
300 {
301   return (0);
302 }
303
304 VLIB_INIT_FUNCTION(dpdk_thread_init);