X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fpipeline%2Frte_swx_ctl.c;h=f52ccffd75a41718cc8dd8e96a5f8f17d0ba56d9;hb=77e764c7ecd36de92c574731d05d1fb34c28e680;hp=5d04e750f4cb4669275af7bf8257376e37d9e04c;hpb=99a2dd955fba6e4cc23b77d590a033650ced9c45;p=dpdk.git diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c index 5d04e750f4..f52ccffd75 100644 --- a/lib/pipeline/rte_swx_ctl.c +++ b/lib/pipeline/rte_swx_ctl.c @@ -10,6 +10,8 @@ #include #include +#include + #include "rte_swx_ctl.h" #define CHECK(condition, err_code) \ @@ -89,11 +91,58 @@ struct table { uint32_t n_delete; }; +struct selector { + /* Selector table info. */ + struct rte_swx_ctl_selector_info info; + + /* group_id field. */ + struct rte_swx_ctl_table_match_field_info group_id_field; + + /* selector fields. */ + struct rte_swx_ctl_table_match_field_info *selector_fields; + + /* member_id field. */ + struct rte_swx_ctl_table_match_field_info member_id_field; + + /* Current selector table. Array of info.n_groups_max elements.*/ + struct rte_swx_table_selector_group **groups; + + /* Pending selector table subject to the next commit. Array of info.n_groups_max elements. + */ + struct rte_swx_table_selector_group **pending_groups; + + /* Valid flag per group. Array of n_groups_max elements. */ + int *groups_added; + + /* Pending delete flag per group. Group deletion is subject to the next commit. Array of + * info.n_groups_max elements. + */ + int *groups_pending_delete; + + /* Params. */ + 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; @@ -183,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; @@ -312,52 +358,48 @@ 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; } } if (data_check) { struct action *a; + struct rte_swx_ctl_table_action_info *tai; uint32_t i; /* action_id. */ - for (i = 0; i < table->info.n_actions; i++) - if (entry->action_id == table->actions[i].action_id) + for (i = 0; i < table->info.n_actions; i++) { + tai = &table->actions[i]; + + if (entry->action_id == tai->action_id) break; + } CHECK(i < table->info.n_actions, EINVAL); /* action_data. */ a = &ctl->actions[entry->action_id]; CHECK(!(a->data_size && !entry->action_data), EINVAL); + + /* When both key_check and data_check are true, we are interested in both the entry + * key and data, which means the operation is _regular_ table entry add. + */ + if (key_check && !tai->action_is_for_table_entries) + return -EINVAL; + + /* When key_check is false while data_check is true, we are only interested in the + * entry data, which means the operation is _default_ table entry add. + */ + if (!key_check && !tai->action_is_for_default_entry) + return -EINVAL; } return 0; @@ -710,17 +752,285 @@ table_free(struct rte_swx_ctl_pipeline *ctl) } static void -table_state_free(struct rte_swx_ctl_pipeline *ctl) +selector_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->groups[group_id] = NULL; +} + +static void +selector_pending_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->pending_groups[group_id] = NULL; +} + +static int +selector_group_duplicate_to_pending(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *g, *gp; + struct rte_swx_table_selector_member *m; + + selector_pending_group_members_free(s, group_id); + + g = s->groups[group_id]; + gp = s->pending_groups[group_id]; + + if (!gp) { + gp = calloc(1, sizeof(struct rte_swx_table_selector_group)); + if (!gp) + goto error; + + TAILQ_INIT(&gp->members); + + s->pending_groups[group_id] = gp; + } + + if (!g) + return 0; + + TAILQ_FOREACH(m, &g->members, node) { + struct rte_swx_table_selector_member *mp; + + mp = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!mp) + goto error; + + memcpy(mp, m, sizeof(struct rte_swx_table_selector_member)); + + TAILQ_INSERT_TAIL(&gp->members, mp, node); + } + + return 0; + +error: + selector_pending_group_members_free(s, group_id); + return -ENOMEM; +} + +static void +selector_free(struct rte_swx_ctl_pipeline *ctl) +{ + uint32_t i; + + if (!ctl->selectors) + return; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t i; + + /* selector_fields. */ + free(s->selector_fields); + + /* groups. */ + if (s->groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_group_members_free(s, i); + + free(s->groups); + + /* pending_groups. */ + if (s->pending_groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_pending_group_members_free(s, i); + + free(s->pending_groups); + + /* groups_added. */ + free(s->groups_added); + + /* groups_pending_delete. */ + free(s->groups_pending_delete); + + /* params. */ + free(s->params.selector_mask); + } + + free(ctl->selectors); + ctl->selectors = NULL; +} + +static struct selector * +selector_find(struct rte_swx_ctl_pipeline *ctl, const char *selector_name) +{ + uint32_t i; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + + if (!strcmp(selector_name, s->info.name)) + return s; + } + + return NULL; +} + +static int +selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL; + uint8_t *selector_mask = NULL; + uint32_t selector_size = 0, selector_offset = 0, i; + + /* Find first (smallest offset) and last (biggest offset) match fields. */ + first = &s->selector_fields[0]; + last = &s->selector_fields[0]; + + for (i = 1; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + + if (f->offset < first->offset) + first = f; + + if (f->offset > last->offset) + last = f; + } + + /* selector_offset. */ + selector_offset = first->offset / 8; + + /* selector_size. */ + selector_size = (last->offset + last->n_bits - first->offset) / 8; + + /* selector_mask. */ + selector_mask = calloc(1, selector_size); + if (!selector_mask) + return -ENOMEM; + + for (i = 0; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + uint32_t start; + size_t size; + + start = (f->offset - first->offset) / 8; + size = f->n_bits / 8; + + memset(&selector_mask[start], 0xFF, size); + } + + /* Fill in. */ + s->params.group_id_offset = s->group_id_field.offset / 8; + s->params.selector_size = selector_size; + s->params.selector_offset = selector_offset; + s->params.selector_mask = selector_mask; + s->params.member_id_offset = s->member_id_field.offset / 8; + s->params.n_groups_max = s->info.n_groups_max; + s->params.n_members_per_group_max = s->info.n_members_per_group_max; + + 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) +{ + uint32_t table_base_index, selector_base_index, learner_base_index, i; + if (!ctl->ts_next) return; /* For each table, free its table state. */ + table_base_index = 0; for (i = 0; i < ctl->info.n_tables; i++) { struct table *table = &ctl->tables[i]; - struct rte_swx_table_state *ts = &ctl->ts_next[i]; + struct rte_swx_table_state *ts = &ctl->ts_next[table_base_index + i]; /* Default action data. */ free(ts->default_action_data); @@ -730,6 +1040,25 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl) table->ops.free(ts->obj); } + /* For each selector table, free its table state. */ + selector_base_index = ctl->info.n_tables; + for (i = 0; i < ctl->info.n_selectors; i++) { + struct rte_swx_table_state *ts = &ctl->ts_next[selector_base_index + i]; + + /* Table object. */ + if (ts->obj) + rte_swx_table_selector_free(ts->obj); + } + + /* For each learner table, free its table state. */ + learner_base_index = ctl->info.n_tables + ctl->info.n_selectors; + for (i = 0; i < ctl->info.n_learners; i++) { + struct rte_swx_table_state *ts = &ctl->ts_next[learner_base_index + i]; + + /* Default action data. */ + free(ts->default_action_data); + } + free(ctl->ts_next); ctl->ts_next = NULL; } @@ -737,20 +1066,22 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl) static int table_state_create(struct rte_swx_ctl_pipeline *ctl) { + uint32_t table_base_index, selector_base_index, learner_base_index, i; int status = 0; - uint32_t i; - ctl->ts_next = calloc(ctl->info.n_tables, + ctl->ts_next = calloc(ctl->info.n_tables + ctl->info.n_selectors + ctl->info.n_learners, sizeof(struct rte_swx_table_state)); if (!ctl->ts_next) { status = -ENOMEM; goto error; } + /* Tables. */ + table_base_index = 0; for (i = 0; i < ctl->info.n_tables; i++) { struct table *table = &ctl->tables[i]; - struct rte_swx_table_state *ts = &ctl->ts[i]; - struct rte_swx_table_state *ts_next = &ctl->ts_next[i]; + struct rte_swx_table_state *ts = &ctl->ts[table_base_index + i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[table_base_index + i]; /* Table object. */ if (!table->is_stub && table->ops.add) { @@ -782,6 +1113,44 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) ts_next->default_action_id = ts->default_action_id; } + /* Selector tables. */ + selector_base_index = ctl->info.n_tables; + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[selector_base_index + i]; + + /* Table object. */ + ts_next->obj = rte_swx_table_selector_create(&s->params, NULL, ctl->numa_node); + if (!ts_next->obj) { + status = -ENODEV; + goto error; + } + } + + /* Learner tables. */ + learner_base_index = ctl->info.n_tables + ctl->info.n_selectors; + for (i = 0; i < ctl->info.n_learners; i++) { + struct learner *l = &ctl->learners[i]; + struct rte_swx_table_state *ts = &ctl->ts[learner_base_index + i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[learner_base_index + 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: @@ -799,6 +1168,10 @@ rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl) table_state_free(ctl); + learner_free(ctl); + + selector_free(ctl); + table_free(ctl); free(ctl); @@ -940,37 +1313,156 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p) goto error; } - /* ts. */ - status = rte_swx_pipeline_table_state_get(p, &ctl->ts); - if (status) + /* selector tables. */ + ctl->selectors = calloc(ctl->info.n_selectors, sizeof(struct selector)); + if (!ctl->selectors) goto error; - /* ts_next. */ - status = table_state_create(ctl); - if (status) - goto error; + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t j; - return ctl; + /* info. */ + status = rte_swx_ctl_selector_info_get(p, i, &s->info); + if (status) + goto error; -error: - rte_swx_ctl_pipeline_free(ctl); - return NULL; -} + /* group_id field. */ + status = rte_swx_ctl_selector_group_id_field_info_get(p, + i, + &s->group_id_field); + if (status) + goto error; -int -rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl, - const char *table_name, - struct rte_swx_table_entry *entry) -{ - struct table *table; - struct rte_swx_table_entry *new_entry, *existing_entry; - uint32_t table_id; + /* selector fields. */ + s->selector_fields = calloc(s->info.n_selector_fields, + sizeof(struct rte_swx_ctl_table_match_field_info)); + if (!s->selector_fields) + goto error; - CHECK(ctl, EINVAL); - CHECK(table_name && table_name[0], EINVAL); + for (j = 0; j < s->info.n_selector_fields; j++) { + status = rte_swx_ctl_selector_field_info_get(p, + i, + j, + &s->selector_fields[j]); + if (status) + goto error; + } - table = table_find(ctl, table_name); - CHECK(table, EINVAL); + /* member_id field. */ + status = rte_swx_ctl_selector_member_id_field_info_get(p, + i, + &s->member_id_field); + if (status) + goto error; + + /* groups. */ + s->groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->groups) + goto error; + + /* pending_groups. */ + s->pending_groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->pending_groups) + goto error; + + /* groups_added. */ + s->groups_added = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_added) + goto error; + + /* groups_pending_delete. */ + s->groups_pending_delete = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_pending_delete) + goto error; + + /* params. */ + status = selector_params_get(ctl, i); + if (status) + 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) + goto error; + + /* ts_next. */ + status = table_state_create(ctl); + if (status) + goto error; + + return ctl; + +error: + rte_swx_ctl_pipeline_free(ctl); + return NULL; +} + +int +rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl, + const char *table_name, + struct rte_swx_table_entry *entry) +{ + struct table *table; + struct rte_swx_table_entry *new_entry, *existing_entry; + uint32_t table_id; + + CHECK(ctl, EINVAL); + CHECK(table_name && table_name[0], EINVAL); + + table = table_find(ctl, table_name); + CHECK(table, EINVAL); table_id = table - ctl->tables; CHECK(entry, EINVAL); @@ -1374,9 +1866,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; } @@ -1499,6 +1990,469 @@ table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id) table_pending_default_free(table); } +int +rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t *group_id) +{ + struct selector *s; + uint32_t i; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0] || !group_id) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s) + return -EINVAL; + + /* Find an unused group. */ + for (i = 0; i < s->info.n_groups_max; i++) + if (!s->groups_added[i]) { + *group_id = i; + s->groups_added[i] = 1; + return 0; + } + + return -ENOSPC; +} + +int +rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id]) + return -EINVAL; + + /* Check if this group is already scheduled for deletion. */ + if (s->groups_pending_delete[group_id]) + return 0; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Schedule removal of all the members from the current group. */ + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + /* Schedule the group for deletion. */ + s->groups_pending_delete[group_id] = 1; + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id, + uint32_t member_weight) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + if (!member_weight) + return rte_swx_ctl_pipeline_selector_group_member_delete(ctl, + selector_name, + group_id, + member_id); + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* If this member is already in this group, then simply update its weight and return. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + m->member_weight = member_weight; + return 0; + } + + /* Add new member to this group. */ + m = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!m) + return -ENOMEM; + + m->member_id = member_id; + m->member_weight = member_weight; + + TAILQ_INSERT_TAIL(&group->members, m, node); + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id __rte_unused, + uint32_t member_id __rte_unused) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Look for this member in the group and remove it, if found. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + TAILQ_REMOVE(&group->members, m, node); + free(m); + return 0; + } + + return 0; +} + +static int +selector_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Push pending group member changes (s->pending_groups[group_id]) to the selector table + * mirror copy (ts_next->obj). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + int status; + + /* Skip this group if no change needed. */ + if (!group) + continue; + + /* Apply the pending changes for the current group. */ + status = rte_swx_table_selector_group_set(ts_next->obj, group_id, group); + if (status) + return status; + } + + return 0; +} + +static void +selector_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Commit pending group member changes (s->pending_groups[group_id]) to the stable group + * records (s->groups[group_id). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *g = s->groups[group_id]; + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + /* Skip this group if no change needed. */ + if (!gp) + continue; + + /* Transition the pending changes to stable. */ + s->groups[group_id] = gp; + s->pending_groups[group_id] = NULL; + + /* Free the old group member list. */ + if (!g) + continue; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&g->members); + if (!m) + break; + + TAILQ_REMOVE(&g->members, m, node); + free(m); + } + + free(g); + } + + /* Commit pending group validity changes (from s->groups_pending_delete[group_id] to + * s->groups_added[group_id]. + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + if (s->groups_pending_delete[group_id]) { + s->groups_added[group_id] = 0; + s->groups_pending_delete[group_id] = 0; + } +} + +static void +selector_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts = &ctl->ts[ctl->info.n_tables + selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Discard any previous changes to the selector table mirror copy (ts_next->obj). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + if (gp) { + ts_next->obj = ts->obj; + break; + } + } +} + +static void +selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Discard any pending group member changes (s->pending_groups[group_id]). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + selector_pending_group_members_free(s, group_id); + + /* Discard any pending group deletions. */ + 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); + + CHECK(l->actions[entry->action_id].action_is_for_default_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) { @@ -1508,8 +2462,9 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) CHECK(ctl, EINVAL); - /* Operate the changes on the current ts_next before it becomes the new - * ts. + /* 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); @@ -1517,9 +2472,21 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) goto rollback; } + for (i = 0; i < ctl->info.n_selectors; i++) { + status = selector_rollfwd(ctl, i); + if (status) + 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. */ @@ -1529,7 +2496,10 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) ctl->ts = ctl->ts_next; ctl->ts_next = ts; - /* Operate the changes on the current ts_next, which is the previous ts. + /* Operate the changes on the current ts_next, which is the previous ts, in order to get + * the current ts_next in sync with the current ts. Since the changes that can fail did + * not fail on the previous ts_next, it is guaranteed that they will not fail on the + * current ts_next, hence no error checking is needed. */ for (i = 0; i < ctl->info.n_tables; i++) { table_rollfwd0(ctl, i, 1); @@ -1537,6 +2507,16 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) table_rollfwd2(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollfwd(ctl, i); + 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: @@ -1546,6 +2526,16 @@ rollback: table_abort(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollback(ctl, i); + if (abort_on_fail) + selector_abort(ctl, i); + } + + if (abort_on_fail) + for (i = 0; i < ctl->info.n_learners; i++) + learner_abort(ctl, i); + return status; } @@ -1559,6 +2549,51 @@ rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl) for (i = 0; i < ctl->info.n_tables; i++) table_abort(ctl, i); + + 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 @@ -1585,8 +2620,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) @@ -1636,7 +2671,7 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl, /* * Match. */ - if (n_tokens && strcmp(tokens[0], "match")) + if (!(n_tokens && !strcmp(tokens[0], "match"))) goto action; if (n_tokens < 1 + table->info.n_match_fields) @@ -1661,6 +2696,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); @@ -1715,11 +2763,139 @@ 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. */ action: - if (n_tokens && strcmp(tokens[0], "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; +} + +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) @@ -1858,3 +3034,49 @@ rte_swx_ctl_pipeline_table_fprintf(FILE *f, n_entries); return 0; } + +int +rte_swx_ctl_pipeline_selector_fprintf(FILE *f, + struct rte_swx_ctl_pipeline *ctl, + const char *selector_name) +{ + struct selector *s; + uint32_t group_id; + + if (!f || !ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s) + return -EINVAL; + + /* Selector. */ + fprintf(f, "# Selector %s: max groups %u, max members per group %u\n", + s->info.name, + s->info.n_groups_max, + s->info.n_members_per_group_max); + + /* Groups. */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->groups[group_id]; + struct rte_swx_table_selector_member *m; + uint32_t n_members = 0; + + fprintf(f, "Group %u = [", group_id); + + /* Non-empty group. */ + if (group) + TAILQ_FOREACH(m, &group->members, node) { + fprintf(f, "%u:%u ", m->member_id, m->member_weight); + n_members++; + } + + /* Empty group. */ + if (!n_members) + fprintf(f, "0:1 "); + + fprintf(f, "]\n"); + } + + return 0; +}