From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DBAE2A0093; Thu, 21 Apr 2022 18:00:04 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B637D410FB; Thu, 21 Apr 2022 17:59:59 +0200 (CEST) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by mails.dpdk.org (Postfix) with ESMTP id 935FF40040 for ; Thu, 21 Apr 2022 17:59:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1650556795; x=1682092795; h=from:to:subject:date:message-id:in-reply-to:references; bh=4to/JKluhd2xm/KT/9l8o61FXO0dtl88wim85rB2/J4=; b=mrsxMPa3bFKSjkRC37OmeIgq2FkPZ3Qd84HeD+ePBeBdAam4qAx7P5yh fWq5lFRTEwckQK46p0g6xxX1SlBKQQgQHk3859FOfmLjakqI7vpGMUsI4 wQoi0pYZJi+rYtLRK+9QQKwdyyt4bb2jhuwlU8zkPy7qiEOuowz0XqLDr FuoX2kRSgWMG2GYw8EIYPAZ91mkPVSNE/U9kb2uy4bvh2gwuP3ZxvO4AC tWidq2AbDdRxUztjgzmi2D06JjCc1wmBq77BoGn6qWcxc9tvVAdb/zAr1 UxPom3gP9RM/ikgt9HPYG6iRtPcZBvlF3S6a+1Xk9Jt2VDaM82lN3FAn7 w==; X-IronPort-AV: E=McAfee;i="6400,9594,10324"; a="244973287" X-IronPort-AV: E=Sophos;i="5.90,279,1643702400"; d="scan'208";a="244973287" Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Apr 2022 08:59:54 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.90,279,1643702400"; d="scan'208";a="728066549" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by orsmga005.jf.intel.com with ESMTP; 21 Apr 2022 08:59:53 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Subject: [PATCH 2/3] pipeline: improve learner table timers Date: Thu, 21 Apr 2022 16:59:50 +0100 Message-Id: <20220421155951.31811-2-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220421155951.31811-1-cristian.dumitrescu@intel.com> References: <20220421155951.31811-1-cristian.dumitrescu@intel.com> X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Signed-off-by: Cristian Dumitrescu --- lib/pipeline/rte_swx_ctl.h | 3 + lib/pipeline/rte_swx_pipeline.c | 164 ++++++++++++++++++++--- lib/pipeline/rte_swx_pipeline.h | 7 +- lib/pipeline/rte_swx_pipeline_internal.h | 70 +++++++++- lib/pipeline/rte_swx_pipeline_spec.c | 146 ++++++++++++++++---- 5 files changed, 336 insertions(+), 54 deletions(-) diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h index 204026dc0e..e4cdc840fc 100644 --- a/lib/pipeline/rte_swx_ctl.h +++ b/lib/pipeline/rte_swx_ctl.h @@ -629,6 +629,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; diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 17be31d5a4..228f309750 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -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); - - if (mf_name) { - struct field *mf; - - mf = metadata_field_parse(p, mf_name); - CHECK(mf, 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); - 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; @@ -10170,6 +10260,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 +10674,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 +11300,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 +11631,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 +11854,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; diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h index c95d0c7682..a5a0954915 100644 --- a/lib/pipeline/rte_swx_pipeline.h +++ b/lib/pipeline/rte_swx_pipeline.h @@ -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 diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h index 51bb464f5f..f8a6661f75 100644 --- a/lib/pipeline/rte_swx_pipeline_internal.h +++ b/lib/pipeline/rte_swx_pipeline_internal.h @@ -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; }; @@ -2202,7 +2210,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]; @@ -2215,8 +2225,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, @@ -2226,6 +2236,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. */ diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c index b2f2469b18..904b9eb471 100644 --- a/lib/pipeline/rte_swx_pipeline_spec.c +++ b/lib/pipeline/rte_swx_pipeline_spec.c @@ -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; -- 2.17.1