summaryrefslogtreecommitdiffstats
path: root/src/vcl
diff options
context:
space:
mode:
authorhanlin <hanlin_wang@163.com>2020-07-13 11:09:15 +0800
committerFlorin Coras <florin.coras@gmail.com>2020-08-12 01:57:18 +0000
commita3a489691d7f2813702fae2d915120743b860d62 (patch)
tree23a22a25ebe051be37244c45fc3a0732cc296e0d /src/vcl
parent40c07ce7a78af69f7354222d4663a65cd5572049 (diff)
vcl: support multi-threads with session migration
Currently, mutlti-threads only support one dispatch thread and multiple worker threads, eventually only dispatch thread is a vcl worker and can interact with epoll. This patch will register all threads as vcl worker, and then each thread can interact with epoll now. Moreover, session migration also supported, such as socket created in thread A and used (bind, connect and etc.) in thread B. Type: feature Signed-off-by: hanlin <hanlin_wang@163.com> Change-Id: Iab0b43a33466968c1423d7d20faf1460c8589d91
Diffstat (limited to 'src/vcl')
-rw-r--r--src/vcl/ldp.c11
-rw-r--r--src/vcl/vcl_cfg.c5
-rw-r--r--src/vcl/vcl_locked.c245
-rw-r--r--src/vcl/vcl_locked.h3
-rw-r--r--src/vcl/vcl_private.c2
-rw-r--r--src/vcl/vcl_private.h7
-rw-r--r--src/vcl/vppcom.c13
7 files changed, 261 insertions, 25 deletions
diff --git a/src/vcl/ldp.c b/src/vcl/ldp.c
index cda4425e574..fddf45cd502 100644
--- a/src/vcl/ldp.c
+++ b/src/vcl/ldp.c
@@ -2223,7 +2223,7 @@ epoll_create1 (int flags)
if ((errno = -ldp_init ()))
return -1;
- if (ldp->vcl_needs_real_epoll)
+ if (ldp->vcl_needs_real_epoll || vls_use_real_epoll ())
{
/* Make sure workers have been allocated */
if (!ldp->workers)
@@ -2425,7 +2425,7 @@ static inline int
ldp_epoll_pwait_eventfd (int epfd, struct epoll_event *events,
int maxevents, int timeout, const sigset_t * sigmask)
{
- ldp_worker_ctx_t *ldpw = ldp_worker_get_current ();
+ ldp_worker_ctx_t *ldpw;
int libc_epfd, rv = 0, num_ev;
vls_handle_t ep_vlsh;
@@ -2438,6 +2438,11 @@ ldp_epoll_pwait_eventfd (int epfd, struct epoll_event *events,
return -1;
}
+ if (vls_mt_wrk_supported ())
+ if (PREDICT_FALSE (vppcom_worker_index () == ~0))
+ vls_register_vcl_worker ();
+
+ ldpw = ldp_worker_get_current ();
if (epfd == ldpw->vcl_mq_epfd)
return libc_epoll_pwait (epfd, events, maxevents, timeout, sigmask);
@@ -2497,7 +2502,7 @@ ldp_epoll_pwait_eventfd (int epfd, struct epoll_event *events,
rv = vls_epoll_wait (ep_vlsh, events, maxevents, 0);
if (rv > 0)
goto done;
- else if (rv < 0)
+ else if (PREDICT_FALSE (rv < 0))
{
errno = -rv;
rv = -1;
diff --git a/src/vcl/vcl_cfg.c b/src/vcl/vcl_cfg.c
index 2ae940170b7..a94c874532f 100644
--- a/src/vcl/vcl_cfg.c
+++ b/src/vcl/vcl_cfg.c
@@ -498,6 +498,11 @@ vppcom_cfg_read_file (char *conf_fname)
VCFG_DBG (0, "VCL<%d>: configured tls-engine %u (0x%x)",
getpid (), vcl_cfg->tls_engine, vcl_cfg->tls_engine);
}
+ else if (unformat (line_input, "multi-thread"))
+ {
+ vcl_cfg->mt_supported = 1;
+ VCFG_DBG (0, "VCL<%d>: configured with multithread", getpid ());
+ }
else if (unformat (line_input, "}"))
{
vc_cfg_input = 0;
diff --git a/src/vcl/vcl_locked.c b/src/vcl/vcl_locked.c
index fc48618429b..02da8cd2cf5 100644
--- a/src/vcl/vcl_locked.c
+++ b/src/vcl/vcl_locked.c
@@ -31,6 +31,9 @@ typedef struct vcl_locked_session_
u32 worker_index;
u32 vls_index;
u32 shared_data_index;
+ /** VCL session owned by different workers because of migration */
+ u32 owner_vcl_wrk_index;
+ uword *vcl_wrk_index_to_session_index;
} vcl_locked_session_t;
typedef struct vls_worker_
@@ -38,7 +41,6 @@ typedef struct vls_worker_
vcl_locked_session_t *vls_pool;
uword *session_index_to_vlsh_table;
u32 wrk_index;
- volatile int rpc_done;
} vls_worker_t;
typedef struct vls_local_
@@ -68,6 +70,7 @@ vls_main_t *vlsm;
typedef enum vls_rpc_msg_type_
{
VLS_RPC_CLONE_AND_SHARE,
+ VLS_RPC_SESS_CLEANUP,
} vls_rpc_msg_type_e;
typedef struct vls_rpc_msg_
@@ -79,14 +82,35 @@ typedef struct vls_rpc_msg_
typedef struct vls_clone_and_share_msg_
{
u32 vls_index; /**< vls to be shared */
- u32 origin_vls_wrk; /**< worker that initiated the rpc */
+ u32 session_index; /**< vcl session to be shared */
+ u32 origin_vls_wrk; /**< vls worker that initiated the rpc */
u32 origin_vls_index; /**< vls session of the originator */
+ u32 origin_vcl_wrk; /**< vcl worker that initiated the rpc */
+ u32 origin_session_index; /**< vcl session of the originator */
} vls_clone_and_share_msg_t;
+typedef struct vls_sess_cleanup_msg_
+{
+ u32 session_index; /**< vcl session to be cleaned */
+ u32 origin_vcl_wrk; /**< worker that initiated the rpc */
+} vls_sess_cleanup_msg_t;
+
+void vls_send_session_cleanup_rpc (vcl_worker_t * wrk,
+ u32 dst_wrk_index, u32 dst_session_index);
+void vls_send_clone_and_share_rpc (vcl_worker_t * wrk,
+ vcl_locked_session_t * vls,
+ u32 session_index, u32 vls_wrk_index,
+ u32 dst_wrk_index, u32 dst_vls_index,
+ u32 dst_session_index);
+
+
static inline u32
vls_get_worker_index (void)
{
- return vcl_get_worker_index ();
+ if (vls_mt_wrk_supported ())
+ return vlsl->vls_wrk_index;
+ else
+ return vcl_get_worker_index ();
}
static u32
@@ -190,7 +214,10 @@ static void
vls_mt_add (void)
{
vlsl->vls_mt_n_threads += 1;
- vcl_set_worker_index (vlsl->vls_wrk_index);
+ if (vls_mt_wrk_supported ())
+ vls_register_vcl_worker ();
+ else
+ vcl_set_worker_index (vlsl->vls_wrk_index);
}
static inline void
@@ -305,6 +332,12 @@ vls_alloc (vcl_session_handle_t sh)
vls->shared_data_index = ~0;
hash_set (wrk->session_index_to_vlsh_table, vls->session_index,
vls->vls_index);
+ if (vls_mt_wrk_supported ())
+ {
+ hash_set (vls->vcl_wrk_index_to_session_index, vls->worker_index,
+ vls->session_index);
+ vls->owner_vcl_wrk_index = vls->worker_index;
+ }
clib_spinlock_init (&vls->lock);
vls_table_wunlock ();
@@ -556,7 +589,8 @@ vls_unshare_session (vcl_locked_session_t * vls, vcl_worker_t * wrk)
u32 n_subscribers;
vcl_session_t *s;
- ASSERT (vls->shared_data_index != ~0);
+ if (vls->shared_data_index == ~0)
+ return 0;
s = vcl_session_get (wrk, vls->session_index);
if (s->session_state == STATE_LISTEN)
@@ -792,10 +826,76 @@ vls_mt_rel_locks (int locks_acq)
vls_mt_create_unlock ();
}
+static void
+vls_session_migrate (vcl_locked_session_t * vls)
+{
+ u32 wrk_index = vcl_get_worker_index ();
+ vcl_worker_t *wrk;
+ u32 src_sid, sid;
+ vcl_session_t *session;
+ uword *p;
+
+ if (!vls_mt_wrk_supported ())
+ return;
+
+ if (PREDICT_TRUE (vls->worker_index == wrk_index))
+ return;
+ if ((p = hash_get (vls->vcl_wrk_index_to_session_index, wrk_index)))
+ {
+ vls->worker_index = wrk_index;
+ vls->session_index = (u32) p[0];
+ return;
+ }
+
+ /* migrate from orignal vls */
+ if (!(p = hash_get (vls->vcl_wrk_index_to_session_index,
+ vls->owner_vcl_wrk_index)))
+ {
+ VERR ("session in owner worker(%u) is free", vls->owner_vcl_wrk_index);
+ ASSERT (0);
+ return;
+ }
+
+ src_sid = (u32) p[0];
+ wrk = vcl_worker_get_current ();
+ session = vcl_session_alloc (wrk);
+ sid = session->session_index;
+ vls_send_clone_and_share_rpc (wrk, vls, sid, vls_get_worker_index (),
+ vls->owner_vcl_wrk_index, vls->vls_index,
+ src_sid);
+ session->session_index = sid;
+ vls->worker_index = wrk_index;
+ vls->session_index = sid;
+ hash_set (vls->vcl_wrk_index_to_session_index, wrk_index, sid);
+ VDBG (1, "migrate session of worker (session): %u (%u) -> %u (%u)",
+ vls->owner_vcl_wrk_index, src_sid, wrk_index, sid);
+
+ if (PREDICT_FALSE (session->is_vep && session->vep.next_sh != ~0))
+ {
+ /* TODO: rollback? */
+ VERR ("can't migrate nonempty epoll session");
+ ASSERT (0);
+ return;
+ }
+ else if (PREDICT_FALSE (!session->is_vep &&
+ session->session_state != STATE_CLOSED))
+ {
+ /* TODO: rollback? */
+ VERR ("migrate NOT supported, session_status (%u)",
+ session->session_state);
+ ASSERT (0);
+ return;
+ }
+}
+
#define vls_mt_guard(_vls, _op) \
int _locks_acq = 0; \
if (PREDICT_FALSE (vcl_get_worker_index () == ~0)) \
vls_mt_add (); \
+ if (PREDICT_FALSE (_vls && vls_mt_wrk_supported () && \
+ ((vcl_locked_session_t *)_vls)->worker_index != \
+ vcl_get_worker_index ())) \
+ vls_session_migrate (_vls); \
if (PREDICT_FALSE (vlsl->vls_mt_n_threads > 1)) \
vls_mt_acq_locks (_vls, _op, &_locks_acq); \
@@ -893,6 +993,7 @@ vls_attr (vls_handle_t vlsh, uint32_t op, void *buffer, uint32_t * buflen)
if (!(vls = vls_get_w_dlock (vlsh)))
return VPPCOM_EBADFD;
+ vls_session_migrate (vls);
rv = vppcom_session_attr (vls_to_sh_tu (vls), op, buffer, buflen);
vls_get_and_unlock (vlsh);
return rv;
@@ -948,6 +1049,9 @@ vls_mp_checks (vcl_locked_session_t * vls, int is_add)
vcl_session_t *s;
u32 owner_wrk;
+ if (vls_mt_wrk_supported ())
+ return;
+
s = vcl_session_get (wrk, vls->session_index);
switch (s->session_state)
{
@@ -1024,6 +1128,27 @@ vls_create (uint8_t proto, uint8_t is_nonblocking)
return vlsh;
}
+static void
+vls_migrate_session_close (vcl_locked_session_t * vls)
+{
+ u32 session_index, wrk_index;
+ vcl_worker_t *wrk = vcl_worker_get_current ();
+
+ if (!vls_mt_wrk_supported ())
+ return;
+
+ /* *INDENT-OFF* */
+ hash_foreach (wrk_index, session_index, vls->vcl_wrk_index_to_session_index,
+ ({
+ if (vcl_get_worker_index () != wrk_index)
+ {
+ vls_send_session_cleanup_rpc (wrk, wrk_index, session_index);
+ }
+ }));
+ /* *INDENT-ON* */
+ hash_free (vls->vcl_wrk_index_to_session_index);
+}
+
int
vls_close (vls_handle_t vlsh)
{
@@ -1039,13 +1164,15 @@ vls_close (vls_handle_t vlsh)
return VPPCOM_EBADFD;
}
- vls_mt_guard (0, VLS_MT_OP_SPOOL);
+ vls_mt_guard (vls, VLS_MT_OP_SPOOL);
if (vls_is_shared (vls))
rv = vls_unshare_session (vls, vcl_worker_get_current ());
else
rv = vppcom_session_close (vls_to_sh (vls));
+ vls_migrate_session_close (vls);
+
vls_free (vls);
vls_mt_unguard ();
@@ -1101,6 +1228,7 @@ vls_epoll_ctl (vls_handle_t ep_vlsh, int op, vls_handle_t vlsh,
vls_table_rlock ();
ep_vls = vls_get_and_lock (ep_vlsh);
vls = vls_get_and_lock (vlsh);
+ vls_session_migrate (ep_vls);
ep_sh = vls_to_sh (ep_vls);
sh = vls_to_sh (vls);
@@ -1388,21 +1516,42 @@ vls_clone_and_share_rpc_handler (void *args)
vls_clone_and_share_msg_t *msg = (vls_clone_and_share_msg_t *) args;
vls_worker_t *wrk = vls_worker_get_current (), *dst_wrk;
vcl_locked_session_t *vls, *dst_vls;
- vcl_worker_t *dst_vcl_wrk;
+ vcl_worker_t *vcl_wrk = vcl_worker_get_current (), *dst_vcl_wrk;
vcl_session_t *s, *dst_s;
vls = vls_session_get (wrk, msg->vls_index);
- vls_init_share_session (wrk, vls);
- s = vcl_session_get (vcl_worker_get_current (), vls->session_index);
+ if (!vls_mt_wrk_supported ())
+ vls_init_share_session (wrk, vls);
+
+ s = vcl_session_get (vcl_wrk, msg->session_index);
dst_wrk = vls_worker_get (msg->origin_vls_wrk);
- dst_vcl_wrk = vcl_worker_get (msg->origin_vls_wrk);
+ dst_vcl_wrk = vcl_worker_get (msg->origin_vcl_wrk);
dst_vls = vls_session_get (dst_wrk, msg->origin_vls_index);
dst_vls->shared_data_index = vls->shared_data_index;
- dst_s = vcl_session_get (dst_vcl_wrk, dst_vls->session_index);
+ dst_s = vcl_session_get (dst_vcl_wrk, msg->origin_session_index);
clib_memcpy (dst_s, s, sizeof (*s));
+ dst_vcl_wrk->rpc_done = 1;
+
+ VDBG (1, "proces session clone of worker (session): %u (%u) -> %u (%u)",
+ vcl_wrk->wrk_index, msg->session_index, dst_vcl_wrk->wrk_index,
+ msg->origin_session_index);
+}
+
+static void
+vls_session_cleanup_rpc_handler (void *args)
+{
+ vls_sess_cleanup_msg_t *msg = (vls_sess_cleanup_msg_t *) args;
+ vcl_worker_t *wrk = vcl_worker_get_current ();
+ vcl_worker_t *dst_wrk = vcl_worker_get (msg->origin_vcl_wrk);
+
+ vppcom_session_close (vcl_session_handle_from_index (msg->session_index));
+
dst_wrk->rpc_done = 1;
+
+ VDBG (1, "proces session cleanup of worker (session): %u (%u) from %u ()",
+ wrk->wrk_index, msg->session_index, dst_wrk->wrk_index);
}
static void
@@ -1414,29 +1563,68 @@ vls_rpc_handler (void *args)
case VLS_RPC_CLONE_AND_SHARE:
vls_clone_and_share_rpc_handler (msg->data);
break;
+ case VLS_RPC_SESS_CLEANUP:
+ vls_session_cleanup_rpc_handler (msg->data);
+ break;
default:
break;
}
}
void
-vls_send_clone_and_share_rpc (vls_worker_t * wrk, vcl_locked_session_t * vls,
- u32 dst_wrk_index, u32 dst_vls_index)
+vls_send_clone_and_share_rpc (vcl_worker_t * wrk,
+ vcl_locked_session_t * vls, u32 session_index,
+ u32 vls_wrk_index, u32 dst_wrk_index,
+ u32 dst_vls_index, u32 dst_session_index)
{
u8 data[sizeof (u8) + sizeof (vls_clone_and_share_msg_t)];
vls_clone_and_share_msg_t *msg;
vls_rpc_msg_t *rpc;
+ int ret;
rpc = (vls_rpc_msg_t *) & data;
rpc->type = VLS_RPC_CLONE_AND_SHARE;
msg = (vls_clone_and_share_msg_t *) & rpc->data;
- msg->origin_vls_wrk = wrk->wrk_index;
+ msg->origin_vls_wrk = vls_wrk_index;
msg->origin_vls_index = vls->vls_index;
+ msg->origin_vcl_wrk = wrk->wrk_index;
+ msg->origin_session_index = session_index;
msg->vls_index = dst_vls_index;
+ msg->session_index = dst_session_index;
+
+ wrk->rpc_done = 0;
+ ret = vcl_send_worker_rpc (dst_wrk_index, rpc, sizeof (data));
+
+ VDBG (1,
+ "send session clone of worker (session): %u (%u) -> %u (%u), ret=%d",
+ dst_wrk_index, msg->session_index, msg->origin_vcl_wrk,
+ msg->origin_session_index, ret);
+ while (!ret && !wrk->rpc_done)
+ ;
+}
+
+void
+vls_send_session_cleanup_rpc (vcl_worker_t * wrk,
+ u32 dst_wrk_index, u32 dst_session_index)
+{
+ u8 data[sizeof (u8) + sizeof (vls_sess_cleanup_msg_t)];
+ vls_sess_cleanup_msg_t *msg;
+ vls_rpc_msg_t *rpc;
+ int ret;
+
+ rpc = (vls_rpc_msg_t *) & data;
+ rpc->type = VLS_RPC_SESS_CLEANUP;
+ msg = (vls_sess_cleanup_msg_t *) & rpc->data;
+ msg->origin_vcl_wrk = wrk->wrk_index;
+ msg->session_index = dst_session_index;
wrk->rpc_done = 0;
- vcl_send_worker_rpc (dst_wrk_index, rpc, sizeof (data));
- while (!wrk->rpc_done)
+ ret = vcl_send_worker_rpc (dst_wrk_index, rpc, sizeof (data));
+
+ VDBG (1,
+ "send session cleanup of worker (session): %u (%u) from %u (), ret=%d",
+ dst_wrk_index, msg->session_index, msg->origin_vcl_wrk, ret);
+ while (!ret && !wrk->rpc_done)
;
}
@@ -1470,6 +1658,31 @@ vls_use_eventfd (void)
return vcm->cfg.use_mq_eventfd;
}
+unsigned char
+vls_mt_wrk_supported (void)
+{
+ return vcm->cfg.mt_supported;
+}
+
+int
+vls_use_real_epoll (void)
+{
+ if (vcl_get_worker_index () == ~0)
+ return 0;
+
+ return vcl_worker_get_current ()->vcl_needs_real_epoll;
+}
+
+void
+vls_register_vcl_worker (void)
+{
+ if (vppcom_worker_register () != VPPCOM_OK)
+ {
+ VERR ("failed to register worker");
+ return;
+ }
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vcl/vcl_locked.h b/src/vcl/vcl_locked.h
index 18b341b11ca..7cfb3bdc522 100644
--- a/src/vcl/vcl_locked.h
+++ b/src/vcl/vcl_locked.h
@@ -51,6 +51,9 @@ vcl_session_handle_t vlsh_to_session_index (vls_handle_t vlsh);
vls_handle_t vls_session_index_to_vlsh (uint32_t session_index);
int vls_app_create (char *app_name);
unsigned char vls_use_eventfd (void);
+unsigned char vls_mt_wrk_supported (void);
+int vls_use_real_epoll (void);
+void vls_register_vcl_worker (void);
#endif /* SRC_VCL_VCL_LOCKED_H_ */
diff --git a/src/vcl/vcl_private.c b/src/vcl/vcl_private.c
index 300b82c4f21..1dadb628fb8 100644
--- a/src/vcl/vcl_private.c
+++ b/src/vcl/vcl_private.c
@@ -217,7 +217,9 @@ vcl_worker_alloc_and_init ()
wrk->mqs_epfd = -1;
if (vcm->cfg.use_mq_eventfd)
{
+ wrk->vcl_needs_real_epoll = 1;
wrk->mqs_epfd = epoll_create (1);
+ wrk->vcl_needs_real_epoll = 0;
if (wrk->mqs_epfd < 0)
{
clib_unix_warning ("epoll_create() returned");
diff --git a/src/vcl/vcl_private.h b/src/vcl/vcl_private.h
index 4a25632379c..b873ad994a6 100644
--- a/src/vcl/vcl_private.h
+++ b/src/vcl/vcl_private.h
@@ -218,6 +218,7 @@ typedef struct vppcom_cfg_t_
u8 *vpp_api_socket_name;
u8 *vpp_api_chroot;
u32 tls_engine;
+ u8 mt_supported;
} vppcom_cfg_t;
void vppcom_cfg (vppcom_cfg_t * vcl_cfg);
@@ -307,6 +308,10 @@ typedef struct vcl_worker_
socket_client_main_t bapi_sock_ctx;
memory_client_main_t bapi_shm_ctx;
api_main_t bapi_api_ctx;
+
+ /** vcl needs next epoll_create to go to libc_epoll */
+ u8 vcl_needs_real_epoll;
+ volatile int rpc_done;
} vcl_worker_t;
typedef void (vcl_rpc_fn_t) (void *args);
@@ -659,7 +664,7 @@ vcl_session_vpp_evt_q (vcl_worker_t * wrk, vcl_session_t * s)
void vcl_send_session_worker_update (vcl_worker_t * wrk, vcl_session_t * s,
u32 wrk_index);
-void vcl_send_worker_rpc (u32 dst_wrk_index, void *data, u32 data_len);
+int vcl_send_worker_rpc (u32 dst_wrk_index, void *data, u32 data_len);
/*
* VCL Binary API
diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c
index 41d2f3170aa..d73c73be383 100644
--- a/src/vcl/vppcom.c
+++ b/src/vcl/vppcom.c
@@ -351,13 +351,14 @@ vcl_send_session_worker_update (vcl_worker_t * wrk, vcl_session_t * s,
app_send_ctrl_evt_to_vpp (mq, app_evt);
}
-void
+int
vcl_send_worker_rpc (u32 dst_wrk_index, void *data, u32 data_len)
{
app_session_evt_t _app_evt, *app_evt = &_app_evt;
session_app_wrk_rpc_msg_t *mp;
vcl_worker_t *dst_wrk, *wrk;
svm_msg_q_t *mq;
+ int ret = -1;
if (data_len > sizeof (mp->data))
goto done;
@@ -376,9 +377,11 @@ vcl_send_worker_rpc (u32 dst_wrk_index, void *data, u32 data_len)
mp->wrk_index = dst_wrk->vpp_wrk_index;
clib_memcpy (mp->data, data, data_len);
app_send_ctrl_evt_to_vpp (mq, app_evt);
+ ret = 0;
done:
clib_spinlock_unlock (&vcm->workers_lock);
+ return ret;
}
static u32
@@ -902,7 +905,7 @@ vcl_worker_rpc_handler (vcl_worker_t * wrk, void *data)
if (!vcm->wrk_rpc_fn)
return;
- (vcm->wrk_rpc_fn) (data);
+ (vcm->wrk_rpc_fn) (((session_app_wrk_rpc_msg_t *) data)->data);
}
static int
@@ -962,7 +965,7 @@ vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
vcl_session_app_del_segment_handler (wrk, e->data);
break;
- case SESSION_CTRL_EVT_RPC:
+ case SESSION_CTRL_EVT_APP_WRK_RPC:
vcl_worker_rpc_handler (wrk, e->data);
break;
default:
@@ -2301,7 +2304,7 @@ vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
vcl_session_app_del_segment_handler (wrk, e->data);
break;
- case SESSION_CTRL_EVT_RPC:
+ case SESSION_CTRL_EVT_APP_WRK_RPC:
vcl_worker_rpc_handler (wrk, e->data);
break;
default:
@@ -2920,7 +2923,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
vcl_session_app_del_segment_handler (wrk, e->data);
break;
- case SESSION_CTRL_EVT_RPC:
+ case SESSION_CTRL_EVT_APP_WRK_RPC:
vcl_worker_rpc_handler (wrk, e->data);
break;
default:
} /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
  Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef included_vec_h
#define included_vec_h

#include <vppinfra/clib.h>	/* word, etc */
#include <vppinfra/mem.h>	/* clib_mem_free */
#include <vppinfra/string.h>	/* memcpy, memmove */
#include <vppinfra/vec_bootstrap.h>

/** \file

   CLIB vectors are ubiquitous dynamically resized arrays with by user
   defined "headers".  Many CLIB data structures (e.g. hash, heap,
   pool) are vectors with various different headers.

   The memory layout looks like this:

~~~~~~~~
                    user header (aligned to uword boundary)
                    vector length: number of elements
   user's pointer-> vector element #0
                    vector element #1
                    ...
~~~~~~~~

   The user pointer contains the address of vector element # 0.  Null
   pointer vectors are valid and mean a zero length vector.

   You can reset the length of an allocated vector to zero via the
   vec_reset_length(v) macro, or by setting the vector length field to
   zero (e.g. _vec_len (v) = 0). Vec_reset_length(v) preferred: it
   understands Null pointers.

   Typically, the header is not present.  Headers allow for other
   data structures to be built atop CLIB vectors.

   Users may specify the alignment for data elements via the
   vec_*_aligned macros.

   Vectors elements can be any C type e.g. (int, double, struct bar).
   This is also true for data types built atop vectors (e.g. heap,
   pool, etc.).

   Many macros have _a variants supporting alignment of vector data
   and _h variants supporting non zero length vector headers.
   The _ha variants support both.

   Standard programming error: memorize a pointer to the ith element
   of a vector then expand it. Vectors expand by 3/2, so such code
   may appear to work for a period of time. Memorize vector indices
   which are invariant.
 */

/** \brief Low-level resize allocation function, usually not called directly

    @param v pointer to a vector
    @param length_increment length increment in elements
    @param data_bytes requested size in bytes
    @param header_bytes header size in bytes (may be zero)
    @param data_align alignment (may be zero)
    @return v_prime pointer to resized vector, may or may not equal v
*/
void *vec_resize_allocate_memory (void *v,
				  word length_increment,
				  uword data_bytes,
				  uword header_bytes, uword data_align);

/** \brief Low-level vector resize function, usually not called directly

    @param v pointer to a vector
    @param length_increment length increment in elements
    @param data_bytes requested size in bytes
    @param header_bytes header size in bytes (may be zero)
    @param data_align alignment (may be zero)
    @return v_prime pointer to resized vector, may or may not equal v
*/

always_inline void *
_vec_resize (void *v,
	     word length_increment,
	     uword data_bytes, uword header_bytes, uword data_align)
{
  vec_header_t *vh = _vec_find (v);
  uword new_data_bytes, aligned_header_bytes;

  aligned_header_bytes = vec_header_bytes (header_bytes);

  new_data_bytes = data_bytes + aligned_header_bytes;

  if (PREDICT_TRUE (v != 0))
    {
      void *p = v - aligned_header_bytes;

      /* Vector header must start heap object. */
      ASSERT (clib_mem_is_heap_object (p));

      /* Typically we'll not need to resize. */
      if (new_data_bytes <= clib_mem_size (p))
	{
	  vh->len += length_increment;
	  return v;
	}
    }

  /* Slow path: call helper function. */
  return vec_resize_allocate_memory (v, length_increment, data_bytes,
				     header_bytes,
				     clib_max (sizeof (vec_header_t),
					       data_align));
}

/** \brief Low-level vector resize predicate

    @param v pointer to a vector
    @param length_increment length increment in elements
    @param data_bytes requested size in bytes
    @param header_bytes header size in bytes (may be zero)
    @param data_align alignment (may be zero)
    @return v_prime pointer to resized vector, may or may not equal v
*/

always_inline int
_vec_resize_will_expand (void *v,
			 word length_increment,
			 uword data_bytes, uword header_bytes,
			 uword data_align)
{
  vec_header_t *vh = _vec_find (v);
  uword new_data_bytes, aligned_header_bytes;

  aligned_header_bytes = vec_header_bytes (header_bytes);

  new_data_bytes = data_bytes + aligned_header_bytes;

  if (PREDICT_TRUE (v != 0))
    {
      void *p = v - aligned_header_bytes;

      /* Vector header must start heap object. */
      ASSERT (clib_mem_is_heap_object (p));

      /* Typically we'll not need to resize. */
      if (new_data_bytes <= clib_mem_size (p))
	{
	  vh->len += length_increment;
	  return 0;
	}
    }
  return 1;
}

/** \brief Predicate function, says whether the supplied vector is a clib heap
    object (general version).

    @param v pointer to a vector
    @param header_bytes vector header size in bytes (may be zero)
    @return 0 or 1
*/
uword clib_mem_is_vec_h (void *v, uword header_bytes);


/** \brief Predicate function, says whether the supplied vector is a clib heap
    object

    @param v pointer to a vector
    @return 0 or 1
*/
always_inline uword
clib_mem_is_vec (void *v)
{
  return clib_mem_is_vec_h (v, 0);
}

/* Local variable naming macro (prevents collisions with other macro naming). */
#define _v(var) _vec_##var

/** \brief Resize a vector (general version).
   Add N elements to end of given vector V, return pointer to start of vector.
   Vector will have room for H header bytes and will have user's data aligned
   at alignment A (rounded to next power of 2).

    @param V pointer to a vector
    @param N number of elements to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_resize_ha(V,N,H,A)							\
do {										\
  word _v(n) = (N);								\
  word _v(l) = vec_len (V);							\
  V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A));	\
} while (0)

/** \brief Resize a vector (no header, unspecified alignment)
   Add N elements to end of given vector V, return pointer to start of vector.
   Vector will have room for H header bytes and will have user's data aligned
   at alignment A (rounded to next power of 2).

    @param V pointer to a vector
    @param N number of elements to add
    @return V (value-result macro parameter)
*/
#define vec_resize(V,N)     vec_resize_ha(V,N,0,0)

/** \brief Resize a vector (no header, alignment specified).
   Add N elements to end of given vector V, return pointer to start of vector.
   Vector will have room for H header bytes and will have user's data aligned
   at alignment A (rounded to next power of 2).

    @param V pointer to a vector
    @param N number of elements to add
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_resize_aligned(V,N,A) vec_resize_ha(V,N,0,A)

/** \brief Allocate space for N more elements

    @param V pointer to a vector
    @param N number of elements to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_alloc_ha(V,N,H,A)			\
do {						\
    uword _v(l) = vec_len (V);			\
    vec_resize_ha (V, N, H, A);			\
    _vec_len (V) = _v(l);			\
} while (0)

/** \brief Allocate space for N more elements
    (no header, unspecified alignment)

    @param V pointer to a vector
    @param N number of elements to add
    @return V (value-result macro parameter)
*/
#define vec_alloc(V,N) vec_alloc_ha(V,N,0,0)

/** \brief Allocate space for N more elements (no header, given alignment)
    @param V pointer to a vector
    @param N number of elements to add
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_alloc_aligned(V,N,A) vec_alloc_ha(V,N,0,A)

/** \brief Create new vector of given type and length (general version).
    @param T type of elements in new vector
    @param N number of elements to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V new vector
*/
#define vec_new_ha(T,N,H,A)					\
({								\
  word _v(n) = (N);						\
  _vec_resize ((T *) 0, _v(n), _v(n) * sizeof (T), (H), (A));	\
})

/** \brief Create new vector of given type and length
    (unspecified alignment, no header).

    @param T type of elements in new vector
    @param N number of elements to add
    @return V new vector
*/
#define vec_new(T,N)           vec_new_ha(T,N,0,0)
/** \brief Create new vector of given type and length
    (alignment specified, no header).

    @param T type of elements in new vector
    @param N number of elements to add
    @param A alignment (may be zero)
    @return V new vector
*/
#define vec_new_aligned(T,N,A) vec_new_ha(T,N,0,A)

/** \brief Free vector's memory (general version)

    @param V pointer to a vector
    @param H size of header in bytes
    @return V (value-result parameter, V=0)
*/
#define vec_free_h(V,H)				\
do {						\
  if (V)					\
    {						\
      clib_mem_free (vec_header ((V), (H)));	\
      V = 0;					\
    }						\
} while (0)

/** \brief Free vector's memory (no header).
    @param V pointer to a vector
    @return V (value-result parameter, V=0)
*/
#define vec_free(V) vec_free_h(V,0)

/**\brief Free vector user header (syntactic sugar)
   @param h vector header
   @void
*/
#define vec_free_header(h) clib_mem_free (h)

/** \brief Return copy of vector (general version).

    @param V pointer to a vector
    @param H size of header in bytes
    @param A alignment (may be zero)

    @return Vdup copy of vector
*/

#define vec_dup_ha(V,H,A)				\
({							\
  __typeof__ ((V)[0]) * _v(v) = 0;			\
  uword _v(l) = vec_len (V);				\
  if (_v(l) > 0)					\
    {							\
      vec_resize_ha (_v(v), _v(l), (H), (A));		\
      clib_memcpy (_v(v), (V), _v(l) * sizeof ((V)[0]));\
    }							\
  _v(v);						\
})

/** \brief Return copy of vector (no header, no alignment)

    @param V pointer to a vector
    @return Vdup copy of vector
*/
#define vec_dup(V) vec_dup_ha(V,0,0)

/** \brief Return copy of vector (no header, alignment specified).

    @param V pointer to a vector
    @param A alignment (may be zero)

    @return Vdup copy of vector
*/
#define vec_dup_aligned(V,A) vec_dup_ha(V,0,A)

/** \brief Copy a vector, memcpy wrapper. Assumes sizeof(SRC[0]) ==
    sizeof(DST[0])

    @param DST destination
    @param SRC source
*/
#define vec_copy(DST,SRC) clib_memcpy (DST, SRC, vec_len (DST) * \
				       sizeof ((DST)[0]))

