// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB // Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include "mlx5dv_dr.h" #include "dr_ste.h" enum dr_ptrn_modify_hdr_action_id { DR_PTRN_MODIFY_HDR_ACTION_ID_NOP = 0x00, DR_PTRN_MODIFY_HDR_ACTION_ID_COPY = 0x05, DR_PTRN_MODIFY_HDR_ACTION_ID_SET = 0x06, DR_PTRN_MODIFY_HDR_ACTION_ID_ADD = 0x07, DR_PTRN_MODIFY_HDR_ACTION_ID_INSERT_INLINE = 0x0a, }; struct dr_ptrn_mngr { struct mlx5dv_dr_domain *dmn; struct dr_icm_pool *ptrn_icm_pool; /* cache for modify_header ptrn */ struct list_head ptrn_list; pthread_mutex_t modify_hdr_mutex; }; int dr_ptrn_sync_pool(struct dr_ptrn_mngr *ptrn_mngr) { return dr_icm_pool_sync_pool(ptrn_mngr->ptrn_icm_pool); } /* Cache structure and functions */ static bool dr_ptrn_compare_modify_hdr(size_t cur_num_of_actions, __be64 cur_hw_actions[], size_t num_of_actions, __be64 hw_actions[]) { int i; if (cur_num_of_actions != num_of_actions) return false; for (i = 0; i < num_of_actions; i++) { u8 action_id = DEVX_GET(ste_double_action_add_v1, &hw_actions[i], action_id); if (action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_COPY) { if (hw_actions[i] != cur_hw_actions[i]) return false; } else { if ((__force __be32)hw_actions[i] != (__force __be32)cur_hw_actions[i]) return false; } } return true; } static bool dr_ptrn_compare_pattern(enum dr_ptrn_type type, enum dr_ptrn_type cur_type, size_t cur_num_of_actions, __be64 cur_hw_action[], size_t num_of_actions, __be64 hw_action[]) { if ((cur_num_of_actions != num_of_actions) || (cur_type != type)) return false; switch (type) { case DR_PTRN_TYP_MODIFY_HDR: return dr_ptrn_compare_modify_hdr(cur_num_of_actions, (__be64 *)cur_hw_action, num_of_actions, (__be64 *)hw_action); case DR_PTRN_TYP_TNL_L3_TO_L2: return true; default: assert(false); return false; } } static struct dr_ptrn_obj * dr_ptrn_find_cached_pattern(struct dr_ptrn_mngr *mngr, enum dr_ptrn_type type, size_t num_of_actions, __be64 hw_actions[]) { struct dr_ptrn_obj *tmp; struct dr_ptrn_obj *cached_pattern; list_for_each_safe(&mngr->ptrn_list, cached_pattern, tmp, list) { if (dr_ptrn_compare_pattern(type, cached_pattern->type, cached_pattern->rewrite_param.num_of_actions, (__be64 *)cached_pattern->rewrite_param.data, num_of_actions, hw_actions)) { list_del(&cached_pattern->list); list_add(&mngr->ptrn_list, &cached_pattern->list); return cached_pattern; } } return NULL; } static struct dr_ptrn_obj * dr_ptrn_alloc_pattern(struct dr_ptrn_mngr *mngr, uint16_t num_of_actions, uint8_t *data, enum dr_ptrn_type type) { struct dr_ptrn_obj *pattern; struct dr_icm_chunk *chunk; uint32_t chunck_size; uint32_t index; chunck_size = ilog32(num_of_actions - 1); /* HW modify action index granularity is at least 64B */ chunck_size = max_t(uint32_t, chunck_size, DR_CHUNK_SIZE_8); chunk = dr_icm_alloc_chunk(mngr->ptrn_icm_pool, chunck_size); if (!chunk) { errno = ENOMEM; return NULL; } index = (dr_icm_pool_get_chunk_icm_addr(chunk) - mngr->dmn->info.caps.hdr_modify_pattern_icm_addr) / ACTION_CACHE_LINE_SIZE; pattern = calloc(1, sizeof(struct dr_ptrn_obj)); if (!pattern) { errno = ENOMEM; goto free_chunk; } pattern->rewrite_param.data = calloc(1, num_of_actions * DR_MODIFY_ACTION_SIZE); if (!pattern->rewrite_param.data) { errno = ENOMEM; goto free_pattern; } pattern->type = type; memcpy(pattern->rewrite_param.data, data, num_of_actions * DR_MODIFY_ACTION_SIZE); pattern->rewrite_param.chunk = chunk; pattern->rewrite_param.index = index; pattern->rewrite_param.num_of_actions = num_of_actions; list_add(&mngr->ptrn_list, &pattern->list); atomic_init(&pattern->refcount, 0); return pattern; free_pattern: free(pattern); free_chunk: dr_icm_free_chunk(chunk); return NULL; } static void dr_ptrn_free_pattern(struct dr_ptrn_obj *pattern) { list_del(&pattern->list); dr_icm_free_chunk(pattern->rewrite_param.chunk); free(pattern->rewrite_param.data); free(pattern); } struct dr_ptrn_obj * dr_ptrn_cache_get_pattern(struct dr_ptrn_mngr *mngr, enum dr_ptrn_type type, uint16_t num_of_actions, uint8_t *data) { struct dr_ptrn_obj *pattern; uint64_t *hw_actions; uint8_t action_id; int i; pthread_mutex_lock(&mngr->modify_hdr_mutex); pattern = dr_ptrn_find_cached_pattern(mngr, type, num_of_actions, (__be64 *)data); if (!pattern) { /* Alloc and add new pattern to cache */ pattern = dr_ptrn_alloc_pattern(mngr, num_of_actions, data, type); if (!pattern) goto out_unlock; hw_actions = (uint64_t *)pattern->rewrite_param.data; /* Here we mask the pattern data to create a valid pattern * since we do an OR operation between the arg and pattern */ for (i = 0; i < num_of_actions; i++) { action_id = DR_STE_GET(double_action_add_v1, &hw_actions[i], action_id); if (action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_SET || action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_ADD || action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_INSERT_INLINE) DR_STE_SET(double_action_set_v1, &hw_actions[i], inline_data, 0); } if (dr_send_postsend_pattern(mngr->dmn, pattern->rewrite_param.chunk, num_of_actions, pattern->rewrite_param.data)) goto free_pattern; } atomic_fetch_add(&pattern->refcount, 1); pthread_mutex_unlock(&mngr->modify_hdr_mutex); return pattern; free_pattern: dr_ptrn_free_pattern(pattern); out_unlock: pthread_mutex_unlock(&mngr->modify_hdr_mutex); return NULL; } void dr_ptrn_cache_put_pattern(struct dr_ptrn_mngr *mngr, struct dr_ptrn_obj *pattern) { pthread_mutex_lock(&mngr->modify_hdr_mutex); if (atomic_fetch_sub(&pattern->refcount, 1) != 1) goto out; dr_ptrn_free_pattern(pattern); out: pthread_mutex_unlock(&mngr->modify_hdr_mutex); } struct dr_ptrn_mngr * dr_ptrn_mngr_create(struct mlx5dv_dr_domain *dmn) { struct dr_ptrn_mngr *mngr; if (!dr_domain_is_support_modify_hdr_cache(dmn)) return NULL; mngr = calloc(1, sizeof(*mngr)); if (!mngr) { errno = ENOMEM; return NULL; } mngr->dmn = dmn; mngr->ptrn_icm_pool = dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_HDR_PTRN); if (!mngr->ptrn_icm_pool) { dr_dbg(dmn, "Couldn't get modify-header-pattern memory for %s\n", ibv_get_device_name(dmn->ctx->device)); goto free_mngr; } list_head_init(&mngr->ptrn_list); return mngr; free_mngr: free(mngr); return NULL; } void dr_ptrn_mngr_destroy(struct dr_ptrn_mngr *mngr) { struct dr_ptrn_obj *tmp; struct dr_ptrn_obj *pattern; if (!mngr) return; list_for_each_safe(&mngr->ptrn_list, pattern, tmp, list) { list_del(&pattern->list); free(pattern->rewrite_param.data); free(pattern); } dr_icm_pool_destroy(mngr->ptrn_icm_pool); free(mngr); }