pipeline: improve learner table timers

Enable the pipeline to use the improved learner table timer operation
through the new "rearm" instruction.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
This commit is contained in:
Cristian Dumitrescu 2022-05-20 23:12:54 +01:00 committed by Thomas Monjalon
parent 8186c0bbc9
commit e2ecc53582
6 changed files with 439 additions and 55 deletions

View File

@ -548,6 +548,9 @@ struct rte_swx_ctl_learner_info {
/** Learner table size parameter. */
uint32_t size;
/** Number of possible key timeout values. */
uint32_t n_key_timeouts;
};
/**
@ -615,6 +618,50 @@ rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
uint32_t learner_action_id,
struct rte_swx_ctl_table_action_info *learner_action);
/**
* Learner table timeout get
*
* @param[in] p
* Pipeline handle.
* @param[in] learner_id
* Learner table ID (0 .. *n_learners* - 1).
* @param[in] timeout_id
* Timeout ID.
* @param[out] timeout
* Timeout value measured in seconds. Must be non-NULL.
* @return
* 0 on success or the following error codes otherwise:
* -EINVAL: Invalid argument.
*/
__rte_experimental
int
rte_swx_ctl_pipeline_learner_timeout_get(struct rte_swx_pipeline *p,
uint32_t learner_id,
uint32_t timeout_id,
uint32_t *timeout);
/**
* Learner table timeout set
*
* @param[in] p
* Pipeline handle.
* @param[in] learner_id
* Learner table ID (0 .. *n_learners* - 1).
* @param[in] timeout_id
* Timeout ID.
* @param[in] timeout
* Timeout value measured in seconds.
* @return
* 0 on success or the following error codes otherwise:
* -EINVAL: Invalid argument.
*/
__rte_experimental
int
rte_swx_ctl_pipeline_learner_timeout_set(struct rte_swx_pipeline *p,
uint32_t learner_id,
uint32_t timeout_id,
uint32_t timeout);
/** Learner table statistics. */
struct rte_swx_learner_stats {
/** Number of packets with lookup hit. */
@ -629,6 +676,9 @@ struct rte_swx_learner_stats {
/** Number of packets with learning error. */
uint64_t n_pkts_learn_err;
/** Number of packets with rearm event. */
uint64_t n_pkts_rearm;
/** Number of packets with forget event. */
uint64_t n_pkts_forget;

View File

@ -2556,7 +2556,7 @@ instr_learner_af_exec(struct rte_swx_pipeline *p)
stats->n_pkts_action[action_id] = n_pkts_action + 1;
/* Thread. */
thread_ip_action_call(p, t, action_id);
thread_ip_inc(p);
/* Action */
action_func(p);
@ -2583,31 +2583,38 @@ instr_learn_translate(struct rte_swx_pipeline *p,
struct instruction_data *data __rte_unused)
{
struct action *a;
const char *mf_name;
uint32_t mf_offset = 0;
struct field *mf_first_arg = NULL, *mf_timeout_id = NULL;
const char *mf_first_arg_name, *mf_timeout_id_name;
CHECK(action, EINVAL);
CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
CHECK((n_tokens == 3) || (n_tokens == 4), EINVAL);
/* Action. */
a = action_find(p, tokens[1]);
CHECK(a, EINVAL);
CHECK(!action_has_nbo_args(a), EINVAL);
mf_name = (n_tokens > 2) ? tokens[2] : NULL;
CHECK(!learner_action_args_check(p, a, mf_name), EINVAL);
/* Action first argument. */
mf_first_arg_name = (n_tokens == 4) ? tokens[2] : NULL;
CHECK(!learner_action_args_check(p, a, mf_first_arg_name), EINVAL);
if (mf_name) {
struct field *mf;
mf = metadata_field_parse(p, mf_name);
CHECK(mf, EINVAL);
mf_offset = mf->offset / 8;
if (mf_first_arg_name) {
mf_first_arg = metadata_field_parse(p, mf_first_arg_name);
CHECK(mf_first_arg, EINVAL);
}
/* Timeout ID. */
mf_timeout_id_name = (n_tokens == 4) ? tokens[3] : tokens[2];
CHECK_NAME(mf_timeout_id_name, EINVAL);
mf_timeout_id = metadata_field_parse(p, mf_timeout_id_name);
CHECK(mf_timeout_id, EINVAL);
/* Instruction. */
instr->type = INSTR_LEARNER_LEARN;
instr->learn.action_id = a->id;
instr->learn.mf_offset = mf_offset;
instr->learn.mf_first_arg_offset = mf_first_arg ? (mf_first_arg->offset / 8) : 0;
instr->learn.mf_timeout_id_offset = mf_timeout_id->offset / 8;
instr->learn.mf_timeout_id_n_bits = mf_timeout_id->n_bits;
return 0;
}
@ -2624,6 +2631,66 @@ instr_learn_exec(struct rte_swx_pipeline *p)
thread_ip_inc(p);
}
/*
* rearm.
*/
static int
instr_rearm_translate(struct rte_swx_pipeline *p,
struct action *action,
char **tokens,
int n_tokens,
struct instruction *instr,
struct instruction_data *data __rte_unused)
{
struct field *mf_timeout_id;
const char *mf_timeout_id_name;
CHECK(action, EINVAL);
CHECK((n_tokens == 1) || (n_tokens == 2), EINVAL);
/* INSTR_LEARNER_REARM. */
if (n_tokens == 1) {
instr->type = INSTR_LEARNER_REARM;
return 0;
}
/* INSTR_LEARNER_REARM_NEW. */
mf_timeout_id_name = tokens[1];
CHECK_NAME(mf_timeout_id_name, EINVAL);
mf_timeout_id = metadata_field_parse(p, mf_timeout_id_name);
CHECK(mf_timeout_id, EINVAL);
instr->type = INSTR_LEARNER_REARM_NEW;
instr->learn.mf_timeout_id_offset = mf_timeout_id->offset / 8;
instr->learn.mf_timeout_id_n_bits = mf_timeout_id->n_bits;
return 0;
}
static inline void
instr_rearm_exec(struct rte_swx_pipeline *p)
{
struct thread *t = &p->threads[p->thread_id];
struct instruction *ip = t->ip;
__instr_rearm_exec(p, t, ip);
/* Thread. */
thread_ip_inc(p);
}
static inline void
instr_rearm_new_exec(struct rte_swx_pipeline *p)
{
struct thread *t = &p->threads[p->thread_id];
struct instruction *ip = t->ip;
__instr_rearm_new_exec(p, t, ip);
/* Thread. */
thread_ip_inc(p);
}
/*
* forget.
*/
@ -6051,6 +6118,13 @@ instr_translate(struct rte_swx_pipeline *p,
n_tokens - tpos,
instr,
data);
if (!strcmp(tokens[tpos], "rearm"))
return instr_rearm_translate(p,
action,
&tokens[tpos],
n_tokens - tpos,
instr,
data);
if (!strcmp(tokens[tpos], "forget"))
return instr_forget_translate(p,
@ -7040,6 +7114,8 @@ static instr_exec_t instruction_table[] = {
[INSTR_LEARNER] = instr_learner_exec,
[INSTR_LEARNER_AF] = instr_learner_af_exec,
[INSTR_LEARNER_LEARN] = instr_learn_exec,
[INSTR_LEARNER_REARM] = instr_rearm_exec,
[INSTR_LEARNER_REARM_NEW] = instr_rearm_new_exec,
[INSTR_LEARNER_FORGET] = instr_forget_exec,
[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
[INSTR_EXTERN_FUNC] = instr_extern_func_exec,
@ -8546,7 +8622,8 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
const char *name,
struct rte_swx_pipeline_learner_params *params,
uint32_t size,
uint32_t timeout)
uint32_t *timeout,
uint32_t n_timeouts)
{
struct learner *l = NULL;
struct action *default_action;
@ -8616,6 +8693,7 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
/* Any other checks. */
CHECK(size, EINVAL);
CHECK(timeout, EINVAL);
CHECK(n_timeouts && (n_timeouts < RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX), EINVAL);
/* Memory allocation. */
l = calloc(1, sizeof(struct learner));
@ -8702,7 +8780,10 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
l->size = size;
l->timeout = timeout;
for (i = 0; i < n_timeouts; i++)
l->timeout[i] = timeout[i];
l->n_timeouts = n_timeouts;
l->id = p->n_learners;
@ -8734,6 +8815,8 @@ learner_params_free(struct rte_swx_table_learner_params *params)
free(params->key_mask0);
free(params->key_timeout);
free(params);
}
@ -8787,9 +8870,16 @@ learner_params_get(struct learner *l)
/* Maximum number of keys. */
params->n_keys_max = l->size;
/* Memory allocation. */
params->key_timeout = calloc(l->n_timeouts, sizeof(uint32_t));
if (!params->key_timeout)
goto error;
/* Timeout. */
params->key_timeout[0] = l->timeout;
params->n_key_timeouts = 1;
for (i = 0; i < l->n_timeouts; i++)
params->key_timeout[i] = l->timeout[i];
params->n_key_timeouts = l->n_timeouts;
return params;
@ -9984,6 +10074,7 @@ rte_swx_ctl_learner_info_get(struct rte_swx_pipeline *p,
learner->n_actions = l->n_actions;
learner->default_action_is_const = l->default_action_is_const;
learner->size = l->size;
learner->n_key_timeouts = l->n_timeouts;
return 0;
}
@ -10039,6 +10130,56 @@ rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
return 0;
}
int
rte_swx_ctl_pipeline_learner_timeout_get(struct rte_swx_pipeline *p,
uint32_t learner_id,
uint32_t timeout_id,
uint32_t *timeout)
{
struct learner *l;
if (!p || (learner_id >= p->n_learners) || !timeout)
return -EINVAL;
l = learner_find_by_id(p, learner_id);
if (!l || (timeout_id >= l->n_timeouts))
return -EINVAL;
*timeout = l->timeout[timeout_id];
return 0;
}
int
rte_swx_ctl_pipeline_learner_timeout_set(struct rte_swx_pipeline *p,
uint32_t learner_id,
uint32_t timeout_id,
uint32_t timeout)
{
struct learner *l;
struct rte_swx_table_state *ts;
int status;
if (!p || (learner_id >= p->n_learners) || !timeout)
return -EINVAL;
l = learner_find_by_id(p, learner_id);
if (!l || (timeout_id >= l->n_timeouts))
return -EINVAL;
if (!p->build_done)
return -EINVAL;
ts = &p->table_state[p->n_tables + p->n_selectors + l->id];
status = rte_swx_table_learner_timeout_update(ts->obj, timeout_id, timeout);
if (status)
return -EINVAL;
l->timeout[timeout_id] = timeout;
return 0;
}
int
rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p,
struct rte_swx_table_state **table_state)
@ -10170,6 +10311,7 @@ rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,
stats->n_pkts_learn_ok = learner_stats->n_pkts_learn[0];
stats->n_pkts_learn_err = learner_stats->n_pkts_learn[1];
stats->n_pkts_rearm = learner_stats->n_pkts_rearm;
stats->n_pkts_forget = learner_stats->n_pkts_forget;
return 0;
@ -10583,6 +10725,8 @@ instr_type_to_name(struct instruction *instr)
case INSTR_LEARNER_AF: return "INSTR_LEARNER_AF";
case INSTR_LEARNER_LEARN: return "INSTR_LEARNER_LEARN";
case INSTR_LEARNER_REARM: return "INSTR_LEARNER_REARM";
case INSTR_LEARNER_REARM_NEW: return "INSTR_LEARNER_REARM_NEW";
case INSTR_LEARNER_FORGET: return "INSTR_LEARNER_FORGET";
case INSTR_EXTERN_OBJ: return "INSTR_EXTERN_OBJ";
@ -11207,11 +11351,40 @@ instr_learn_export(struct instruction *instr, FILE *f)
"\t{\n"
"\t\t.type = %s,\n"
"\t\t.learn = {\n"
"\t\t\t\t.action_id = %u,\n"
"\t\t\t.action_id = %u,\n"
"\t\t\t.mf_first_arg_offset = %u,\n"
"\t\t\t.mf_timeout_id_offset = %u,\n"
"\t\t\t.mf_timeout_id_n_bits = %u,\n"
"\t\t},\n"
"\t},\n",
instr_type_to_name(instr),
instr->learn.action_id);
instr->learn.action_id,
instr->learn.mf_first_arg_offset,
instr->learn.mf_timeout_id_offset,
instr->learn.mf_timeout_id_n_bits);
}
static void
instr_rearm_export(struct instruction *instr, FILE *f)
{
if (instr->type == INSTR_LEARNER_REARM)
fprintf(f,
"\t{\n"
"\t\t.type = %s,\n"
"\t},\n",
instr_type_to_name(instr));
else
fprintf(f,
"\t{\n"
"\t\t.type = %s,\n"
"\t\t.learn = {\n"
"\t\t\t.mf_timeout_id_offset = %u,\n"
"\t\t\t.mf_timeout_id_n_bits = %u,\n"
"\t\t},\n"
"\t},\n",
instr_type_to_name(instr),
instr->learn.mf_timeout_id_offset,
instr->learn.mf_timeout_id_n_bits);
}
static void
@ -11509,6 +11682,8 @@ static instruction_export_t export_table[] = {
[INSTR_LEARNER_AF] = instr_table_export,
[INSTR_LEARNER_LEARN] = instr_learn_export,
[INSTR_LEARNER_REARM] = instr_rearm_export,
[INSTR_LEARNER_REARM_NEW] = instr_rearm_export,
[INSTR_LEARNER_FORGET] = instr_forget_export,
[INSTR_EXTERN_OBJ] = instr_extern_export,
@ -11730,6 +11905,8 @@ instr_type_to_func(struct instruction *instr)
case INSTR_LEARNER_AF: return NULL;
case INSTR_LEARNER_LEARN: return "__instr_learn_exec";
case INSTR_LEARNER_REARM: return "__instr_rearm_exec";
case INSTR_LEARNER_REARM_NEW: return "__instr_rearm_new_exec";
case INSTR_LEARNER_FORGET: return "__instr_forget_exec";
case INSTR_EXTERN_OBJ: return NULL;

View File

@ -786,7 +786,9 @@ struct rte_swx_pipeline_learner_params {
* @param[in] size
* The maximum number of table entries. Must be non-zero.
* @param[in] timeout
* Table entry timeout in seconds. Must be non-zero.
* Array of possible table entry timeouts in seconds. Must be non-NULL.
* @param[in] n_timeouts
* Number of elements in the *timeout* array.
* @return
* 0 on success or the following error codes otherwise:
* -EINVAL: Invalid argument;
@ -800,7 +802,8 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
const char *name,
struct rte_swx_pipeline_learner_params *params,
uint32_t size,
uint32_t timeout);
uint32_t *timeout,
uint32_t n_timeouts);
/**
* Pipeline register array configure

View File

@ -476,9 +476,13 @@ enum instruction_type {
INSTR_LEARNER,
INSTR_LEARNER_AF,
/* learn LEARNER ACTION_NAME [ m.action_first_arg ] */
/* learn ACTION_NAME [ m.action_first_arg ] m.timeout_id */
INSTR_LEARNER_LEARN,
/* rearm [ m.timeout_id ] */
INSTR_LEARNER_REARM,
INSTR_LEARNER_REARM_NEW,
/* forget */
INSTR_LEARNER_FORGET,
@ -611,7 +615,9 @@ struct instr_table {
struct instr_learn {
uint8_t action_id;
uint8_t mf_offset;
uint8_t mf_first_arg_offset;
uint8_t mf_timeout_id_offset;
uint8_t mf_timeout_id_n_bits;
};
struct instr_extern_obj {
@ -850,7 +856,8 @@ struct learner {
int *action_is_for_default_entry;
uint32_t size;
uint32_t timeout;
uint32_t timeout[RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX];
uint32_t n_timeouts;
uint32_t id;
};
@ -864,6 +871,7 @@ struct learner_runtime {
struct learner_statistics {
uint64_t n_pkts_hit[2]; /* 0 = Miss, 1 = Hit. */
uint64_t n_pkts_learn[2]; /* 0 = Learn OK, 1 = Learn error. */
uint64_t n_pkts_rearm;
uint64_t n_pkts_forget;
uint64_t *n_pkts_action;
};
@ -2208,7 +2216,9 @@ __instr_learn_exec(struct rte_swx_pipeline *p,
const struct instruction *ip)
{
uint64_t action_id = ip->learn.action_id;
uint32_t mf_offset = ip->learn.mf_offset;
uint32_t mf_first_arg_offset = ip->learn.mf_first_arg_offset;
uint32_t timeout_id = METADATA_READ(t, ip->learn.mf_timeout_id_offset,
ip->learn.mf_timeout_id_n_bits);
uint32_t learner_id = t->learner_id;
struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
p->n_selectors + learner_id];
@ -2221,8 +2231,8 @@ __instr_learn_exec(struct rte_swx_pipeline *p,
l->mailbox,
t->time,
action_id,
&t->metadata[mf_offset],
0);
&t->metadata[mf_first_arg_offset],
timeout_id);
TRACE("[Thread %2u] learner %u learn %s\n",
p->thread_id,
@ -2232,6 +2242,54 @@ __instr_learn_exec(struct rte_swx_pipeline *p,
stats->n_pkts_learn[status] += 1;
}
/*
* rearm.
*/
static inline void
__instr_rearm_exec(struct rte_swx_pipeline *p,
struct thread *t,
const struct instruction *ip __rte_unused)
{
uint32_t learner_id = t->learner_id;
struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
p->n_selectors + learner_id];
struct learner_runtime *l = &t->learners[learner_id];
struct learner_statistics *stats = &p->learner_stats[learner_id];
/* Table. */
rte_swx_table_learner_rearm(ts->obj, l->mailbox, t->time);
TRACE("[Thread %2u] learner %u rearm\n",
p->thread_id,
learner_id);
stats->n_pkts_rearm += 1;
}
static inline void
__instr_rearm_new_exec(struct rte_swx_pipeline *p,
struct thread *t,
const struct instruction *ip)
{
uint32_t timeout_id = METADATA_READ(t, ip->learn.mf_timeout_id_offset,
ip->learn.mf_timeout_id_n_bits);
uint32_t learner_id = t->learner_id;
struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
p->n_selectors + learner_id];
struct learner_runtime *l = &t->learners[learner_id];
struct learner_statistics *stats = &p->learner_stats[learner_id];
/* Table. */
rte_swx_table_learner_rearm_new(ts->obj, l->mailbox, t->time, timeout_id);
TRACE("[Thread %2u] learner %u rearm with timeout ID %u\n",
p->thread_id,
learner_id,
timeout_id);
stats->n_pkts_rearm += 1;
}
/*
* forget.
*/

View File

@ -29,7 +29,8 @@
#define LEARNER_BLOCK 7
#define LEARNER_KEY_BLOCK 8
#define LEARNER_ACTIONS_BLOCK 9
#define APPLY_BLOCK 10
#define LEARNER_TIMEOUT_BLOCK 10
#define APPLY_BLOCK 11
/*
* extobj.
@ -1395,14 +1396,18 @@ selector_block_parse(struct selector_spec *s,
* }
* default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
* size SIZE
* timeout TIMEOUT_IN_SECONDS
* timeout {
* TIMEOUT_IN_SECONDS
* ...
* }
* }
*/
struct learner_spec {
char *name;
struct rte_swx_pipeline_learner_params params;
uint32_t size;
uint32_t timeout;
uint32_t *timeout;
uint32_t n_timeouts;
};
static void
@ -1457,7 +1462,10 @@ learner_spec_free(struct learner_spec *s)
s->size = 0;
s->timeout = 0;
free(s->timeout);
s->timeout = NULL;
s->n_timeouts = 0;
}
static int
@ -1719,6 +1727,95 @@ learner_default_action_statement_parse(struct learner_spec *s,
return status;
}
static int
learner_timeout_statement_parse(uint32_t *block_mask,
char **tokens,
uint32_t n_tokens,
uint32_t n_lines,
uint32_t *err_line,
const char **err_msg)
{
/* Check format. */
if ((n_tokens != 2) || strcmp(tokens[1], "{")) {
if (err_line)
*err_line = n_lines;
if (err_msg)
*err_msg = "Invalid timeout statement.";
return -EINVAL;
}
/* block_mask. */
*block_mask |= 1 << LEARNER_TIMEOUT_BLOCK;
return 0;
}
static int
learner_timeout_block_parse(struct learner_spec *s,
uint32_t *block_mask,
char **tokens,
uint32_t n_tokens,
uint32_t n_lines,
uint32_t *err_line,
const char **err_msg)
{
uint32_t *new_timeout = NULL;
char *str;
uint32_t val;
int status = 0;
/* Handle end of block. */
if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
*block_mask &= ~(1 << LEARNER_TIMEOUT_BLOCK);
return 0;
}
/* Check input arguments. */
if (n_tokens != 1) {
status = -EINVAL;
goto error;
}
str = tokens[0];
val = strtoul(str, &str, 0);
if (str[0]) {
status = -EINVAL;
goto error;
}
new_timeout = realloc(s->timeout, (s->n_timeouts + 1) * sizeof(uint32_t));
if (!new_timeout) {
status = -ENOMEM;
goto error;
}
s->timeout = new_timeout;
s->timeout[s->n_timeouts] = val;
s->n_timeouts++;
return 0;
error:
free(new_timeout);
if (err_line)
*err_line = n_lines;
if (err_msg)
switch (status) {
case -ENOMEM:
*err_msg = "Memory allocation failed.";
break;
default:
*err_msg = "Invalid timeout value statement.";
break;
}
return status;
}
static int
learner_statement_parse(struct learner_spec *s,
uint32_t *block_mask,
@ -1780,6 +1877,15 @@ learner_block_parse(struct learner_spec *s,
err_line,
err_msg);
if (*block_mask & (1 << LEARNER_TIMEOUT_BLOCK))
return learner_timeout_block_parse(s,
block_mask,
tokens,
n_tokens,
n_lines,
err_line,
err_msg);
/* Handle end of block. */
if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
*block_mask &= ~(1 << LEARNER_BLOCK);
@ -1833,28 +1939,13 @@ learner_block_parse(struct learner_spec *s,
return 0;
}
if (!strcmp(tokens[0], "timeout")) {
char *p = tokens[1];
if (n_tokens != 2) {
if (err_line)
*err_line = n_lines;
if (err_msg)
*err_msg = "Invalid timeout statement.";
return -EINVAL;
}
s->timeout = strtoul(p, &p, 0);
if (p[0]) {
if (err_line)
*err_line = n_lines;
if (err_msg)
*err_msg = "Invalid timeout argument.";
return -EINVAL;
}
return 0;
}
if (!strcmp(tokens[0], "timeout"))
return learner_timeout_statement_parse(block_mask,
tokens,
n_tokens,
n_lines,
err_line,
err_msg);
/* Anything else. */
if (err_line)
@ -2365,7 +2456,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
learner_spec.name,
&learner_spec.params,
learner_spec.size,
learner_spec.timeout);
learner_spec.timeout,
learner_spec.n_timeouts);
if (status) {
if (err_line)
*err_line = n_lines;

View File

@ -140,4 +140,8 @@ EXPERIMENTAL {
rte_swx_ctl_learner_info_get;
rte_swx_ctl_learner_match_field_info_get;
rte_swx_pipeline_learner_config;
#added in 22.07
rte_swx_ctl_pipeline_learner_timeout_get;
rte_swx_ctl_pipeline_learner_timeout_set;
};