/** \brief Clone a vector. Make a new vector with the
    same size as a given vector but possibly with a different type.

    @param NEW_V pointer to new vector
    @param OLD_V pointer to old vector
*/
#define vec_clone(NEW_V,OLD_V)							\
do {										\
  (NEW_V) = 0;									\
  (NEW_V) = _vec_resize ((NEW_V), vec_len (OLD_V),				\
			 vec_len (OLD_V) * sizeof ((NEW_V)[0]), (0), (0));	\
} while (0)

/** \brief Make sure vector is long enough for given index (general version).

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_validate_ha(V,I,H,A)					\
do {									\
  word _v(i) = (I);							\
  word _v(l) = vec_len (V);						\
  if (_v(i) >= _v(l))							\
    {									\
      vec_resize_ha ((V), 1 + (_v(i) - _v(l)), (H), (A));		\
      /* Must zero new space since user may have previously		\
	 used e.g. _vec_len (v) -= 10 */				\
      memset ((V) + _v(l), 0, (1 + (_v(i) - _v(l))) * sizeof ((V)[0]));	\
    }									\
} while (0)

/** \brief Make sure vector is long enough for given index
    (no header, unspecified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @return V (value-result macro parameter)
*/
#define vec_validate(V,I)           vec_validate_ha(V,I,0,0)

