| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| // Copyright (c) 2020 Mellanox Technologies |
| |
| #include <linux/jhash.h> |
| #include "mod_hdr.h" |
| |
| #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) |
| |
| struct mod_hdr_key { |
| int num_actions; |
| void *actions; |
| }; |
| |
| struct mlx5e_mod_hdr_handle { |
| /* a node of a hash table which keeps all the mod_hdr entries */ |
| struct hlist_node mod_hdr_hlist; |
| |
| struct mod_hdr_key key; |
| |
| struct mlx5_modify_hdr *modify_hdr; |
| |
| refcount_t refcnt; |
| struct completion res_ready; |
| int compl_result; |
| }; |
| |
| static u32 hash_mod_hdr_info(struct mod_hdr_key *key) |
| { |
| return jhash(key->actions, |
| key->num_actions * MLX5_MH_ACT_SZ, 0); |
| } |
| |
| static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b) |
| { |
| if (a->num_actions != b->num_actions) |
| return 1; |
| |
| return memcmp(a->actions, b->actions, |
| a->num_actions * MLX5_MH_ACT_SZ); |
| } |
| |
| void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl) |
| { |
| mutex_init(&tbl->lock); |
| hash_init(tbl->hlist); |
| } |
| |
| void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl) |
| { |
| mutex_destroy(&tbl->lock); |
| } |
| |
| static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl, |
| struct mod_hdr_key *key, |
| u32 hash_key) |
| { |
| struct mlx5e_mod_hdr_handle *mh, *found = NULL; |
| |
| hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) { |
| if (!cmp_mod_hdr_info(&mh->key, key)) { |
| refcount_inc(&mh->refcnt); |
| found = mh; |
| break; |
| } |
| } |
| |
| return found; |
| } |
| |
| struct mlx5e_mod_hdr_handle * |
| mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev, |
| struct mod_hdr_tbl *tbl, |
| enum mlx5_flow_namespace_type namespace, |
| struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) |
| { |
| int num_actions, actions_size, err; |
| struct mlx5e_mod_hdr_handle *mh; |
| struct mod_hdr_key key; |
| u32 hash_key; |
| |
| num_actions = mod_hdr_acts->num_actions; |
| actions_size = MLX5_MH_ACT_SZ * num_actions; |
| |
| key.actions = mod_hdr_acts->actions; |
| key.num_actions = num_actions; |
| |
| hash_key = hash_mod_hdr_info(&key); |
| |
| mutex_lock(&tbl->lock); |
| mh = mod_hdr_get(tbl, &key, hash_key); |
| if (mh) { |
| mutex_unlock(&tbl->lock); |
| wait_for_completion(&mh->res_ready); |
| |
| if (mh->compl_result < 0) { |
| err = -EREMOTEIO; |
| goto attach_header_err; |
| } |
| goto attach_header; |
| } |
| |
| mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL); |
| if (!mh) { |
| mutex_unlock(&tbl->lock); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| mh->key.actions = (void *)mh + sizeof(*mh); |
| memcpy(mh->key.actions, key.actions, actions_size); |
| mh->key.num_actions = num_actions; |
| refcount_set(&mh->refcnt, 1); |
| init_completion(&mh->res_ready); |
| |
| hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); |
| mutex_unlock(&tbl->lock); |
| |
| mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace, |
| mh->key.num_actions, |
| mh->key.actions); |
| if (IS_ERR(mh->modify_hdr)) { |
| err = PTR_ERR(mh->modify_hdr); |
| mh->compl_result = err; |
| goto alloc_header_err; |
| } |
| mh->compl_result = 1; |
| complete_all(&mh->res_ready); |
| |
| attach_header: |
| return mh; |
| |
| alloc_header_err: |
| complete_all(&mh->res_ready); |
| attach_header_err: |
| mlx5e_mod_hdr_detach(mdev, tbl, mh); |
| return ERR_PTR(err); |
| } |
| |
| void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev, |
| struct mod_hdr_tbl *tbl, |
| struct mlx5e_mod_hdr_handle *mh) |
| { |
| if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock)) |
| return; |
| hash_del(&mh->mod_hdr_hlist); |
| mutex_unlock(&tbl->lock); |
| |
| if (mh->compl_result > 0) |
| mlx5_modify_header_dealloc(mdev, mh->modify_hdr); |
| |
| kfree(mh); |
| } |
| |
| struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh) |
| { |
| return mh->modify_hdr; |
| } |
| |