/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Intel Corporation */ #include #include #include #include #include #include #include "rte_swx_table_selector.h" #ifndef RTE_SWX_TABLE_SELECTOR_HUGE_PAGES_DISABLE #include static void * env_calloc(size_t size, size_t alignment, int numa_node) { return rte_zmalloc_socket(NULL, size, alignment, numa_node); } static void env_free(void *start, size_t size __rte_unused) { rte_free(start); } #else #include static void * env_calloc(size_t size, size_t alignment __rte_unused, int numa_node) { void *start; if (numa_available() == -1) return NULL; start = numa_alloc_onnode(size, numa_node); if (!start) return NULL; memset(start, 0, size); return start; } static void env_free(void *start, size_t size) { if ((numa_available() == -1) || !start) return; numa_free(start, size); } #endif #if defined(RTE_ARCH_X86_64) #include #define crc32_u64(crc, v) _mm_crc32_u64(crc, v) #else static inline uint64_t crc32_u64_generic(uint64_t crc, uint64_t value) { int i; crc = (crc & 0xFFFFFFFFLLU) ^ value; for (i = 63; i >= 0; i--) { uint64_t mask; mask = -(crc & 1LLU); crc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask); } return crc; } #define crc32_u64(crc, v) crc32_u64_generic(crc, v) #endif /* Key size needs to be one of: 8, 16, 32 or 64. */ static inline uint32_t hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed) { uint64_t *k = key; uint64_t *m = key_mask; uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5; switch (key_size) { case 8: crc0 = crc32_u64(seed, k[0] & m[0]); return crc0; case 16: k0 = k[0] & m[0]; crc0 = crc32_u64(k0, seed); crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); crc0 ^= crc1; return crc0; case 32: k0 = k[0] & m[0]; k2 = k[2] & m[2]; crc0 = crc32_u64(k0, seed); crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); crc2 = crc32_u64(k2, k[3] & m[3]); crc3 = k2 >> 32; crc0 = crc32_u64(crc0, crc1); crc1 = crc32_u64(crc2, crc3); crc0 ^= crc1; return crc0; case 64: k0 = k[0] & m[0]; k2 = k[2] & m[2]; k5 = k[5] & m[5]; crc0 = crc32_u64(k0, seed); crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); crc2 = crc32_u64(k2, k[3] & m[3]); crc3 = crc32_u64(k2 >> 32, k[4] & m[4]); crc4 = crc32_u64(k5, k[6] & m[6]); crc5 = crc32_u64(k5 >> 32, k[7] & m[7]); crc0 = crc32_u64(crc0, (crc1 << 32) ^ crc2); crc1 = crc32_u64(crc3, (crc4 << 32) ^ crc5); crc0 ^= crc1; return crc0; default: crc0 = 0; return crc0; } } struct group_member_info { uint32_t member_id; uint32_t member_weight; uint32_t member_weight_normalized; uint32_t count; }; struct table { /* Input parameters */ struct rte_swx_table_selector_params params; /* Internal. */ uint32_t *group_table; uint64_t group_table_size; struct group_member_info *members; uint32_t n_members_per_group_max_log2; }; uint64_t rte_swx_table_selector_footprint_get(uint32_t n_groups_max, uint32_t n_members_per_group_max) { uint64_t group_table_size, members_size; group_table_size = n_groups_max * n_members_per_group_max * sizeof(uint32_t); members_size = n_members_per_group_max * sizeof(struct group_member_info); return sizeof(struct table) + group_table_size + members_size; } void rte_swx_table_selector_free(void *table) { struct table *t = table; if (!t) return; free(t->members); env_free(t->group_table, t->group_table_size); free(t->params.selector_mask); free(t); } static int table_create_check(struct rte_swx_table_selector_params *params) { if (!params) return -1; if (!params->selector_size || (params->selector_size > 64) || !params->n_groups_max || (params->n_groups_max > 1U << 31) || !params->n_members_per_group_max || (params->n_members_per_group_max > 1U << 31)) return -EINVAL; return 0; } static int table_params_copy(struct table *t, struct rte_swx_table_selector_params *params) { uint32_t selector_size, i; selector_size = rte_align32pow2(params->selector_size); if (selector_size < 8) selector_size = 8; memcpy(&t->params, params, sizeof(struct rte_swx_table_selector_params)); t->params.selector_size = selector_size; t->params.selector_mask = NULL; t->params.n_groups_max = rte_align32pow2(params->n_groups_max); t->params.n_members_per_group_max = rte_align32pow2(params->n_members_per_group_max); for (i = 0; i < 32; i++) if (t->params.n_members_per_group_max == 1U << i) t->n_members_per_group_max_log2 = i; /* t->params.selector_mask */ t->params.selector_mask = calloc(selector_size, sizeof(uint8_t)); if (!t->params.selector_mask) goto error; if (params->selector_mask) memcpy(t->params.selector_mask, params->selector_mask, params->selector_size); else memset(t->params.selector_mask, 0xFF, params->selector_size); return 0; error: free(t->params.selector_mask); t->params.selector_mask = NULL; return -ENOMEM; } static int group_set(struct table *t, uint32_t group_id, struct rte_swx_table_selector_group *group); void * rte_swx_table_selector_create(struct rte_swx_table_selector_params *params, struct rte_swx_table_selector_group **groups, int numa_node) { struct table *t = NULL; uint32_t group_size, i; int status; /* Check input arguments. */ status = table_create_check(params); if (status) goto error; /* Table object. */ t = calloc(1, sizeof(struct table)); if (!t) goto error; /* Parameter copy. */ status = table_params_copy(t, params); if (status) goto error; /* Group. */ group_size = params->n_members_per_group_max * sizeof(uint32_t); t->group_table_size = params->n_groups_max * group_size; t->group_table = env_calloc(t->group_table_size, RTE_CACHE_LINE_SIZE, numa_node); if (!t->group_table) goto error; t->members = calloc(params->n_members_per_group_max, sizeof(struct group_member_info)); if (!t->members) goto error; if (groups) for (i = 0; i < params->n_groups_max; i++) if (groups[i]) { status = group_set(t, i, groups[i]); if (status) goto error; } return t; error: rte_swx_table_selector_free(t); return NULL; } static int group_check(struct table *t, struct rte_swx_table_selector_group *group) { struct rte_swx_table_selector_member *elem; uint32_t n_members = 0; if (!group) return 0; TAILQ_FOREACH(elem, &group->members, node) { struct rte_swx_table_selector_member *e; uint32_t n = 0; /* Check group size. */ if (n_members >= t->params.n_members_per_group_max) return -ENOSPC; /* Check attributes of the current group member. */ if (elem->member_id >= t->params.n_members_per_group_max || !elem->member_weight) return -ENOSPC; /* Check against duplicate member IDs. */ TAILQ_FOREACH(e, &group->members, node) if (e->member_id == elem->member_id) n++; if (n != 1) return -EINVAL; /* Update group size. */ n_members++; } return 0; } static uint32_t members_read(struct group_member_info *members, struct rte_swx_table_selector_group *group) { struct rte_swx_table_selector_member *elem; uint32_t n_members = 0; if (!group) return 0; TAILQ_FOREACH(elem, &group->members, node) { struct group_member_info *m = &members[n_members]; memset(m, 0, sizeof(struct group_member_info)); m->member_id = elem->member_id; m->member_weight = elem->member_weight; m->member_weight_normalized = elem->member_weight; n_members++; } return n_members; } static uint32_t members_min_weight_find(struct group_member_info *members, uint32_t n_members) { uint32_t min = UINT32_MAX, i; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; if (m->member_weight < min) min = m->member_weight; } return min; } static uint32_t members_weight_divisor_check(struct group_member_info *members, uint32_t n_members, uint32_t divisor) { uint32_t i; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; if (m->member_weight_normalized % divisor) return 0; /* FALSE. */ } return 1; /* TRUE. */ } static void members_weight_divisor_apply(struct group_member_info *members, uint32_t n_members, uint32_t divisor) { uint32_t i; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; m->member_weight_normalized /= divisor; } } static uint32_t members_weight_sum(struct group_member_info *members, uint32_t n_members) { uint32_t result = 0, i; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; result += m->member_weight_normalized; } return result; } static void members_weight_scale(struct group_member_info *members, uint32_t n_members, uint32_t n_members_per_group_max, uint32_t weight_sum) { uint32_t multiplier, remainder, i; multiplier = n_members_per_group_max / weight_sum; remainder = n_members_per_group_max % weight_sum; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; m->count = m->member_weight_normalized * multiplier; } for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; uint32_t min; min = m->member_weight_normalized; if (remainder < m->member_weight_normalized) min = remainder; m->count += min; remainder -= min; if (!remainder) break; } } static void members_write(struct group_member_info *members, uint32_t n_members, uint32_t *group_table) { uint32_t pos = 0, i; for (i = 0; i < n_members; i++) { struct group_member_info *m = &members[i]; uint32_t j; for (j = 0; j < m->count; j++) group_table[pos++] = m->member_id; } } static int group_set(struct table *t, uint32_t group_id, struct rte_swx_table_selector_group *group) { uint32_t *gt = &t->group_table[group_id * t->params.n_members_per_group_max]; struct group_member_info *members = t->members; uint32_t n_members, weight_min, weight_sum, divisor; int status = 0; /* Check input arguments. */ if (group_id >= t->params.n_groups_max) return -EINVAL; status = group_check(t, group); if (status) return status; /* Read group members. */ n_members = members_read(members, group); if (!n_members) { memset(gt, 0, t->params.n_members_per_group_max * sizeof(uint32_t)); return 0; } /* Normalize weights. */ weight_min = members_min_weight_find(members, n_members); for (divisor = 2; divisor <= weight_min; divisor++) if (members_weight_divisor_check(members, n_members, divisor)) members_weight_divisor_apply(members, n_members, divisor); /* Scale weights. */ weight_sum = members_weight_sum(members, n_members); if (weight_sum > t->params.n_members_per_group_max) return -ENOSPC; members_weight_scale(members, n_members, t->params.n_members_per_group_max, weight_sum); /* Write group members to the group table. */ members_write(members, n_members, gt); return 0; } int rte_swx_table_selector_group_set(void *table, uint32_t group_id, struct rte_swx_table_selector_group *group) { struct table *t = table; return group_set(t, group_id, group); } struct mailbox { }; uint64_t rte_swx_table_selector_mailbox_size_get(void) { return sizeof(struct mailbox); } int rte_swx_table_selector_select(void *table, void *mailbox __rte_unused, uint8_t **group_id_buffer, uint8_t **selector_buffer, uint8_t **member_id_buffer) { struct table *t = table; uint32_t *group_id_ptr, *member_id_ptr, group_id, member_id, selector, group_member_index; group_id_ptr = (uint32_t *)&(*group_id_buffer)[t->params.group_id_offset]; member_id_ptr = (uint32_t *)&(*member_id_buffer)[t->params.member_id_offset]; group_id = *group_id_ptr & (t->params.n_groups_max - 1); selector = hash(&(*selector_buffer)[t->params.selector_offset], t->params.selector_mask, t->params.selector_size, 0); group_member_index = selector & (t->params.n_members_per_group_max - 1); member_id = t->group_table[(group_id << t->n_members_per_group_max_log2) + group_member_index]; *member_id_ptr = member_id; return 1; }