/** \brief Make sure vector is long enough for given index
    (no header, specified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_validate_aligned(V,I,A) vec_validate_ha(V,I,0,A)

/** \brief Make sure vector is long enough for given index
    and initialize empty space (general version)

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @param INIT initial value (can be a complex expression!)
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_validate_init_empty_ha(V,I,INIT,H,A)		\
do {								\
  word _v(i) = (I);						\
  word _v(l) = vec_len (V);					\
  if (_v(i) >= _v(l))						\
    {								\
      vec_resize_ha ((V), 1 + (_v(i) - _v(l)), (H), (A));	\
      while (_v(l) <= _v(i))					\
	{							\
	  (V)[_v(l)] = (INIT);					\
	  _v(l)++;						\
	}							\
    }								\
} while (0)

/** \brief Make sure vector is long enough for given index
    and initialize empty space (no header, unspecified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @param INIT initial value (can be a complex expression!)
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_validate_init_empty(V,I,INIT) \
  vec_validate_init_empty_ha(V,I,INIT,0,0)

/** \brief Make sure vector is long enough for given index
    and initialize empty space (no header, alignment alignment)

    @param V (possibly NULL) pointer to a vector.
    @param I vector index which will be valid upon return
    @param INIT initial value (can be a complex expression!)
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_validate_init_empty_aligned(V,I,INIT,A) \
  vec_validate_init_empty_ha(V,I,INIT,0,A)

/** \brief Add 1 element to end of vector (general version).

    @param V pointer to a vector
    @param E element to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_add1_ha(V,E,H,A)						\
do {									\
  word _v(l) = vec_len (V);						\
  V = _vec_resize ((V), 1, (_v(l) + 1) * sizeof ((V)[0]), (H), (A));	\
  (V)[_v(l)] = (E);							\
} while (0)

/** \brief Add 1 element to end of vector (unspecified alignment).

    @param V pointer to a vector
    @param E element to add
    @return V (value-result macro parameter)
*/
#define vec_add1(V,E)           vec_add1_ha(V,E,0,0)

