100a75b33327b4cac60d665d23384c1d575e7825
[vpp.git] / src / plugins / tlsopenssl / tls_async.c
1 /*
2  * Copyright (c) 2018 Intel 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 <vnet/api_errno.h>
17 #include <vlib/node_funcs.h>
18 #include <openssl/engine.h>
19 #include <tlsopenssl/tls_openssl.h>
20
21 #define SSL_ASYNC_INFLIGHT    1
22 #define SSL_ASYNC_READY       2
23 #define SSL_ASYNC_REENTER     3
24 #define MAX_VECTOR_ASYNC    256
25
26 typedef struct openssl_tls_callback_arg_
27 {
28   int thread_index;
29   int event_index;
30 } openssl_tls_callback_arg_t;
31
32 typedef struct openssl_event_
33 {
34   u32 ctx_index;
35   int session_index;
36   u8 status;
37
38   openssl_resume_handler *handler;
39   openssl_tls_callback_arg_t cb_args;
40 #define thread_idx cb_args.thread_index
41 #define event_idx cb_args.event_index
42   int next;
43 } openssl_evt_t;
44
45 typedef struct openssl_async_queue_
46 {
47   int evt_run_head;
48   int evt_run_tail;
49 } openssl_async_queue_t;
50
51 typedef struct openssl_async_
52 {
53   openssl_evt_t ***evt_pool;
54   openssl_async_queue_t *queue;
55   void (*polling) (void);
56   u8 start_polling;
57   ENGINE *engine;
58
59 } openssl_async_t;
60
61 void qat_polling ();
62 void qat_pre_init ();
63 void qat_polling_config ();
64 void dasync_polling ();
65
66 struct engine_polling
67 {
68   char *engine;
69   void (*polling) (void);
70   void (*pre_init) (void);
71   void (*thread_init) (void *);
72 };
73
74 void qat_init_thread (void *arg);
75
76 struct engine_polling engine_list[] = {
77   {"qat", qat_polling, qat_pre_init, qat_init_thread},
78   {"dasync", dasync_polling, NULL, NULL}
79 };
80
81 openssl_async_t openssl_async_main;
82 static vlib_node_registration_t tls_async_process_node;
83
84 /* to avoid build warning */
85 void session_send_rpc_evt_to_thread (u32 thread_index, void *fp,
86                                      void *rpc_args);
87
88 void
89 evt_pool_init (vlib_main_t * vm)
90 {
91   vlib_thread_main_t *vtm = vlib_get_thread_main ();
92   openssl_async_t *om = &openssl_async_main;
93   int i, num_threads;
94
95   num_threads = 1 /* main thread */  + vtm->n_threads;
96
97   TLS_DBG (2, "Totally there is %d thread\n", num_threads);
98
99   vec_validate (om->evt_pool, num_threads - 1);
100   vec_validate (om->queue, num_threads - 1);
101
102   om->start_polling = 0;
103   om->engine = 0;
104
105   for (i = 0; i < num_threads; i++)
106     {
107       om->queue[i].evt_run_head = -1;
108       om->queue[i].evt_run_tail = -1;
109     }
110   om->polling = NULL;
111
112   return;
113 }
114
115 int
116 openssl_engine_register (char *engine_name, char *algorithm, int async)
117 {
118   int i, registered = -1;
119   openssl_async_t *om = &openssl_async_main;
120   void (*p) (void);
121   ENGINE *engine;
122
123   for (i = 0; i < ARRAY_LEN (engine_list); i++)
124     {
125       if (!strcmp (engine_list[i].engine, engine_name))
126         {
127           om->polling = engine_list[i].polling;
128           registered = i;
129         }
130     }
131   if (registered < 0)
132     {
133       clib_error ("engine %s is not regisered in VPP", engine_name);
134       return -1;
135     }
136
137   ENGINE_load_builtin_engines ();
138   ENGINE_load_dynamic ();
139   engine = ENGINE_by_id (engine_name);
140
141   if (engine == NULL)
142     {
143       clib_warning ("Failed to find engine ENGINE_by_id %s", engine_name);
144       return -1;
145     }
146
147   om->engine = engine;
148   /* call pre-init */
149   p = engine_list[registered].pre_init;
150   if (p)
151     (*p) ();
152
153   if (algorithm)
154     {
155       if (!ENGINE_set_default_string (engine, algorithm))
156         {
157           clib_warning ("Failed to set engine %s algorithm %s\n",
158                         engine_name, algorithm);
159           return -1;
160         }
161     }
162   else
163     {
164       if (!ENGINE_set_default (engine, ENGINE_METHOD_ALL))
165         {
166           clib_warning ("Failed to set engine %s to all algorithm",
167                         engine_name);
168           return -1;
169         }
170     }
171
172   if (async)
173     {
174       openssl_async_node_enable_disable (1);
175     }
176
177   for (i = 0; i < vlib_num_workers (); i++)
178     {
179       if (engine_list[registered].thread_init)
180         session_send_rpc_evt_to_thread (i + 1,
181                                         engine_list[registered].thread_init,
182                                         (void *) &i);
183     }
184
185   om->start_polling = 1;
186
187   return 0;
188
189 }
190
191 static openssl_evt_t *
192 openssl_evt_get (u32 evt_index)
193 {
194   openssl_evt_t **evt;
195   evt =
196     pool_elt_at_index (openssl_async_main.evt_pool[vlib_get_thread_index ()],
197                        evt_index);
198   return *evt;
199 }
200
201 static openssl_evt_t *
202 openssl_evt_get_w_thread (int evt_index, u8 thread_index)
203 {
204   openssl_evt_t **evt;
205
206   evt =
207     pool_elt_at_index (openssl_async_main.evt_pool[thread_index], evt_index);
208   return *evt;
209 }
210
211 int
212 openssl_evt_free (int event_index, u8 thread_index)
213 {
214   openssl_async_t *om = &openssl_async_main;
215
216   /*pool operation */
217   pool_put_index (om->evt_pool[thread_index], event_index);
218
219   return 1;
220 }
221
222 static u32
223 openssl_evt_alloc (void)
224 {
225   u8 thread_index = vlib_get_thread_index ();
226   openssl_async_t *tm = &openssl_async_main;
227   openssl_evt_t **evt;
228
229   pool_get (tm->evt_pool[thread_index], evt);
230   if (!(*evt))
231     *evt = clib_mem_alloc (sizeof (openssl_evt_t));
232
233   clib_memset (*evt, 0, sizeof (openssl_evt_t));
234   (*evt)->event_idx = evt - tm->evt_pool[thread_index];
235   return ((*evt)->event_idx);
236 }
237
238
239 /* In most cases, tls_async_openssl_callback is called by HW to make event active
240  * When EAGAIN received, VPP will call this callback to retry
241  */
242 int
243 tls_async_openssl_callback (SSL * s, void *cb_arg)
244 {
245   openssl_evt_t *event, *event_tail;
246   openssl_async_t *om = &openssl_async_main;
247   openssl_tls_callback_arg_t *args = (openssl_tls_callback_arg_t *) cb_arg;
248   int thread_index = args->thread_index;
249   int event_index = args->event_index;
250   int *evt_run_tail = &om->queue[thread_index].evt_run_tail;
251   int *evt_run_head = &om->queue[thread_index].evt_run_head;
252
253   TLS_DBG (2, "Set event %d to run\n", event_index);
254
255   event = openssl_evt_get (event_index);
256
257   /* Happend when a recursive case, especially in SW simulation */
258   if (PREDICT_FALSE (event->status == SSL_ASYNC_READY))
259     {
260       event->status = SSL_ASYNC_REENTER;
261       return 0;
262     }
263   event->status = SSL_ASYNC_READY;
264   event->next = -1;
265
266   if (*evt_run_tail >= 0)
267     {
268       event_tail = openssl_evt_get_w_thread (*evt_run_tail, thread_index);
269       event_tail->next = event_index;
270     }
271   *evt_run_tail = event_index;
272   if (*evt_run_head < 0)
273     {
274       *evt_run_head = event_index;
275     }
276
277   return 1;
278 }
279
280 int
281 vpp_tls_async_init_event (tls_ctx_t * ctx,
282                           openssl_resume_handler * handler,
283                           session_t * session)
284 {
285   u32 eidx;
286   openssl_evt_t *event;
287   openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
288   u32 thread_id = ctx->c_thread_index;
289
290   eidx = openssl_evt_alloc ();
291   event = openssl_evt_get (eidx);
292   event->ctx_index = oc->openssl_ctx_index;
293   event->event_idx = eidx;
294   event->thread_idx = thread_id;
295   event->handler = handler;
296   event->session_index = session->session_index;
297   event->status = 0;
298   ctx->evt_index = eidx;
299 #ifdef HAVE_OPENSSL_ASYNC
300   SSL_set_async_callback_arg (oc->ssl, &event->cb_args);
301 #endif
302
303   return 1;
304 }
305
306
307 int
308 vpp_tls_async_update_event (tls_ctx_t * ctx, int eagain)
309 {
310   u32 eidx;
311   openssl_evt_t *event;
312
313   if (eagain)
314     {
315       eidx = ctx->evt_index;
316       event = openssl_evt_get (eidx);
317
318       return tls_async_openssl_callback (0, &event->cb_args);
319     }
320
321   return 1;
322 }
323
324 void
325 event_handler (void *tls_async)
326 {
327   openssl_resume_handler *handler;
328   openssl_evt_t *event;
329   session_t *session;
330   int thread_index;
331   tls_ctx_t *ctx;
332
333   event = (openssl_evt_t *) tls_async;
334   thread_index = event->thread_idx;
335   ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
336   handler = event->handler;
337   session = session_get (event->session_index, thread_index);
338
339   if (handler)
340     {
341       (*handler) (ctx, session);
342     }
343
344   return;
345 }
346
347  /* engine specific code to polling the response ring */
348 void
349 dasync_polling ()
350 {
351 /* dasync is a fake async device, and could not be polled.
352  * We have added code in the dasync engine to triggered the callback already,
353  * so nothing can be done here
354  */
355 }
356
357 void
358 qat_pre_init ()
359 {
360   openssl_async_t *om = &openssl_async_main;
361
362   ENGINE_ctrl_cmd (om->engine, "ENABLE_EXTERNAL_POLLING", 0, NULL, NULL, 0);
363 }
364
365 /* Below code is spefic to QAT engine, and other vendors can refer to this code to enable a new engine */
366 void
367 qat_init_thread (void *arg)
368 {
369   openssl_async_t *om = &openssl_async_main;
370   int thread_index = *(int *) arg;
371
372   ENGINE_ctrl_cmd (om->engine, "SET_INSTANCE_FOR_THREAD", thread_index,
373                    NULL, NULL, 0);
374
375   TLS_DBG (2, "set thread %d and instance %d mapping\n", thread_index,
376            thread_index);
377
378 }
379
380 void
381 qat_polling ()
382 {
383   openssl_async_t *om = &openssl_async_main;
384   int poll_status = 0;
385
386   if (om->start_polling)
387     {
388       ENGINE_ctrl_cmd (om->engine, "POLL", 0, &poll_status, NULL, 0);
389     }
390 }
391
392 void
393 openssl_async_polling ()
394 {
395   openssl_async_t *om = &openssl_async_main;
396   if (om->polling)
397     {
398       (*om->polling) ();
399     }
400 }
401
402 void
403 openssl_async_node_enable_disable (u8 is_en)
404 {
405   u8 state = is_en ? VLIB_NODE_STATE_POLLING : VLIB_NODE_STATE_DISABLED;
406   vlib_thread_main_t *vtm = vlib_get_thread_main ();
407   u8 have_workers = vtm->n_threads != 0;
408
409   /* *INDENT-OFF* */
410   foreach_vlib_main (({
411     if (have_workers && ii != 0)
412       {
413         vlib_node_set_state (this_vlib_main, tls_async_process_node.index,
414                          state);
415       }
416   }));
417   /* *INDENT-ON* */
418 }
419
420 int
421 tls_async_do_job (int eidx, u32 thread_index)
422 {
423   tls_ctx_t *ctx;
424   openssl_evt_t *event;
425
426   /* do the real job */
427   event = openssl_evt_get_w_thread (eidx, thread_index);
428   ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
429
430   if (ctx)
431     {
432       ctx->resume = 1;
433       session_send_rpc_evt_to_thread (thread_index, event_handler, event);
434     }
435   return 1;
436 }
437
438 int
439 tls_resume_from_crypto (int thread_index)
440 {
441   int i;
442
443   openssl_async_t *om = &openssl_async_main;
444   openssl_evt_t *event;
445   int *evt_run_head = &om->queue[thread_index].evt_run_head;
446   int *evt_run_tail = &om->queue[thread_index].evt_run_tail;
447
448   if (*evt_run_head < 0)
449     return 0;
450
451   for (i = 0; i < MAX_VECTOR_ASYNC; i++)
452     {
453       if (*evt_run_head >= 0)
454         {
455           event = openssl_evt_get_w_thread (*evt_run_head, thread_index);
456           tls_async_do_job (*evt_run_head, thread_index);
457           if (PREDICT_FALSE (event->status == SSL_ASYNC_REENTER))
458             {
459               /* recusive event triggered */
460               event->status = SSL_ASYNC_READY;
461               continue;
462             }
463
464           event->status = 0;
465           *evt_run_head = event->next;
466
467           if (event->next < 0)
468             {
469               *evt_run_tail = -1;
470               break;
471             }
472         }
473     }
474
475   return 0;
476
477 }
478
479 static clib_error_t *
480 tls_async_init (vlib_main_t * vm)
481 {
482   evt_pool_init (vm);
483   return 0;
484 }
485
486 static uword
487 tls_async_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
488                    vlib_frame_t * f)
489 {
490   u8 thread_index;
491   openssl_async_t *om = &openssl_async_main;
492
493   thread_index = vlib_get_thread_index ();
494   if (pool_elts (om->evt_pool[thread_index]) > 0)
495     {
496       openssl_async_polling ();
497       tls_resume_from_crypto (thread_index);
498     }
499
500   return 0;
501 }
502
503 VLIB_INIT_FUNCTION (tls_async_init);
504
505 /* *INDENT-OFF* */
506 VLIB_REGISTER_NODE (tls_async_process_node,static) = {
507     .function = tls_async_process,
508     .type = VLIB_NODE_TYPE_INPUT,
509     .name = "tls-async-process",
510     .state = VLIB_NODE_STATE_DISABLED,
511 };
512
513 /* *INDENT-ON* */
514
515 /*
516  * fd.io coding-style-patch-verification: ON
517  *
518  * Local Variables:
519  * eval: (c-set-style "gnu")
520  * End:
521  */