/*
* Copyright (c) 2020 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.
*/
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vppinfra/error.h>
#include <vnet/ip/ip_format_fns.h>
#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet_format_fns.h>
#include <l2tp/l2tp.h>
/* define message IDs */
#include <l2tp/l2tp.api_enum.h>
#include <l2tp/l2tp.api_types.h>
#include <vlibmemory/vlib.api_types.h>
typedef struct
{
/* API message ID base */
u16 msg_id_base;
u32 ping_id;
vat_main_t *vat_main;
} l2tp_test_main_t;
l2tp_test_main_t l2tp_test_main;
#define __plugin_msg_base l2tp_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>
/* Macro to finish up custom dump fns */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define FINISH \
vec_add1 (s, 0); \
vl_print (handle, (char *)s); \
vec_free (s); \
return handle;
static void vl_api_l2tpv3_create_tunnel_reply_t_handler
(vl_api_l2tpv3_create_tunnel_reply_t * mp)
{
vat_main_t *vam = &vat_main;
i32 retval = ntohl (mp->retval);
if (vam->async_mode)
{
vam->async_errors += (retval < 0);
}
else
{
vam->retval = retval;
vam->sw_if_index = ntohl (mp->sw_if_index);
vam->result_ready = 1;
}
}
static int
api_l2tpv3_create_tunnel (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
ip6_address_t client_address, our_address;
int client_address_set = 0;
int our_address_set = 0;
u32 local_session_id = 0;
u32 remote_session_id = 0;
u64 local_cookie = 0;
u64 remote_cookie = 0;
u8 l2_sublayer_present = 0;
vl_api_l2tpv3_create_tunnel_t *mp;
int ret;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "client_address %U", unformat_ip6_address,
&client_address))
client_address_set = 1;
else if (unformat (i, "our_address %U", unformat_ip6_address,
&our_address))
our_address_set = 1;
else if (unformat (i, "local_session_id %d", &local_session_id))
;
else if (unformat (i, "remote_session_id %d", &remote_session_id))
;
else if (unformat (i, "local_cookie %lld", &local_cookie))
;
else if (unformat (i, "remote_cookie %lld", &remote_cookie))
;
else if (unformat (i, "l2-sublayer-present"))
l2_sublayer_present = 1;
else
break;
}
if (client_address_set == 0)
{
errmsg ("client_address required");
return -99;
}
if (our_address_set == 0)
{
errmsg ("our_address required");
return -99;
}
M (L2TPV3_CREATE_TUNNEL, mp);
clib_memcpy (mp->client_address.un.ip6, client_address.as_u8,
sizeof (ip6_address_t));
clib_memcpy (mp->our_address.un.ip6, our_address.as_u8,
sizeof (ip6_address_t));
mp->local_session_id = ntohl (local_session_id);
mp->remote_session_id = ntohl (remote_session_id);
mp->local_cookie = clib_host_to_net_u64 (local_cookie);
mp->remote_cookie = clib_host_to_net_u64 (remote_cookie);
mp->l2_sublayer_present = l2_sublayer_present;
S (mp);
W (ret);
return ret;
}
static int
api_l2tpv3_set_tunnel_cookies (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
u32 sw_if_index;
u8 sw_if_index_set = 0;
u64 new_local_cookie = 0;
u64 new_remote_cookie = 0;
vl_api_l2tpv3_set_tunnel_cookies_t *mp;
int ret;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "sw_if_index %d", &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "new_local_cookie %lld", &new_local_cookie))
;
else if (unformat (i, "new_remote_cookie %lld", &new_remote_cookie))
;
else
break;
}
if (sw_if_index_set == 0)
{
errmsg ("missing interface name or sw_if_index");
return -99;
}
M (L2TPV3_SET_TUNNEL_COOKIES, mp);
mp->sw_if_index = ntohl (sw_if_index);
mp->new_local_cookie = clib_host_to_net_u64 (new_local_cookie);
mp->new_remote_cookie = clib_host_to_net_u64 (new_remote_cookie);
S (mp);
W (ret);
return ret;
}
static int
api_l2tpv3_interface_enable_disable (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
vl_api_l2tpv3_interface_enable_disable_t *mp;
u32 sw_if_index;
u8 sw_if_index_set = 0;
u8 enable_disable = 1;
int ret;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "sw_if_index %d", &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "enable"))
enable_disable = 1;
else if (unformat (i, "disable"))
enable_disable = 0;
else
break;
}
if (sw_if_index_set == 0)
{
errmsg ("missing interface name or sw_if_index");
return -99;
}
M (L2TPV3_INTERFACE_ENABLE_DISABLE, mp);
mp->sw_if_index = ntohl (sw_if_index);
mp->enable_disable = enable_disable;
S (mp);
W (ret);
return ret;
}
static int
api_l2tpv3_set_lookup_key (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
vl_api_l2tpv3_set_lookup_key_t *mp;
u8 key = ~0;
int ret;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "lookup_v6_src"))
key = L2T_LOOKUP_SRC_ADDRESS;
else if (unformat (i, "lookup_v6_dst"))
key = L2T_LOOKUP_DST_ADDRESS;
else if (unformat (i, "lookup_session_id"))
key = L2T_LOOKUP_SESSION_ID;
else
break;
}
if (key == (u8) ~ 0)
{
errmsg ("l2tp session lookup key unset");
return -99;
}
M (L2TPV3_SET_LOOKUP_KEY, mp);
mp->key = key;
S (mp);
W (ret);
return ret;
}
static void vl_api_sw_if_l2tpv3_tunnel_details_t_handler
(vl_api_sw_if_l2tpv3_tunnel_details_t * mp)
{
vat_main_t *vam = &vat_main;
print (vam->ofp, "* %U (our) %U (client) (sw_if_index %d)",
format_ip6_address, mp->our_address,
format_ip6_address, mp->client_address,
clib_net_to_host_u32 (mp->sw_if_index));
print (vam->ofp,
" local cookies %016llx %016llx remote cookie %016llx",
clib_net_to_host_u64 (mp->local_cookie[0]),
clib_net_to_host_u64 (mp->local_cookie[1]),
clib_net_to_host_u64 (mp->remote_cookie));
print (vam->ofp, " local session-id %d remote session-id %d",
clib_net_to_host_u32 (mp->local_session_id),
clib_net_to_host_u32 (mp->remote_session_id));
print (vam->ofp, " l2 specific sublayer %s\n",
mp->l2_sublayer_present ? "preset" : "absent");
}
static int
api_sw_if_l2tpv3_tunnel_dump (vat_main_t * vam)
{
vl_api_sw_if_l2tpv3_tunnel_dump_t *mp;
vl_api_control_ping_t *mp_ping;
int ret;
/* Get list of l2tpv3-tunnel interfaces */
M (SW_IF_L2TPV3_TUNNEL_DUMP, mp);
S (mp);
/* Use a control ping for synchronization */
if (!l2tp_test_main.ping_id)
l2tp_test_main.ping_id =
vl_msg_api_get_msg_index ((u8 *) (.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* 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 */#include <vppinfra/time.h>
#include <vppinfra/cache.h>
#include <vppinfra/error.h>
#include <vppinfra/tw_timer_2t_1w_2048sl.h>
#include <vppinfra/tw_timer_16t_2w_512sl.h>
#include <vppinfra/tw_timer_4t_3w_256sl.h>
#include <vppinfra/tw_timer_1t_3w_1024sl_ov.h>
typedef struct
{
/** Handle returned from tw_start_timer */
u32 stop_timer_handle;
/** Test item should expire at this clock tick */
u64 expected_to_expire;
} tw_timer_test_elt_t;
typedef struct
{
/** Pool of test objects */
tw_timer_test_elt_t *test_elts;
/** The single-wheel */
tw_timer_wheel_2t_1w_2048sl_t single_wheel;
/** The double-wheel */
tw_timer_wheel_16t_2w_512sl_t double_wheel;
/* The triple wheel */
tw_timer_wheel_4t_3w_256sl_t triple_wheel;
/* The triple wheel with overflow vector */
tw_timer_wheel_1t_3w_1024sl_ov_t triple_ov_wheel;
/** random number seed */
u64 seed;
/** number of timers */
u32 ntimers;
/** number of "churn" iterations */
u32 niter;
/** number of clock ticks per churn iteration */
u32 ticks_per_iter;
/** cpu timer */
clib_time_t clib_time;
} tw_timer_test_main_t;
tw_timer_test_main_t tw_timer_test_main;
static void
run_single_wheel (tw_timer_wheel_2t_1w_2048sl_t * tw, u32 n_ticks)
{
u32 i;
f64 now = tw->last_run_time + 1.01;
for (i = 0; i < n_ticks; i++)
{
tw_timer_expire_timers_2t_1w_2048sl (tw, now);
now += 1.01;
}
}
static void
run_double_wheel (tw_timer_wheel_16t_2w_512sl_t * tw, u32 n_ticks)
{
u32 i;
f64 now = tw->last_run_time + 1.01;
for (i = 0; i < n_ticks; i++)
{
tw_timer_expire_timers_16t_2w_512sl (tw, now);
now += 1.01;
}
}
static void
run_triple_wheel (tw_timer_wheel_4t_3w_256sl_t * tw, u32 n_ticks)
{
u32 i;
f64 now = tw->last_run_time + 1.01;
for (i = 0; i < n_ticks; i++)
{
tw_timer_expire_timers_4t_3w_256sl (tw, now);
now += 1.01;
}
}
static void
run_triple_ov_wheel (tw_timer_wheel_1t_3w_1024sl_ov_t * tw, u32 n_ticks)
{
u32 i;
f64 now = tw->last_run_time + 1.01;
for (i = 0; i < n_ticks; i++)
{
tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
now += 1.01;
}
}
static void
expired_timer_single_callback (u32 * expired_timers)
{
int i;
u32 pool_index, timer_id;
tw_timer_test_elt_t *e;
tw_timer_test_main_t *tm = &tw_timer_test_main;
for (i = 0; i < vec_len (expired_timers); i++)
{
pool_index = expired_timers[i] & 0x7FFFFFFF;
timer_id = expired_timers[i] >> 31;
ASSERT (timer_id == 1);
e = pool_elt_at_index (tm->test_elts, pool_index);
if (e->expected_to_expire != tm->single_wheel.current_tick)
{
fformat (stdout, "[%d] expired at %lld not %lld\n",
e - tm->test_elts, tm->single_wheel.current_tick,
e->expected_to_expire);
}
pool_put (tm->test_elts, e);
}
}
static void
expired_timer_double_callback (u32 * expired_timers)
{
int i;
u32 pool_index, timer_id;
tw_timer_test_elt_t *e;
tw_timer_test_main_t *tm = &tw_timer_test_main;
for (i = 0; i < vec_len (expired_timers); i++)
{
pool_index = expired_timers[i] & 0x0FFFFFFF;
timer_id = expired_timers[i] >> 28;
ASSERT (timer_id == 14);
e = pool_elt_at_index (tm->test_elts, pool_index);
if (e->expected_to_expire != tm->double_wheel.current_tick)
{
fformat (stdout, "[%d] expired at %lld not %lld\n",
e - tm->test_elts, tm->double_wheel.current_tick,
e->expected_to_expire);
}
pool_put (tm->test_elts, e);
}
}
static void
expired_timer_triple_callback (u32 * expired_timers)
{
int i;
u32 pool_index, timer_id;
tw_timer_test_elt_t *e;
tw_timer_test_main_t *tm = &tw_timer_test_main;
for (i = 0; i < vec_len (expired_timers); i++)
{
pool_index = expired_timers[i] & 0x3FFFFFFF;
timer_id = expired_timers[i] >> 30;
ASSERT (timer_id == 3);
e = pool_elt_at_index (tm->test_elts, pool_index);
if (e->expected_to_expire != tm->triple_wheel.current_tick)
{
fformat (stdout, "[%d] expired at %lld not %lld\n",
e - tm->test_elts, tm->triple_wheel.current_tick,
e->expected_to_expire);
}
pool_put (tm->test_elts, e);
}
}
static void
expired_timer_triple_ov_callback (u32 * expired_timers)
{
int i;
u32 pool_index;
tw_timer_test_elt_t *e;
tw_timer_test_main_t *tm = &tw_timer_test_main;
for (i = 0; i < vec_len (expired_timers); i++)
{
pool_index = expired_timers[i];
e = pool_elt_at_index (tm->test_elts, pool_index);
if (e->expected_to_expire != tm->triple_ov_wheel.current_tick)
{
fformat (stdout, "[%d] expired at %lld not %lld\n",
e - tm->test_elts, tm->triple_ov_wheel.current_tick,
e->expected_to_expire);
}
pool_put (tm->test_elts, e);
}
}
static clib_error_t *
test2_single (tw_timer_test_main_t * tm)
{
u32 i, j;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset;
u64 expiration_time;
u32 max_expiration_time = 0;
u32 *deleted_indices = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,
expired_timer_single_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 757;
run_single_wheel (&tm->single_wheel, initial_wheel_offset);
fformat (stdout, "initial wheel time %d, fast index %d\n",
tm->single_wheel.current_tick,
tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
initial_wheel_offset = tm->single_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & (2047);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts,
1 /* timer id */ ,
expiration_time);
}
adds += i;
for (i = 0; i < tm->niter; i++)
{
run_single_wheel (&tm->single_wheel, tm->ticks_per_iter);
j = 0;
vec_reset_length (deleted_indices);
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, e->stop_timer_handle);
vec_add1 (deleted_indices, e - tm->test_elts);
if (++j >= tm->ntimers / 4)
goto del_and_re_add;
}));
/* *INDENT-ON* */
del_and_re_add:
for (j = 0; j < vec_len (deleted_indices); j++)
{
pool_put_index (tm->test_elts, deleted_indices[j]);
}
deletes += j;
for (j = 0; j < tm->ntimers / 4; j++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & (2047);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire =
expiration_time + tm->single_wheel.current_tick;
e->stop_timer_handle = tw_timer_start_2t_1w_2048sl
(&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ ,
expiration_time);
}
adds += j;
}
vec_free (deleted_indices);
run_single_wheel (&tm->single_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->single_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->single_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel);
return 0;
}
static clib_error_t *
test2_double (tw_timer_test_main_t * tm)
{
u32 i, j;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 *deleted_indices = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
expired_timer_double_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 7577;
run_double_wheel (&tm->double_wheel, initial_wheel_offset);
fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST],
tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
initial_wheel_offset = tm->double_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
14 /* timer id */ ,
expiration_time);
}
adds += i;
for (i = 0; i < tm->niter; i++)
{
run_double_wheel (&tm->double_wheel, tm->ticks_per_iter);
j = 0;
vec_reset_length (deleted_indices);
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
tw_timer_stop_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle);
vec_add1 (deleted_indices, e - tm->test_elts);
if (++j >= tm->ntimers / 4)
goto del_and_re_add;
}));
/* *INDENT-ON* */
del_and_re_add:
for (j = 0; j < vec_len (deleted_indices); j++)
pool_put_index (tm->test_elts, deleted_indices[j]);
deletes += j;
for (j = 0; j < tm->ntimers / 4; j++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time +
tm->double_wheel.current_tick;
e->stop_timer_handle = tw_timer_start_16t_2w_512sl
(&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ ,
expiration_time);
}
adds += j;
}
vec_free (deleted_indices);
run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->double_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->double_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
return 0;
}
static u32
get_expiration_time (tw_timer_test_main_t * tm)
{
u32 expiration_time;
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
return expiration_time;
}
static clib_error_t *
test2_double_updates (tw_timer_test_main_t * tm)
{
u32 i, j;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset;
u32 expiration_time;
u32 max_expiration_time = 0, updates = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
expired_timer_double_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 7577;
run_double_wheel (&tm->double_wheel, initial_wheel_offset);
fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST],
tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
initial_wheel_offset = tm->double_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
expiration_time = get_expiration_time (tm);
max_expiration_time = clib_max (expiration_time, max_expiration_time);
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel,
e - tm->test_elts,
14 /* timer id */ ,
expiration_time);
}
for (i = 0; i < tm->niter; i++)
{
run_double_wheel (&tm->double_wheel, tm->ticks_per_iter);
j = 0;
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
expiration_time = get_expiration_time (tm);
max_expiration_time = clib_max (expiration_time, max_expiration_time);
e->expected_to_expire = expiration_time
+ tm->double_wheel.current_tick;
tw_timer_update_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle,
expiration_time);
if (++j >= tm->ntimers / 4)
goto done;
}));
/* *INDENT-ON* */
done:
updates += j;
}
run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d updates, %d ticks\n", updates,
tm->double_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) updates + (f64) tm->double_wheel.current_tick) / (after -
before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
return 0;
}
static clib_error_t *
test2_triple (tw_timer_test_main_t * tm)
{
u32 i, j;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset = 0;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 *deleted_indices = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel,
expired_timer_triple_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 75700;
run_triple_wheel (&tm->triple_wheel, initial_wheel_offset);
fformat (stdout,
"initial wheel time %d, fi %d si %d gi %d\n",
tm->triple_wheel.current_tick,
tm->triple_wheel.current_index[TW_TIMER_RING_FAST],
tm->triple_wheel.current_index[TW_TIMER_RING_SLOW],
tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]);
initial_wheel_offset = tm->triple_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts,
3 /* timer id */ ,
expiration_time);
}
adds += i;
for (i = 0; i < tm->niter; i++)
{
run_triple_wheel (&tm->triple_wheel, tm->ticks_per_iter);
j = 0;
vec_reset_length (deleted_indices);
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
tw_timer_stop_4t_3w_256sl (&tm->triple_wheel, e->stop_timer_handle);
vec_add1 (deleted_indices, e - tm->test_elts);
if (++j >= tm->ntimers / 4)
goto del_and_re_add;
}));
/* *INDENT-ON* */
del_and_re_add:
for (j = 0; j < vec_len (deleted_indices); j++)
pool_put_index (tm->test_elts, deleted_indices[j]);
deletes += j;
for (j = 0; j < tm->ntimers / 4; j++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time +
tm->triple_wheel.current_tick;
e->stop_timer_handle = tw_timer_start_4t_3w_256sl
(&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ ,
expiration_time);
}
adds += j;
}
vec_free (deleted_indices);
run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->triple_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->triple_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel);
return 0;
}
static clib_error_t *
test2_triple_ov (tw_timer_test_main_t * tm)
{
u32 i, j;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset = 0;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 *deleted_indices = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
expired_timer_triple_ov_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 75700;
run_triple_ov_wheel (&tm->triple_ov_wheel, initial_wheel_offset);
fformat (stdout,
"initial wheel time %d, fi %d si %d gi %d\n",
tm->triple_ov_wheel.current_tick,
tm->triple_ov_wheel.current_index[TW_TIMER_RING_FAST],
tm->triple_ov_wheel.current_index[TW_TIMER_RING_SLOW],
tm->triple_ov_wheel.current_index[TW_TIMER_RING_GLACIER]);
initial_wheel_offset = tm->triple_ov_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
e - tm->test_elts, 0 /* timer id */ ,
expiration_time);
}
adds += i;
for (i = 0; i < tm->niter; i++)
{
run_triple_ov_wheel (&tm->triple_ov_wheel, tm->ticks_per_iter);
j = 0;
vec_reset_length (deleted_indices);
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
tw_timer_stop_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
e->stop_timer_handle);
vec_add1 (deleted_indices, e - tm->test_elts);
if (++j >= tm->ntimers / 4)
goto del_and_re_add;
}));
/* *INDENT-ON* */
del_and_re_add:
for (j = 0; j < vec_len (deleted_indices); j++)
pool_put_index (tm->test_elts, deleted_indices[j]);
deletes += j;
for (j = 0; j < tm->ntimers / 4; j++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
do
{
expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
}
while (expiration_time == 0);
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time +
tm->triple_ov_wheel.current_tick;
e->stop_timer_handle = tw_timer_start_1t_3w_1024sl_ov
(&tm->triple_ov_wheel, e - tm->test_elts, 0 /* timer id */ ,
expiration_time);
}
adds += j;
}
vec_free (deleted_indices);
run_triple_ov_wheel (&tm->triple_ov_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->triple_ov_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->triple_ov_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
TWT (tw_timer) * t;
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
t = pool_elt_at_index (tm->triple_ov_wheel.timers, e->stop_timer_handle);
fformat (stdout, " expiration_time %lld\n", t->expiration_time);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_1t_3w_1024sl_ov (&tm->triple_ov_wheel);
return 0;
}
static clib_error_t *
test1_single (tw_timer_test_main_t * tm)
{
u32 i;
tw_timer_test_elt_t *e;
u32 offset;
tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,
expired_timer_single_callback,
1.0 /* timer interval */ , ~0);
/*
* Prime offset, to make sure that the wheel starts in a
* non-trivial position
*/
offset = 123;
run_single_wheel (&tm->single_wheel, offset);
fformat (stdout, "initial wheel time %d, fast index %d\n",
tm->single_wheel.current_tick,
tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
offset = tm->single_wheel.current_tick;
for (i = 0; i < tm->ntimers; i++)
{
u32 expected_to_expire;
u32 timer_arg;
timer_arg = 1 + i;
timer_arg &= 2047;
if (timer_arg == 0)
timer_arg = 1;
expected_to_expire = timer_arg + offset;
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
e->expected_to_expire = expected_to_expire;
e->stop_timer_handle = tw_timer_start_2t_1w_2048sl
(&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ ,
timer_arg);
}
run_single_wheel (&tm->single_wheel, tm->ntimers + 3);
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat(stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
fformat (stdout,
"final wheel time %d, fast index %d\n",
tm->single_wheel.current_tick,
tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
pool_free (tm->test_elts);
tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel);
return 0;
}
static clib_error_t *
test1_double (tw_timer_test_main_t * tm)
{
u32 i;
tw_timer_test_elt_t *e;
u32 offset;
tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
expired_timer_double_callback,
1.0 /* timer interval */ , ~0);
/*
* Prime offset, to make sure that the wheel starts in a
* non-trivial position
*/
offset = 227989;
run_double_wheel (&tm->double_wheel, offset);
fformat (stdout, "initial wheel time %d, fast index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST]);
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
e->expected_to_expire = i + offset + 1;
e->stop_timer_handle = tw_timer_start_16t_2w_512sl
(&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ ,
i + 1);
}
run_double_wheel (&tm->double_wheel, tm->ntimers + 3);
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat(stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
fformat (stdout,
"final wheel time %d, fast index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST]);
pool_free (tm->test_elts);
tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
return 0;
}
static clib_error_t *
test3_triple_double (tw_timer_test_main_t * tm)
{
tw_timer_test_elt_t *e;
u32 initial_wheel_offset = 0;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel,
expired_timer_triple_callback,
1.0 /* timer interval */ , ~0);
initial_wheel_offset = 0;
run_triple_wheel (&tm->triple_wheel, initial_wheel_offset);
fformat (stdout,
"initial wheel time %d, fi %d si %d gi %d\n",
tm->triple_wheel.current_tick,
tm->triple_wheel.current_index[TW_TIMER_RING_FAST],
tm->triple_wheel.current_index[TW_TIMER_RING_SLOW],
tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]);
initial_wheel_offset = tm->triple_wheel.current_tick;
fformat (stdout, "Create a timer which expires at wheel-time (1, 0, 0)\n");
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
/* 1 glacier ring tick from now */
expiration_time = TW_SLOTS_PER_RING * TW_SLOTS_PER_RING;
e->expected_to_expire = expiration_time + initial_wheel_offset;
max_expiration_time = expiration_time;
e->stop_timer_handle =
tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts,
3 /* timer id */ ,
expiration_time);
run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->triple_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->triple_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel);
return 0;
}
static clib_error_t *
test4_double_double (tw_timer_test_main_t * tm)
{
u32 i;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 *deleted_indices = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
expired_timer_double_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 0;
run_double_wheel (&tm->double_wheel, initial_wheel_offset);
fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST],
tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
initial_wheel_offset = tm->double_wheel.current_tick;
fformat (stdout, "test timer which expires at 512 ticks\n");
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
expiration_time = 512;
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
14 /* timer id */ ,
expiration_time);
}
adds = 1;
vec_free (deleted_indices);
run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->double_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->double_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
return 0;
}
static clib_error_t *
test5_double (tw_timer_test_main_t * tm)
{
u32 i;
tw_timer_test_elt_t *e;
u32 initial_wheel_offset;
u32 expiration_time;
u32 max_expiration_time = 0;
u32 adds = 0, deletes = 0;
f64 before, after;
clib_time_init (&tm->clib_time);
tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
expired_timer_double_callback,
1.0 /* timer interval */ , ~0);
/* Prime offset */
initial_wheel_offset = 7567;
run_double_wheel (&tm->double_wheel, initial_wheel_offset);
fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
tm->double_wheel.current_tick,
tm->double_wheel.current_index[TW_TIMER_RING_FAST],
tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
initial_wheel_offset = tm->double_wheel.current_tick;
fformat (stdout,
"test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
before = clib_time_now (&tm->clib_time);
/* Prime the pump */
for (i = 0; i < tm->ntimers; i++)
{
pool_get (tm->test_elts, e);
clib_memset (e, 0, sizeof (*e));
expiration_time = i + 1;
if (expiration_time > max_expiration_time)
max_expiration_time = expiration_time;
e->expected_to_expire = expiration_time + initial_wheel_offset;
e->stop_timer_handle =
tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
14 /* timer id */ ,
expiration_time);
}
adds += i;
run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
after = clib_time_now (&tm->clib_time);
fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
tm->double_wheel.current_tick);
fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
(after - before),
((f64) adds + (f64) deletes +
(f64) tm->double_wheel.current_tick) / (after - before));
if (pool_elts (tm->test_elts))
fformat (stdout, "Note: %d elements remain in pool\n",
pool_elts (tm->test_elts));
/* *INDENT-OFF* */
pool_foreach (e, tm->test_elts,
({
fformat (stdout, "[%d] expected to expire %d\n",
e - tm->test_elts,
e->expected_to_expire);
}));
/* *INDENT-ON* */
pool_free (tm->test_elts);
tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
return 0;
}
static clib_error_t *
timer_test_command_fn (tw_timer_test_main_t * tm, unformat_input_t * input)
{
int is_test1 = 0, is_updates = 0;
int num_wheels = 1;
int is_test2 = 0;
int is_test3 = 0;
int is_test4 = 0;
int is_test5 = 0;
int overflow = 0;
clib_memset (tm, 0, sizeof (*tm));
/* Default values */
tm->ntimers = 100000;
tm->seed = 0xDEADDABEB00BFACE;
tm->niter = 1000;
tm->ticks_per_iter = 727;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "seed %lld", &tm->seed))
;
else if (unformat (input, "test1"))
is_test1 = 1;
else if (unformat (input, "test2"))
is_test2 = 1;
else if (unformat (input, "overflow"))
overflow = 1;
else if (unformat (input, "lebron"))
is_test3 = 1;
else if (unformat (input, "wilt"))
is_test4 = 1;
else if (unformat (input, "linear"))
is_test5 = 1;
else if (unformat (input, "updates"))
is_updates = 1;
else if (unformat (input, "wheels %d", &num_wheels))
;
else if (unformat (input, "ntimers %d", &tm->ntimers))
;
else if (unformat (input, "niter %d", &tm->niter))
;
else if (unformat (input, "ticks_per_iter %d", &tm->ticks_per_iter))
;
else
break;
}
if (is_test1 + is_test2 + is_test3 + is_test4 + is_test5 == 0)
return clib_error_return (0, "No test specified [test1..n]");
if (num_wheels < 1 || num_wheels > 3)
return clib_error_return (0, "unsupported... 1 or 2 wheels only");
if (is_test1)
{
if (num_wheels == 1)
return test1_single (tm);
else
return test1_double (tm);
}
if (is_test2)
{
if (num_wheels == 1)
return test2_single (tm);
else if (num_wheels == 2)
if (is_updates)
return test2_double_updates (tm);
else
return test2_double (tm);
else if (num_wheels == 3)
{
if (overflow == 0)
return test2_triple (tm);
else
return test2_triple_ov (tm);
}
}
if (is_test3)
return test3_triple_double (tm);
if (is_test4)
return test4_double_double (tm);
if (is_test5)
return test5_double (tm);
/* NOTREACHED */
return 0;
}
#ifdef CLIB_UNIX
int
main (int argc, char *argv[])
{
unformat_input_t i;
clib_error_t *error;
tw_timer_test_main_t *tm = &tw_timer_test_main;
clib_mem_init (0, 3ULL << 30);
unformat_init_command_line (&i, argv);
error = timer_test_command_fn (tm, &i);
unformat_free (&i);
if (error)
{
clib_error_report (error);
return 1;
}
return 0;
}
#endif /* CLIB_UNIX */
/* For debugging... */
int
pifi (void *p, u32 index)
{
return pool_is_free_index (p, index);
}
u32
vl (void *p)
{
return vec_len (p);
}
uword
pe (void *v)
{
return (pool_elts (v));
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/