/** \brief Add 1 element to end of vector (alignment specified).

    @param V pointer to a vector
    @param E element to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_add1_aligned(V,E,A) vec_add1_ha(V,E,0,A)

/** \brief Add N elements to end of vector V,
    return pointer to new elements in P. (general version)

    @param V pointer to a vector
    @param P pointer to new vector element(s)
    @param N number of elements to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V and P (value-result macro parameters)
*/
#define vec_add2_ha(V,P,N,H,A)							\
do {										\
  word _v(n) = (N);								\
  word _v(l) = vec_len (V);							\
  V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A));	\
  P = (V) + _v(l);								\
} while (0)

/** \brief Add N elements to end of vector V,
    return pointer to new elements in P. (no header, unspecified alignment)

    @param V pointer to a vector
    @param P pointer to new vector element(s)
    @param N number of elements to add
    @return V and P (value-result macro parameters)
*/

#define vec_add2(V,P,N)           vec_add2_ha(V,P,N,0,0)

/** \brief Add N elements to end of vector V,
    return pointer to new elements in P. (no header, alignment specified)

    @param V pointer to a vector
    @param P pointer to new vector element(s)
    @param N number of elements to add
    @param A alignment (may be zero)
    @return V and P (value-result macro parameters)
*/

#define vec_add2_aligned(V,P,N,A) vec_add2_ha(V,P,N,0,A)

