X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fpipeline%2Frte_swx_ctl.c;h=86b58e21dc2c4a337b3c5f7086a2706e8a6a37d0;hb=28dde5da503ed09f10cdfb295e390b114df7330a;hp=4ee47df108670699fda992b84acf3765eb76c8ab;hpb=cdaa937d3eaab0704ee991908497171417c7c09b;p=dpdk.git diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c index 4ee47df108..86b58e21dc 100644 --- a/lib/pipeline/rte_swx_ctl.c +++ b/lib/pipeline/rte_swx_ctl.c @@ -123,12 +123,26 @@ struct selector { struct rte_swx_table_selector_params params; }; +struct learner { + struct rte_swx_ctl_learner_info info; + struct rte_swx_ctl_table_match_field_info *mf; + struct rte_swx_ctl_table_action_info *actions; + uint32_t action_data_size; + + /* The pending default action: this is NOT the current default action; + * this will be the new default action after the next commit, if the + * next commit operation is successful. + */ + struct rte_swx_table_entry *pending_default; +}; + struct rte_swx_ctl_pipeline { struct rte_swx_ctl_pipeline_info info; struct rte_swx_pipeline *p; struct action *actions; struct table *tables; struct selector *selectors; + struct learner *learners; struct rte_swx_table_state *ts; struct rte_swx_table_state *ts_next; int numa_node; @@ -218,9 +232,6 @@ table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id) if (n_match_fields_em == table->info.n_match_fields) match_type = RTE_SWX_TABLE_MATCH_EXACT; - else if ((n_match_fields_em == table->info.n_match_fields - 1) && - (last->match_type == RTE_SWX_TABLE_MATCH_LPM)) - match_type = RTE_SWX_TABLE_MATCH_LPM; /* key_offset. */ key_offset = first->offset / 8; @@ -347,35 +358,15 @@ table_entry_check(struct rte_swx_ctl_pipeline *ctl, CHECK(entry, EINVAL); - if (key_check) { - if (table->is_stub) { - /* key. */ - CHECK(!entry->key, EINVAL); - - /* key_mask. */ - CHECK(!entry->key_mask, EINVAL); - } else { - /* key. */ - CHECK(entry->key, EINVAL); - - /* key_mask. */ - switch (table->params.match_type) { - case RTE_SWX_TABLE_MATCH_WILDCARD: - break; - - case RTE_SWX_TABLE_MATCH_LPM: - /* TBD Check that key mask is prefix. */ - break; - - case RTE_SWX_TABLE_MATCH_EXACT: - status = table_entry_key_check_em(table, entry); - if (status) - return status; - break; + if (key_check && !table->is_stub) { + /* key. */ + CHECK(entry->key, EINVAL); - default: - CHECK(0, EINVAL); - } + /* key_mask. */ + if (table->params.match_type == RTE_SWX_TABLE_MATCH_EXACT) { + status = table_entry_key_check_em(table, entry); + if (status) + return status; } } @@ -838,7 +829,7 @@ selector_free(struct rte_swx_ctl_pipeline *ctl) { uint32_t i; - if (ctl->selectors) + if (!ctl->selectors) return; for (i = 0; i < ctl->info.n_selectors; i++) { @@ -947,6 +938,70 @@ selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) return 0; } +static void +learner_pending_default_free(struct learner *l) +{ + if (!l->pending_default) + return; + + free(l->pending_default->action_data); + free(l->pending_default); + l->pending_default = NULL; +} + + +static void +learner_free(struct rte_swx_ctl_pipeline *ctl) +{ + uint32_t i; + + if (!ctl->learners) + return; + + for (i = 0; i < ctl->info.n_learners; i++) { + struct learner *l = &ctl->learners[i]; + + free(l->mf); + free(l->actions); + + learner_pending_default_free(l); + } + + free(ctl->learners); + ctl->learners = NULL; +} + +static struct learner * +learner_find(struct rte_swx_ctl_pipeline *ctl, const char *learner_name) +{ + uint32_t i; + + for (i = 0; i < ctl->info.n_learners; i++) { + struct learner *l = &ctl->learners[i]; + + if (!strcmp(learner_name, l->info.name)) + return l; + } + + return NULL; +} + +static uint32_t +learner_action_data_size_get(struct rte_swx_ctl_pipeline *ctl, struct learner *l) +{ + uint32_t action_data_size = 0, i; + + for (i = 0; i < l->info.n_actions; i++) { + uint32_t action_id = l->actions[i].action_id; + struct action *a = &ctl->actions[action_id]; + + if (a->data_size > action_data_size) + action_data_size = a->data_size; + } + + return action_data_size; +} + static void table_state_free(struct rte_swx_ctl_pipeline *ctl) { @@ -977,6 +1032,14 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl) rte_swx_table_selector_free(ts->obj); } + /* For each learner table, free its table state. */ + for (i = 0; i < ctl->info.n_learners; i++) { + struct rte_swx_table_state *ts = &ctl->ts_next[i]; + + /* Default action data. */ + free(ts->default_action_data); + } + free(ctl->ts_next); ctl->ts_next = NULL; } @@ -1043,6 +1106,29 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) } } + /* Learner tables. */ + for (i = 0; i < ctl->info.n_learners; i++) { + struct learner *l = &ctl->learners[i]; + struct rte_swx_table_state *ts = &ctl->ts[i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[i]; + + /* Table object: duplicate from the current table state. */ + ts_next->obj = ts->obj; + + /* Default action data: duplicate from the current table state. */ + ts_next->default_action_data = malloc(l->action_data_size); + if (!ts_next->default_action_data) { + status = -ENOMEM; + goto error; + } + + memcpy(ts_next->default_action_data, + ts->default_action_data, + l->action_data_size); + + ts_next->default_action_id = ts->default_action_id; + } + return 0; error: @@ -1060,6 +1146,8 @@ rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl) table_state_free(ctl); + learner_free(ctl); + selector_free(ctl); table_free(ctl); @@ -1274,6 +1362,54 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p) goto error; } + /* learner tables. */ + ctl->learners = calloc(ctl->info.n_learners, sizeof(struct learner)); + if (!ctl->learners) + goto error; + + for (i = 0; i < ctl->info.n_learners; i++) { + struct learner *l = &ctl->learners[i]; + uint32_t j; + + /* info. */ + status = rte_swx_ctl_learner_info_get(p, i, &l->info); + if (status) + goto error; + + /* mf. */ + l->mf = calloc(l->info.n_match_fields, + sizeof(struct rte_swx_ctl_table_match_field_info)); + if (!l->mf) + goto error; + + for (j = 0; j < l->info.n_match_fields; j++) { + status = rte_swx_ctl_learner_match_field_info_get(p, + i, + j, + &l->mf[j]); + if (status) + goto error; + } + + /* actions. */ + l->actions = calloc(l->info.n_actions, + sizeof(struct rte_swx_ctl_table_action_info)); + if (!l->actions) + goto error; + + for (j = 0; j < l->info.n_actions; j++) { + status = rte_swx_ctl_learner_action_info_get(p, + i, + j, + &l->actions[j]); + if (status || l->actions[j].action_id >= ctl->info.n_actions) + goto error; + } + + /* action_data_size. */ + l->action_data_size = learner_action_data_size_get(ctl, l); + } + /* ts. */ status = rte_swx_pipeline_table_state_get(p, &ctl->ts); if (status) @@ -1708,9 +1844,8 @@ table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id) action_data = table->pending_default->action_data; a = &ctl->actions[action_id]; - memcpy(ts_next->default_action_data, - action_data, - a->data_size); + if (a->data_size) + memcpy(ts_next->default_action_data, action_data, a->data_size); ts_next->default_action_id = action_id; } @@ -2122,6 +2257,178 @@ selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int)); } +static struct rte_swx_table_entry * +learner_default_entry_alloc(struct learner *l) +{ + struct rte_swx_table_entry *entry; + + entry = calloc(1, sizeof(struct rte_swx_table_entry)); + if (!entry) + goto error; + + /* action_data. */ + if (l->action_data_size) { + entry->action_data = calloc(1, l->action_data_size); + if (!entry->action_data) + goto error; + } + + return entry; + +error: + table_entry_free(entry); + return NULL; +} + +static int +learner_default_entry_check(struct rte_swx_ctl_pipeline *ctl, + uint32_t learner_id, + struct rte_swx_table_entry *entry) +{ + struct learner *l = &ctl->learners[learner_id]; + struct action *a; + uint32_t i; + + CHECK(entry, EINVAL); + + /* action_id. */ + for (i = 0; i < l->info.n_actions; i++) + if (entry->action_id == l->actions[i].action_id) + break; + + CHECK(i < l->info.n_actions, EINVAL); + + /* action_data. */ + a = &ctl->actions[entry->action_id]; + CHECK(!(a->data_size && !entry->action_data), EINVAL); + + return 0; +} + +static struct rte_swx_table_entry * +learner_default_entry_duplicate(struct rte_swx_ctl_pipeline *ctl, + uint32_t learner_id, + struct rte_swx_table_entry *entry) +{ + struct learner *l = &ctl->learners[learner_id]; + struct rte_swx_table_entry *new_entry = NULL; + struct action *a; + uint32_t i; + + if (!entry) + goto error; + + new_entry = calloc(1, sizeof(struct rte_swx_table_entry)); + if (!new_entry) + goto error; + + /* action_id. */ + for (i = 0; i < l->info.n_actions; i++) + if (entry->action_id == l->actions[i].action_id) + break; + + if (i >= l->info.n_actions) + goto error; + + new_entry->action_id = entry->action_id; + + /* action_data. */ + a = &ctl->actions[entry->action_id]; + if (a->data_size && !entry->action_data) + goto error; + + /* The table layer provisions a constant action data size per + * entry, which should be the largest data size for all the + * actions enabled for the current table, and attempts to copy + * this many bytes each time a table entry is added, even if the + * specific action requires less data or even no data at all, + * hence we always have to allocate the max. + */ + new_entry->action_data = calloc(1, l->action_data_size); + if (!new_entry->action_data) + goto error; + + if (a->data_size) + memcpy(new_entry->action_data, entry->action_data, a->data_size); + + return new_entry; + +error: + table_entry_free(new_entry); + return NULL; +} + +int +rte_swx_ctl_pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *ctl, + const char *learner_name, + struct rte_swx_table_entry *entry) +{ + struct learner *l; + struct rte_swx_table_entry *new_entry; + uint32_t learner_id; + + CHECK(ctl, EINVAL); + + CHECK(learner_name && learner_name[0], EINVAL); + l = learner_find(ctl, learner_name); + CHECK(l, EINVAL); + learner_id = l - ctl->learners; + CHECK(!l->info.default_action_is_const, EINVAL); + + CHECK(entry, EINVAL); + CHECK(!learner_default_entry_check(ctl, learner_id, entry), EINVAL); + + new_entry = learner_default_entry_duplicate(ctl, learner_id, entry); + CHECK(new_entry, ENOMEM); + + learner_pending_default_free(l); + + l->pending_default = new_entry; + return 0; +} + +static void +learner_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id) +{ + struct learner *l = &ctl->learners[learner_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + + ctl->info.n_selectors + learner_id]; + struct action *a; + uint8_t *action_data; + uint64_t action_id; + + /* Copy the pending default entry. */ + if (!l->pending_default) + return; + + action_id = l->pending_default->action_id; + action_data = l->pending_default->action_data; + a = &ctl->actions[action_id]; + + if (a->data_size) + memcpy(ts_next->default_action_data, action_data, a->data_size); + + ts_next->default_action_id = action_id; +} + +static void +learner_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id) +{ + struct learner *l = &ctl->learners[learner_id]; + + /* Free up the pending default entry, as it is now part of the table. */ + learner_pending_default_free(l); +} + +static void +learner_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id) +{ + struct learner *l = &ctl->learners[learner_id]; + + /* Free up the pending default entry, as it is no longer going to be added to the table. */ + learner_pending_default_free(l); +} + int rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) { @@ -2133,6 +2440,7 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) /* Operate the changes on the current ts_next before it becomes the new ts. First, operate * all the changes that can fail; if no failure, then operate the changes that cannot fail. + * We must be able to fully revert all the changes that can fail as if they never happened. */ for (i = 0; i < ctl->info.n_tables; i++) { status = table_rollfwd0(ctl, i, 0); @@ -2146,9 +2454,15 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) goto rollback; } + /* Second, operate all the changes that cannot fail. Since nothing can fail from this point + * onwards, the transaction is guaranteed to be successful. + */ for (i = 0; i < ctl->info.n_tables; i++) table_rollfwd1(ctl, i); + for (i = 0; i < ctl->info.n_learners; i++) + learner_rollfwd(ctl, i); + /* Swap the table state for the data plane. The current ts and ts_next * become the new ts_next and ts, respectively. */ @@ -2174,6 +2488,11 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) selector_rollfwd_finalize(ctl, i); } + for (i = 0; i < ctl->info.n_learners; i++) { + learner_rollfwd(ctl, i); + learner_rollfwd_finalize(ctl, i); + } + return 0; rollback: @@ -2189,6 +2508,10 @@ rollback: selector_abort(ctl, i); } + if (abort_on_fail) + for (i = 0; i < ctl->info.n_learners; i++) + learner_abort(ctl, i); + return status; } @@ -2205,6 +2528,48 @@ rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl) for (i = 0; i < ctl->info.n_selectors; i++) selector_abort(ctl, i); + + for (i = 0; i < ctl->info.n_learners; i++) + learner_abort(ctl, i); +} + +static int +mask_to_prefix(uint64_t mask, uint32_t mask_length, uint32_t *prefix_length) +{ + uint32_t n_trailing_zeros = 0, n_ones = 0, i; + + if (!mask) { + *prefix_length = 0; + return 0; + } + + /* Count trailing zero bits. */ + for (i = 0; i < 64; i++) { + if (mask & (1LLU << i)) + break; + + n_trailing_zeros++; + } + + /* Count the one bits that follow. */ + for ( ; i < 64; i++) { + if (!(mask & (1LLU << i))) + break; + + n_ones++; + } + + /* Check that no more one bits are present */ + for ( ; i < 64; i++) + if (mask & (1LLU << i)) + return -EINVAL; + + /* Check that the input mask is a prefix or the right length. */ + if (n_ones + n_trailing_zeros != mask_length) + return -EINVAL; + + *prefix_length = n_ones; + return 0; } static int @@ -2231,8 +2596,8 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl, struct action *action; struct rte_swx_table_entry *entry = NULL; char *s0 = NULL, *s; - uint32_t n_tokens = 0, arg_offset = 0, i; - int blank_or_comment = 0; + uint32_t n_tokens = 0, arg_offset = 0, lpm_prefix_length_max = 0, lpm_prefix_length = 0, i; + int lpm = 0, blank_or_comment = 0; /* Check input arguments. */ if (!ctl) @@ -2307,6 +2672,19 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl, if (mf_mask[0]) goto error; + /* LPM. */ + if (mf->match_type == RTE_SWX_TABLE_MATCH_LPM) { + int status; + + lpm = 1; + + lpm_prefix_length_max = mf->n_bits; + + status = mask_to_prefix(mask, mf->n_bits, &lpm_prefix_length); + if (status) + goto error; + } + /* Endianness conversion. */ if (mf->is_header) mask = field_hton(mask, mf->n_bits); @@ -2361,6 +2739,10 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl, n_tokens -= 2; } + /* LPM. */ + if (lpm) + entry->key_priority = lpm_prefix_length_max - lpm_prefix_length; + /* * Action. */ @@ -2427,6 +2809,130 @@ error: return NULL; } +struct rte_swx_table_entry * +rte_swx_ctl_pipeline_learner_default_entry_read(struct rte_swx_ctl_pipeline *ctl, + const char *learner_name, + const char *string, + int *is_blank_or_comment) +{ + char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens; + struct learner *l; + struct action *action; + struct rte_swx_table_entry *entry = NULL; + char *s0 = NULL, *s; + uint32_t n_tokens = 0, arg_offset = 0, i; + int blank_or_comment = 0; + + /* Check input arguments. */ + if (!ctl) + goto error; + + if (!learner_name || !learner_name[0]) + goto error; + + l = learner_find(ctl, learner_name); + if (!l) + goto error; + + if (!string || !string[0]) + goto error; + + /* Memory allocation. */ + s0 = strdup(string); + if (!s0) + goto error; + + entry = learner_default_entry_alloc(l); + if (!entry) + goto error; + + /* Parse the string into tokens. */ + for (s = s0; ; ) { + char *token; + + token = strtok_r(s, " \f\n\r\t\v", &s); + if (!token || token_is_comment(token)) + break; + + if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX) + goto error; + + token_array[n_tokens] = token; + n_tokens++; + } + + if (!n_tokens) { + blank_or_comment = 1; + goto error; + } + + tokens = token_array; + + /* + * Action. + */ + if (!(n_tokens && !strcmp(tokens[0], "action"))) + goto other; + + if (n_tokens < 2) + goto error; + + action = action_find(ctl, tokens[1]); + if (!action) + goto error; + + if (n_tokens < 2 + action->info.n_args * 2) + goto error; + + /* action_id. */ + entry->action_id = action - ctl->actions; + + /* action_data. */ + for (i = 0; i < action->info.n_args; i++) { + struct rte_swx_ctl_action_arg_info *arg = &action->args[i]; + char *arg_name, *arg_val; + uint64_t val; + + arg_name = tokens[2 + i * 2]; + arg_val = tokens[2 + i * 2 + 1]; + + if (strcmp(arg_name, arg->name)) + goto error; + + val = strtoull(arg_val, &arg_val, 0); + if (arg_val[0]) + goto error; + + /* Endianness conversion. */ + if (arg->is_network_byte_order) + val = field_hton(val, arg->n_bits); + + /* Copy to entry. */ + memcpy(&entry->action_data[arg_offset], + (uint8_t *)&val, + arg->n_bits / 8); + + arg_offset += arg->n_bits / 8; + } + + tokens += 2 + action->info.n_args * 2; + n_tokens -= 2 + action->info.n_args * 2; + +other: + if (n_tokens) + goto error; + + free(s0); + return entry; + +error: + table_entry_free(entry); + free(s0); + if (is_blank_or_comment) + *is_blank_or_comment = blank_or_comment; + return NULL; +} + static void table_entry_printf(FILE *f, struct rte_swx_ctl_pipeline *ctl,