2 * Copyright (c) 2022 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <utils/debug.h>
18 #include <threading/thread.h>
19 #include <threading/condvar.h>
20 #include <threading/mutex.h>
21 #include <collections/array.h>
22 #include <collections/hashtable.h>
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vlibmemory/memclnt.api_enum.h>
27 #include "kernel_vpp_shared.h"
31 /* Include the (first) vlib-api API definition layer */
32 #include <vlibmemory/vl_memory_api_h.h>
33 /* Include the current layer (third) vpp API definition layer */
34 #include <vpp/api/vpe_types.api.h>
35 #include <vpp/api/vpe.api.h>
39 typedef struct private_vac_t private_vac_t;
40 typedef struct vl_api_header_t vl_api_header_t;
41 typedef struct vl_api_rheader_t vl_api_rheader_t;
42 typedef struct want_event_reply_t want_event_reply_t;
47 * Private variables and functions of vac_t class.
53 * public part of the vac_t object.
58 * Timeout for VPP API replies, in ms
60 uint16_t read_timeout;
63 * True if connected to VPP vlib
65 bool connected_to_vlib;
68 * True if receive thread is running
78 * Mutex to lock receive queue
83 * Condition variable rx thread susspend
85 condvar_t *suspend_cv;
88 * Condition variable rx thread resume
93 * Condition variable rx thread terminate
95 condvar_t *terminate_cv;
98 * Mutex to lock send VPP API message entries
100 mutex_t *entries_lock;
103 * VPP API message entries currently active, uintptr_t seq => entry_t
105 hashtable_t *entries;
108 * Mutex to lock VPP API event entries
110 mutex_t *events_lock;
113 * VPP API event entries currently active, uintptr_t id = event_t
118 * Current sequence number for VPP API messages
124 * VPP API message header
126 struct vl_api_header_t
132 /** opaque cookie to identify the client */
133 uint32_t client_index;
135 /** client context, to match reply with request */
137 } __attribute__ ((packed));
140 * VPP API response message header
142 struct vl_api_rheader_t
148 /** opaque cookie to identify the client */
150 } __attribute__ ((packed));
153 * VPP API register event response message header
155 struct want_event_reply_t
161 /** opaque cookie to identify the client */
164 /** retrun code for the request */
166 } __attribute__ ((packed));
169 * VPP API request entry the answer for a waiting thread is collected in
173 /** Condition variable thread is waiting */
175 /** Array of reply msgs in a multi-message response, as struct rmsgbuf_t */
177 /** All response messages received? */
179 /** Is VPP API dump? */
184 * Reply message buffer
195 * VPP API event entry
199 /** Event callback */
201 /** User data passed to callback */
206 * Free VPP API message
211 vl_msg_api_free (msg);
215 * Process a single VPP API message
218 vac_api_handler (private_vac_t *this, void *msg)
220 vl_api_rheader_t *rmp;
223 uintptr_t seq, event_id;
224 u16 id = ntohs (*((u16 *) msg));
225 msgbuf_t *msgbuf = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data));
226 int l = ntohl (msgbuf->data_len);
231 DBG2 (DBG_KNL, "vac msg ID %d has wrong len %d", id, l);
237 seq = (uintptr_t) rmp->context;
239 this->entries_lock->lock (this->entries_lock);
240 entry = this->entries->get (this->entries, (void *) seq);
246 vl_msg_api_get_msg_index ((u8 *) "control_ping_reply_f6b0b8ca");
249 entry->complete = TRUE;
250 entry->condvar->signal (entry->condvar);
252 this->entries_lock->unlock (this->entries_lock);
258 entry->complete = TRUE;
259 entry->condvar->signal (entry->condvar);
262 rmsg = malloc (l + sizeof (msgbuf_t));
264 memcpy (rmsg->data, msg, l);
265 array_insert (entry->rmsgs, ARRAY_TAIL, rmsg);
269 this->events_lock->lock (this->events_lock);
270 event_id = (uintptr_t) id;
271 event = this->events->get (this->events, (void *) event_id);
273 event->cb (msg, l, event->ctx);
275 DBG1 (DBG_KNL, "received unknown vac msg seq %u id %d len %d, ignored",
277 this->events_lock->unlock (this->events_lock);
280 this->entries_lock->unlock (this->entries_lock);
285 * VPP API receive thread
288 vac_rx_thread_fn (private_vac_t *this)
291 api_main_t *am = vlibapi_get_main ();
292 vl_api_memclnt_keepalive_t *mp;
293 vl_api_memclnt_keepalive_reply_t *rmp;
294 vl_shmem_hdr_t *shmem_hdr;
297 q = am->vl_input_queue;
301 while (!svm_queue_sub (q, (u8 *) &msg, SVM_Q_WAIT, 0))
303 u16 id = ntohs (*((u16 *) msg));
306 case VL_API_RX_THREAD_EXIT:
307 vl_msg_api_free ((void *) msg);
308 this->queue_lock->lock (this->queue_lock);
309 this->terminate_cv->signal (this->terminate_cv);
310 this->queue_lock->unlock (this->queue_lock);
311 DBG3 (DBG_KNL, "vac received rx thread exit [%d]",
312 VL_API_RX_THREAD_EXIT);
317 case VL_API_MEMCLNT_RX_THREAD_SUSPEND:
318 vl_msg_api_free ((void *) msg);
319 this->queue_lock->lock (this->queue_lock);
320 this->suspend_cv->signal (this->suspend_cv);
321 this->resume_cv->wait (this->resume_cv, this->queue_lock);
322 this->queue_lock->unlock (this->queue_lock);
323 DBG3 (DBG_KNL, "vac received rx thread suspend [%d]",
324 VL_API_MEMCLNT_RX_THREAD_SUSPEND);
327 case VL_API_MEMCLNT_READ_TIMEOUT:
328 DBG3 (DBG_KNL, "vac received read timeout [%d]",
329 VL_API_MEMCLNT_READ_TIMEOUT);
330 vl_msg_api_free ((void *) msg);
333 case VL_API_MEMCLNT_KEEPALIVE:
335 rmp = vl_msg_api_alloc (sizeof (*rmp));
336 memset (rmp, 0, sizeof (*rmp));
337 u16 msg_id = vl_msg_api_get_msg_index (
338 (u8 *) "memclnt_keepalive_reply_e8d4e804");
339 rmp->_vl_msg_id = ntohs (msg_id);
340 rmp->context = mp->context;
341 shmem_hdr = am->shmem_hdr;
342 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &rmp);
343 vl_msg_api_free ((void *) msg);
344 DBG3 (DBG_KNL, "vac received keepalive %d",
345 VL_API_MEMCLNT_KEEPALIVE);
349 vac_api_handler (this, (void *) msg);
357 METHOD (vac_t, destroy, void, private_vac_t *this)
359 if (this->connected_to_vlib)
363 api_main_t *am = vlibapi_get_main ();
364 vl_api_rx_thread_exit_t *ep;
366 ep = vl_msg_api_alloc (sizeof (*ep));
367 memset (ep, 0, sizeof (*ep));
369 vl_msg_api_get_msg_index ((u8 *) "rx_thread_exit_c3a3a452");
370 ep->_vl_msg_id = ntohs (msg_id);
371 vl_msg_api_send_shmem (am->vl_input_queue, (u8 *) &ep);
372 this->queue_lock->lock (this->queue_lock);
373 timed_out = this->terminate_cv->timed_wait (this->terminate_cv,
374 this->queue_lock, 5000);
375 this->queue_lock->unlock (this->queue_lock);
377 this->rx->cancel (this->rx);
379 this->rx->join (this->rx);
381 vl_client_disconnect ();
382 vl_client_api_unmap ();
385 this->queue_lock->destroy (this->queue_lock);
386 this->suspend_cv->destroy (this->suspend_cv);
387 this->resume_cv->destroy (this->resume_cv);
388 this->terminate_cv->destroy (this->terminate_cv);
389 this->entries->destroy (this->entries);
390 this->entries_lock->destroy (this->entries_lock);
391 this->events->destroy (this->events);
392 this->events_lock->destroy (this->events_lock);
399 * Write a VPP API message to shared memory
402 vac_write (private_vac_t *this, char *p, int l, uint32_t ctx)
404 api_main_t *am = vlibapi_get_main ();
405 vl_api_header_t *mp = vl_msg_api_alloc (l);
406 memset (mp, 0, sizeof (*mp));
409 if (!this->connected_to_vlib)
416 mp->client_index = am->my_client_index;
418 q = am->shmem_hdr->vl_input_queue;
419 if (svm_queue_add (q, (u8 *) &mp, 0))
421 DBG1 (DBG_KNL, "vac vpe_api_write failed");
430 * Clean up a thread waiting entry
433 destroy_entry (entry_t *entry)
435 entry->condvar->destroy (entry->condvar);
436 array_destroy_function (entry->rmsgs, (void *) free, NULL);
441 * Send VPP API message and wait for a reply
444 send_vac (private_vac_t *this, char *in, int in_len, char **out, int *out_len,
448 uint32_t ctx = ref_get (&this->seq);
449 uintptr_t seq = (uintptr_t) ctx;
454 this->entries_lock->lock (this->entries_lock);
455 INIT (entry, .condvar = condvar_create (CONDVAR_TYPE_DEFAULT),
456 .rmsgs = array_create (0, 0), .is_dump = is_dump, );
457 this->entries->put (this->entries, (void *) seq, entry);
459 if (vac_write (this, in, in_len, ctx))
461 destroy_entry (entry);
462 this->entries_lock->unlock (this->entries_lock);
468 vl_api_control_ping_t *mp;
470 mp = vl_msg_api_alloc (sizeof (*mp));
471 memset (mp, 0, sizeof (*mp));
472 u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "control_ping_51077d14");
473 mp->_vl_msg_id = ntohs (msg_id);
474 rv = vac_write (this, (char *) mp, sizeof (*mp), ctx);
475 vl_msg_api_free (mp);
478 DBG2 (DBG_KNL, "vac_write VL_API_CONTROL_PING failed");
479 destroy_entry (entry);
480 this->entries_lock->unlock (this->entries_lock);
485 while (!entry->complete)
487 if (this->read_timeout)
489 if (entry->condvar->timed_wait (entry->condvar, this->entries_lock,
490 this->read_timeout * 1000))
497 entry->condvar->wait (entry->condvar, this->entries_lock);
501 this->entries->remove (this->entries, (void *) seq);
502 this->entries_lock->unlock (this->entries_lock);
504 if (!entry->complete)
506 destroy_entry (entry);
507 DBG1 (DBG_KNL, "vac timeout");
511 for (i = 0, *out_len = 0; i < array_count (entry->rmsgs); i++)
513 array_get (entry->rmsgs, i, &rmsg);
514 *out_len += rmsg->data_len;
516 ptr = malloc (*out_len);
518 while (array_remove (entry->rmsgs, ARRAY_HEAD, &rmsg))
520 memcpy (ptr, rmsg->data, rmsg->data_len);
521 ptr += rmsg->data_len;
525 destroy_entry (entry);
530 METHOD (vac_t, vac_send, status_t, private_vac_t *this, char *in, int in_len,
531 char **out, int *out_len)
533 return send_vac (this, in, in_len, out, out_len, FALSE);
536 METHOD (vac_t, vac_send_dump, status_t, private_vac_t *this, char *in,
537 int in_len, char **out, int *out_len)
539 return send_vac (this, in, in_len, out, out_len, TRUE);
542 METHOD (vac_t, register_event, status_t, private_vac_t *this, char *in,
543 int in_len, event_cb_t cb, uint16_t event_id, void *ctx)
547 want_event_reply_t *rmp;
548 uintptr_t id = (uintptr_t) event_id;
551 if (vac->send (vac, in, in_len, &out, &out_len))
557 vl_msg_api_free (in);
558 this->events_lock->lock (this->events_lock);
559 INIT (event, .cb = cb, .ctx = ctx, );
560 this->events->put (this->events, (void *) id, event);
561 this->events_lock->unlock (this->events_lock);
567 vac_create (char *name)
575 .send_dump = _vac_send_dump,
576 .register_event = _register_event,
578 .rx_is_running = FALSE,
579 .read_timeout = lib->settings->get_int(lib->settings,
580 "%s.plugins.kernel-vpp.read_timeout", 0, lib->ns),
581 .queue_lock = mutex_create(MUTEX_TYPE_DEFAULT),
582 .suspend_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
583 .resume_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
584 .terminate_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
585 .entries_lock = mutex_create(MUTEX_TYPE_RECURSIVE),
586 .entries = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
587 .events_lock = mutex_create(MUTEX_TYPE_DEFAULT),
588 .events = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
592 clib_mem_init_thread_safe (0, 256 << 20);
594 if (vl_client_api_map ("/vpe-api"))
596 DBG1 (DBG_KNL, "vac unable to map");
601 if (vl_client_connect (name, 0, 32) < 0)
603 DBG1 (DBG_KNL, "vac unable to connect");
604 vl_client_api_unmap ();
609 this->connected_to_vlib = TRUE;
611 this->rx = thread_create ((thread_main_t) vac_rx_thread_fn, this);
614 vl_client_api_unmap ();
618 this->rx_is_running = TRUE;
621 return &this->public;