/** \brief Add N elements to end of vector V (general version)

    @param V pointer to a vector
    @param E pointer to element(s) to add
    @param N number of elements to add
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_add_ha(V,E,N,H,A)							\
do {										\
  word _v(n) = (N);								\
  word _v(l) = vec_len (V);							\
  V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A));	\
  clib_memcpy ((V) + _v(l), (E), _v(n) * sizeof ((V)[0]));			\
} while (0)

/** \brief Add N elements to end of vector V (no header, unspecified alignment)

    @param V pointer to a vector
    @param E pointer to element(s) to add
    @param N number of elements to add
    @return V (value-result macro parameter)
*/
#define vec_add(V,E,N)           vec_add_ha(V,E,N,0,0)

/** \brief Add N elements to end of vector V (no header, specified alignment)

    @param V pointer to a vector
    @param E pointer to element(s) to add
    @param N number of elements to add
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_add_aligned(V,E,N,A) vec_add_ha(V,E,N,0,A)

/** \brief Returns last element of a vector and decrements its length

    @param V pointer to a vector
    @return E element removed from the end of the vector
*/
#define vec_pop(V)				\
({						\
  uword _v(l) = vec_len (V);			\
  ASSERT (_v(l) > 0);				\
  _v(l) -= 1;					\
  _vec_len (V) = _v (l);			\
  (V)[_v(l)];					\
})

