Retire io threads and main-thread-io mode
[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  * callback
180  *     If not null, this function will be called once during each main loop.
181  */
182 static_always_inline void
183 dpdk_worker_thread_internal (vlib_main_t *vm,
184                              dpdk_worker_thread_callback_t callback)
185 {
186   vlib_node_main_t * nm = &vm->node_main;
187   u64 cpu_time_now = clib_cpu_time_now ();
188
189   while (1)
190     {
191       vlib_worker_thread_barrier_check ();
192
193       vlib_frame_queue_dequeue_internal (vm);
194
195       /* Invoke callback if supplied */
196       if (PREDICT_FALSE(callback != NULL))
197           callback(vm);
198
199       vlib_node_runtime_t * n;
200       vec_foreach (n, nm->nodes_by_type[VLIB_NODE_TYPE_INPUT])
201         {
202           cpu_time_now = dispatch_node (vm, n, VLIB_NODE_TYPE_INPUT,
203                                         VLIB_NODE_STATE_POLLING, /* frame */ 0,
204                                         cpu_time_now);
205         }
206
207       if (_vec_len (nm->pending_frames))
208         {
209           int i;
210           cpu_time_now = clib_cpu_time_now ();
211           for (i = 0; i < _vec_len (nm->pending_frames); i++) {
212             vlib_pending_frame_t *p;
213
214             p = nm->pending_frames + i;
215
216             cpu_time_now = dispatch_pending_node (vm, p, cpu_time_now);
217           }
218           _vec_len (nm->pending_frames) = 0;
219         }
220       vlib_increment_main_loop_counter (vm);
221
222       /* Record time stamp in case there are no enabled nodes and above
223          calls do not update time stamp. */
224       cpu_time_now = clib_cpu_time_now ();
225     }
226 }
227
228 void dpdk_worker_thread (vlib_worker_thread_t * w,
229                          dpdk_worker_thread_callback_t callback)
230 {
231   vlib_main_t *vm;
232   dpdk_main_t * dm = &dpdk_main;
233
234   vm = vlib_get_main();
235
236   ASSERT(vm->cpu_index == os_get_cpu_number());
237
238   clib_time_init (&vm->clib_time);
239   clib_mem_set_heap (w->thread_mheap);
240
241   /* Wait until the dpdk init sequence is complete */
242   while (dm->worker_thread_release == 0)
243     vlib_worker_thread_barrier_check ();
244
245   dpdk_worker_thread_internal(vm, callback);
246 }
247
248 void dpdk_worker_thread_fn (void * arg)
249 {
250   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
251   vlib_worker_thread_init (w);
252   dpdk_worker_thread (w, 0);
253 }
254
255 #if VIRL == 0
256 VLIB_REGISTER_THREAD (worker_thread_reg, static) = {
257   .name = "workers",
258   .short_name = "wk",
259   .function = dpdk_worker_thread_fn,
260 };
261 #endif
262
263 static clib_error_t *
264 dpdk_thread_init (vlib_main_t *vm)
265 {
266   return (0);
267 }
268
269 VLIB_INIT_FUNCTION(dpdk_thread_init);