1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2// Copyright (c) 2020 Mellanox Technologies 3 4#include <linux/jhash.h> 5#include "mod_hdr.h" 6 7#define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) 8 9struct mod_hdr_key { 10 int num_actions; 11 void *actions; 12}; 13 14struct mlx5e_mod_hdr_handle { 15 /* a node of a hash table which keeps all the mod_hdr entries */ 16 struct hlist_node mod_hdr_hlist; 17 18 struct mod_hdr_key key; 19 20 struct mlx5_modify_hdr *modify_hdr; 21 22 refcount_t refcnt; 23 struct completion res_ready; 24 int compl_result; 25}; 26 27static u32 hash_mod_hdr_info(struct mod_hdr_key *key) 28{ 29 return jhash(key->actions, 30 key->num_actions * MLX5_MH_ACT_SZ, 0); 31} 32 33static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b) 34{ 35 if (a->num_actions != b->num_actions) 36 return 1; 37 38 return memcmp(a->actions, b->actions, 39 a->num_actions * MLX5_MH_ACT_SZ); 40} 41 42void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl) 43{ 44 mutex_init(&tbl->lock); 45 hash_init(tbl->hlist); 46} 47 48void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl) 49{ 50 WARN_ON(!hash_empty(tbl->hlist)); 51 mutex_destroy(&tbl->lock); 52} 53 54static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl, 55 struct mod_hdr_key *key, 56 u32 hash_key) 57{ 58 struct mlx5e_mod_hdr_handle *mh, *found = NULL; 59 60 hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) { 61 if (!cmp_mod_hdr_info(&mh->key, key)) { 62 refcount_inc(&mh->refcnt); 63 found = mh; 64 break; 65 } 66 } 67 68 return found; 69} 70 71struct mlx5e_mod_hdr_handle * 72mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev, 73 struct mod_hdr_tbl *tbl, 74 enum mlx5_flow_namespace_type namespace, 75 struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) 76{ 77 int num_actions, actions_size, err; 78 struct mlx5e_mod_hdr_handle *mh; 79 struct mod_hdr_key key; 80 u32 hash_key; 81 82 num_actions = mod_hdr_acts->num_actions; 83 actions_size = MLX5_MH_ACT_SZ * num_actions; 84 85 key.actions = mod_hdr_acts->actions; 86 key.num_actions = num_actions; 87 88 hash_key = hash_mod_hdr_info(&key); 89 90 mutex_lock(&tbl->lock); 91 mh = mod_hdr_get(tbl, &key, hash_key); 92 if (mh) { 93 mutex_unlock(&tbl->lock); 94 wait_for_completion(&mh->res_ready); 95 96 if (mh->compl_result < 0) { 97 err = -EREMOTEIO; 98 goto attach_header_err; 99 } 100 goto attach_header; 101 } 102 103 mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL); 104 if (!mh) { 105 mutex_unlock(&tbl->lock); 106 return ERR_PTR(-ENOMEM); 107 } 108 109 mh->key.actions = (void *)mh + sizeof(*mh); 110 memcpy(mh->key.actions, key.actions, actions_size); 111 mh->key.num_actions = num_actions; 112 refcount_set(&mh->refcnt, 1); 113 init_completion(&mh->res_ready); 114 115 hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); 116 mutex_unlock(&tbl->lock); 117 118 mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace, 119 mh->key.num_actions, 120 mh->key.actions); 121 if (IS_ERR(mh->modify_hdr)) { 122 err = PTR_ERR(mh->modify_hdr); 123 mh->compl_result = err; 124 goto alloc_header_err; 125 } 126 mh->compl_result = 1; 127 complete_all(&mh->res_ready); 128 129attach_header: 130 return mh; 131 132alloc_header_err: 133 complete_all(&mh->res_ready); 134attach_header_err: 135 mlx5e_mod_hdr_detach(mdev, tbl, mh); 136 return ERR_PTR(err); 137} 138 139void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev, 140 struct mod_hdr_tbl *tbl, 141 struct mlx5e_mod_hdr_handle *mh) 142{ 143 if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock)) 144 return; 145 hash_del(&mh->mod_hdr_hlist); 146 mutex_unlock(&tbl->lock); 147 148 if (mh->compl_result > 0) 149 mlx5_modify_header_dealloc(mdev, mh->modify_hdr); 150 151 kfree(mh); 152} 153 154struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh) 155{ 156 return mh->modify_hdr; 157} 158 159char * 160mlx5e_mod_hdr_alloc(struct mlx5_core_dev *mdev, int namespace, 161 struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) 162{ 163 int new_num_actions, max_hw_actions; 164 size_t new_sz, old_sz; 165 void *ret; 166 167 if (mod_hdr_acts->num_actions < mod_hdr_acts->max_actions) 168 goto out; 169 170 max_hw_actions = mlx5e_mod_hdr_max_actions(mdev, namespace); 171 new_num_actions = min(max_hw_actions, 172 mod_hdr_acts->actions ? 173 mod_hdr_acts->max_actions * 2 : 1); 174 if (mod_hdr_acts->max_actions == new_num_actions) 175 return ERR_PTR(-ENOSPC); 176 177 new_sz = MLX5_MH_ACT_SZ * new_num_actions; 178 old_sz = mod_hdr_acts->max_actions * MLX5_MH_ACT_SZ; 179 180 if (mod_hdr_acts->is_static) { 181 ret = kzalloc(new_sz, GFP_KERNEL); 182 if (ret) { 183 memcpy(ret, mod_hdr_acts->actions, old_sz); 184 mod_hdr_acts->is_static = false; 185 } 186 } else { 187 ret = krealloc(mod_hdr_acts->actions, new_sz, GFP_KERNEL); 188 if (ret) 189 memset(ret + old_sz, 0, new_sz - old_sz); 190 } 191 if (!ret) 192 return ERR_PTR(-ENOMEM); 193 194 mod_hdr_acts->actions = ret; 195 mod_hdr_acts->max_actions = new_num_actions; 196 197out: 198 return mod_hdr_acts->actions + (mod_hdr_acts->num_actions * MLX5_MH_ACT_SZ); 199} 200 201void 202mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) 203{ 204 if (!mod_hdr_acts->is_static) 205 kfree(mod_hdr_acts->actions); 206 207 mod_hdr_acts->actions = NULL; 208 mod_hdr_acts->num_actions = 0; 209 mod_hdr_acts->max_actions = 0; 210} 211 212char * 213mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts, int pos) 214{ 215 return mod_hdr_acts->actions + (pos * MLX5_MH_ACT_SZ); 216} 217