/** \brief Set E to the last element of a vector, decrement vector length
    @param V pointer to a vector
    @param E pointer to the last vector element
    @return E element removed from the end of the vector
    (value-result macro parameter
*/

#define vec_pop2(V,E)				\
({						\
  uword _v(l) = vec_len (V);			\
  if (_v(l) > 0) (E) = vec_pop (V);		\
  _v(l) > 0;					\
})

/** \brief Insert N vector elements starting at element M,
    initialize new elements (general version).

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @param INIT initial value (can be a complex expression!)
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_insert_init_empty_ha(V,N,M,INIT,H,A)	\
do {							\
  word _v(l) = vec_len (V);				\
  word _v(n) = (N);					\
  word _v(m) = (M);					\
  V = _vec_resize ((V),					\
		   _v(n),				\
		   (_v(l) + _v(n))*sizeof((V)[0]),	\
		   (H), (A));				\
  ASSERT (_v(m) <= _v(l));				\
  memmove ((V) + _v(m) + _v(n),				\
	   (V) + _v(m),					\
	   (_v(l) - _v(m)) * sizeof ((V)[0]));		\
  memset  ((V) + _v(m), INIT, _v(n) * sizeof ((V)[0]));	\
} while (0)

/** \brief Insert N vector elements starting at element M,
    initialize new elements to zero (general version)

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_insert_ha(V,N,M,H,A)    vec_insert_init_empty_ha(V,N,M,0,H,A)

/** \brief Insert N vector elements starting at element M,
    initialize new elements to zero (no header, unspecified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @return V (value-result macro parameter)
*/
#define vec_insert(V,N,M)           vec_insert_ha(V,N,M,0,0)

/** \brief Insert N vector elements starting at element M,
    initialize new elements to zero (no header, alignment specified)

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_insert_aligned(V,N,M,A) vec_insert_ha(V,N,M,0,A)

/** \brief Insert N vector elements starting at element M,
    initialize new elements (no header, unspecified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @param INIT initial value (can be a complex expression!)
    @return V (value-result macro parameter)
*/

#define vec_insert_init_empty(V,N,M,INIT) \
  vec_insert_init_empty_ha(V,N,M,INIT,0,0)
/* Resize vector by N elements starting from element M, initialize new elements to INIT (alignment specified, no header). */

/** \brief Insert N vector elements starting at element M,
    initialize new elements (no header, specified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param N number of elements to insert
    @param M insertion point
    @param INIT initial value (can be a complex expression!)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_insert_init_empty_aligned(V,N,M,INIT,A) \
  vec_insert_init_empty_ha(V,N,M,INIT,0,A)

/** \brief Insert N vector elements starting at element M,
    insert given elements (general version)

    @param V (possibly NULL) pointer to a vector.
    @param E element(s) to insert
    @param N number of elements to insert
    @param M insertion point
    @param H header size in bytes (may be zero)
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/

#define vec_insert_elts_ha(V,E,N,M,H,A)			\
do {							\
  word _v(l) = vec_len (V);				\
  word _v(n) = (N);					\
  word _v(m) = (M);					\
  V = _vec_resize ((V),					\
		   _v(n),				\
		   (_v(l) + _v(n))*sizeof((V)[0]),	\
		   (H), (A));				\
  ASSERT (_v(m) <= _v(l));				\
  memmove ((V) + _v(m) + _v(n),				\
	   (V) + _v(m),					\
	   (_v(l) - _v(m)) * sizeof ((V)[0]));		\
  clib_memcpy ((V) + _v(m), (E),			\
	       _v(n) * sizeof ((V)[0]));		\
} while (0)

/** \brief Insert N vector elements starting at element M,
    insert given elements (no header, unspecified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param E element(s) to insert
    @param N number of elements to insert
    @param M insertion point
    @return V (value-result macro parameter)
*/
#define vec_insert_elts(V,E,N,M)           vec_insert_elts_ha(V,E,N,M,0,0)

/** \brief Insert N vector elements starting at element M,
    insert given elements (no header, specified alignment)

    @param V (possibly NULL) pointer to a vector.
    @param E element(s) to insert
    @param N number of elements to insert
    @param M insertion point
    @param A alignment (may be zero)
    @return V (value-result macro parameter)
*/
#define vec_insert_elts_aligned(V,E,N,M,A) vec_insert_elts_ha(V,E,N,M,0,A)

/** \brief Delete N elements starting at element M

    @param V pointer to a vector
    @param N number of elements to delete
    @param M first element to delete
    @return V (value-result macro parameter)
*/
#define vec_delete(V,N,M)					\
do {								\
  word _v(l) = vec_len (V);					\
  word _v(n) = (N);						\
  word _v(m) = (M);						\
  /* Copy over deleted elements. */				\
  if (_v(l) - _v(n) - _v(m) > 0)				\
    memmove ((V) + _v(m), (V) + _v(m) + _v(n),			\
	     (_v(l) - _v(n) - _v(m)) * sizeof ((V)[0]));	\
  /* Zero empty space at end (for future re-allocation). */	\
  if (_v(n) > 0)						\
    memset ((V) + _v(l) - _v(n), 0, _v(n) * sizeof ((V)[0]));	\
  _vec_len (V) -= _v(n);					\
} while (0)

/** \brief Delete the element at index I

    @param V pointer to a vector
    @param I index to delete
*/
#define vec_del1(v,i)				\
do {						\
  uword _vec_del_l = _vec_len (v) - 1;		\
  uword _vec_del_i = (i);			\
  if (_vec_del_i < _vec_del_l)			\
    (v)[_vec_del_i] = (v)[_vec_del_l];		\
  _vec_len (v) = _vec_del_l;			\
} while (0)

/** \brief Append v2 after v1. Result in v1.
    @param V1 target vector
    @param V2 vector to append
*/

#define vec_append(v1,v2)						\
do {									\
  uword _v(l1) = vec_len (v1);						\
  uword _v(l2) = vec_len (v2);						\
									\
  v1 = _vec_resize ((v1), _v(l2),					\
		    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, 0);	\
  clib_memcpy ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));		\
} while (0)

/** \brief Append v2 after v1. Result in v1. Specified alignment.
    @param V1 target vector
    @param V2 vector to append
    @param align required alignment
*/

#define vec_append_aligned(v1,v2,align)					\
do {									\
  uword _v(l1) = vec_len (v1);						\
  uword _v(l2) = vec_len (v2);						\
									\
  v1 = _vec_resize ((v1), _v(l2),					\
		    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, align);	\
  clib_memcpy ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));		\
} while (0)

/** \brief Prepend v2 before v1. Result in v1.
    @param V1 target vector
    @param V2 vector to prepend
*/

#define vec_prepend(v1,v2)                                              \
do {                                                                    \
  uword _v(l1) = vec_len (v1);                                          \
  uword _v(l2) = vec_len (v2);                                          \
                                                                        \
  v1 = _vec_resize ((v1), _v(l2),                                       \
		    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, 0);	\
  memmove ((v1) + _v(l2), (v1), _v(l1) * sizeof ((v1)[0]));             \
  clib_memcpy ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
} while (0)

/** \brief Prepend v2 before v1. Result in v1. Specified alignment
    @param V1 target vector
    @param V2 vector to prepend
    @param align required alignment
*/

#define vec_prepend_aligned(v1,v2,align)                                \
do {                                                                    \
  uword _v(l1) = vec_len (v1);                                          \
  uword _v(l2) = vec_len (v2);                                          \
                                                                        \
  v1 = _vec_resize ((v1), _v(l2),                                       \
		    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, align);	\
  memmove ((v1) + _v(l2), (v1), _v(l1) * sizeof ((v1)[0]));             \
  clib_memcpy ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
} while (0)


/** \brief Zero all vector elements. Null-pointer tolerant.
    @param var Vector to zero
*/
#define vec_zero(var)						\
do {								\
  if (var)							\
    memset ((var), 0, vec_len (var) * sizeof ((var)[0]));	\
} while (0)

/** \brief Set all vector elements to given value. Null-pointer tolerant.
    @param v vector to set
    @param val value for each vector element
*/
#define vec_set(v,val)				\
do {						\
  word _v(i);					\
  __typeof__ ((v)[0]) _val = (val);		\
  for (_v(i) = 0; _v(i) < vec_len (v); _v(i)++)	\
    (v)[_v(i)] = _val;				\
} while (0)

#ifdef CLIB_UNIX
#include <stdlib.h>		/* for qsort */
#endif

/** \brief Compare two vectors, not NULL-pointer tolerant

    @param v1 Pointer to a vector
    @param v2 Pointer to a vector
    @return 1 if equal, 0 if unequal
*/
#define vec_is_equal(v1,v2) \
  (vec_len (v1) == vec_len (v2) && ! memcmp ((v1), (v2), vec_len (v1) * sizeof ((v1)[0])))

/** \brief Compare two vectors (only applicable to vectors of signed numbers).
   Used in qsort compare functions.

    @param v1 Pointer to a vector
    @param v2 Pointer to a vector
    @return -1, 0, +1
*/
#define vec_cmp(v1,v2)					\
({							\
  word _v(i), _v(cmp), _v(l);				\
  _v(l) = clib_min (vec_len (v1), vec_len (v2));	\
  _v(cmp) = 0;						\
  for (_v(i) = 0; _v(i) < _v(l); _v(i)++) {		\
    _v(cmp) = (v1)[_v(i)] - (v2)[_v(i)];		\
    if (_v(cmp))					\
      break;						\
  }							\
  if (_v(cmp) == 0 && _v(l) > 0)			\
    _v(cmp) = vec_len(v1) - vec_len(v2);		\
  (_v(cmp) < 0 ? -1 : (_v(cmp) > 0 ? +1 : 0));		\
})

/** \brief Search a vector for the index of the entry that matches.

    @param v1 Pointer to a vector
    @param v2 Entry to match
    @return index of match or ~0
*/
#define vec_search(v,E)					\
({							\
  word _v(i) = 0;					\
  while (_v(i) < vec_len(v))				\
  {							\
    if (v[_v(i)] == E)				        \
      break;						\
    _v(i)++;						\
  }							\
  if (_v(i) == vec_len(v))				\
    _v(i) = ~0;					        \
  _v(i);						\
})

/** \brief Sort a vector using the supplied element comparison function

    @param vec vector to sort
    @param f comparison function
*/
#define vec_sort_with_function(vec,f)				\
do {								\
  qsort (vec, vec_len (vec), sizeof (vec[0]), (void *) (f));	\
} while (0)

/** \brief Make a vector containing a NULL terminated c-string.

    @param V (possibly NULL) pointer to a vector.
    @param S pointer to string buffer.
    @param L string length (NOT including the terminating NULL; a la strlen())
*/
#define vec_validate_init_c_string(V, S, L)     \
  do {                                          \
    vec_reset_length (V);                       \
    vec_validate ((V), (L));                    \
    if ((S) && (L))                             \
        clib_memcpy ((V), (S), (L));            \
    (V)[(L)] = 0;                               \
  } while (0)


/** \brief Test whether a vector is a NULL terminated c-string.

    @param V (possibly NULL) pointer to a vector.
    @return BOOLEAN indicating if the vector c-string is null terminated.
*/
#define vec_c_string_is_terminated(V)                   \
  (((V) != 0) && (vec_len (V) != 0) && ((V)[vec_len ((V)) - 1] == 0))

/** \brief (If necessary) NULL terminate a vector containing a c-string.

    @param V (possibly NULL) pointer to a vector.
    @return V (value-result macro parameter)
*/
#define vec_terminate_c_string(V)               \
  do {                                          \
    u32 vl = vec_len ((V));                     \
    if (!vec_c_string_is_terminated(V))         \
      {                                         \
        vec_validate ((V), vl);                 \
        (V)[vl] = 0;                            \
      }                                         \
  } while (0)

#endif /* included_vec_h */


/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */