129cf8febSAlex Vesker // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
229cf8febSAlex Vesker /* Copyright (c) 2019 Mellanox Technologies. */
329cf8febSAlex Vesker 
429cf8febSAlex Vesker #include "dr_types.h"
529cf8febSAlex Vesker 
629cf8febSAlex Vesker #define DR_ICM_MODIFY_HDR_ALIGN_BASE 64
772b2cff6SYevgeny Kliteynik #define DR_ICM_POOL_STE_HOT_MEM_PERCENT 25
872b2cff6SYevgeny Kliteynik #define DR_ICM_POOL_MODIFY_HDR_PTRN_HOT_MEM_PERCENT 50
972b2cff6SYevgeny Kliteynik #define DR_ICM_POOL_MODIFY_ACTION_HOT_MEM_PERCENT 90
1029cf8febSAlex Vesker 
114519fc45SYevgeny Kliteynik struct mlx5dr_icm_hot_chunk {
124519fc45SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy_mem;
134519fc45SYevgeny Kliteynik 	unsigned int seg;
144519fc45SYevgeny Kliteynik 	enum mlx5dr_icm_chunk_size size;
154519fc45SYevgeny Kliteynik };
164519fc45SYevgeny Kliteynik 
1729cf8febSAlex Vesker struct mlx5dr_icm_pool {
1829cf8febSAlex Vesker 	enum mlx5dr_icm_type icm_type;
1929cf8febSAlex Vesker 	enum mlx5dr_icm_chunk_size max_log_chunk_sz;
2029cf8febSAlex Vesker 	struct mlx5dr_domain *dmn;
21fd785e52SYevgeny Kliteynik 	struct kmem_cache *chunks_kmem_cache;
22fd785e52SYevgeny Kliteynik 
23a00cd878SYevgeny Kliteynik 	/* memory management */
24a00cd878SYevgeny Kliteynik 	struct mutex mutex; /* protect the ICM pool and ICM buddy */
25a00cd878SYevgeny Kliteynik 	struct list_head buddy_mem_list;
264519fc45SYevgeny Kliteynik 
274519fc45SYevgeny Kliteynik 	/* Hardware may be accessing this memory but at some future,
284519fc45SYevgeny Kliteynik 	 * undetermined time, it might cease to do so.
294519fc45SYevgeny Kliteynik 	 * sync_ste command sets them free.
304519fc45SYevgeny Kliteynik 	 */
314519fc45SYevgeny Kliteynik 	struct mlx5dr_icm_hot_chunk *hot_chunks_arr;
324519fc45SYevgeny Kliteynik 	u32 hot_chunks_num;
331c586514SYevgeny Kliteynik 	u64 hot_memory_size;
3472b2cff6SYevgeny Kliteynik 	/* hot memory size threshold for triggering sync */
3572b2cff6SYevgeny Kliteynik 	u64 th;
3629cf8febSAlex Vesker };
3729cf8febSAlex Vesker 
3829cf8febSAlex Vesker struct mlx5dr_icm_dm {
3929cf8febSAlex Vesker 	u32 obj_id;
4029cf8febSAlex Vesker 	enum mlx5_sw_icm_type type;
41334a306fSNathan Chancellor 	phys_addr_t addr;
4229cf8febSAlex Vesker 	size_t length;
4329cf8febSAlex Vesker };
4429cf8febSAlex Vesker 
4529cf8febSAlex Vesker struct mlx5dr_icm_mr {
4683fec3f1SAharon Landau 	u32 mkey;
4729cf8febSAlex Vesker 	struct mlx5dr_icm_dm dm;
48a00cd878SYevgeny Kliteynik 	struct mlx5dr_domain *dmn;
4929cf8febSAlex Vesker 	size_t length;
5029cf8febSAlex Vesker 	u64 icm_start_addr;
5129cf8febSAlex Vesker };
5229cf8febSAlex Vesker 
dr_icm_create_dm_mkey(struct mlx5_core_dev * mdev,u32 pd,u64 length,u64 start_addr,int mode,u32 * mkey)5329cf8febSAlex Vesker static int dr_icm_create_dm_mkey(struct mlx5_core_dev *mdev,
5429cf8febSAlex Vesker 				 u32 pd, u64 length, u64 start_addr, int mode,
5583fec3f1SAharon Landau 				 u32 *mkey)
5629cf8febSAlex Vesker {
5729cf8febSAlex Vesker 	u32 inlen = MLX5_ST_SZ_BYTES(create_mkey_in);
5829cf8febSAlex Vesker 	u32 in[MLX5_ST_SZ_DW(create_mkey_in)] = {};
5929cf8febSAlex Vesker 	void *mkc;
6029cf8febSAlex Vesker 
6129cf8febSAlex Vesker 	mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
6229cf8febSAlex Vesker 
6329cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, access_mode_1_0, mode);
6429cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, access_mode_4_2, (mode >> 2) & 0x7);
6529cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, lw, 1);
6629cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, lr, 1);
6729cf8febSAlex Vesker 	if (mode == MLX5_MKC_ACCESS_MODE_SW_ICM) {
6829cf8febSAlex Vesker 		MLX5_SET(mkc, mkc, rw, 1);
6929cf8febSAlex Vesker 		MLX5_SET(mkc, mkc, rr, 1);
7029cf8febSAlex Vesker 	}
7129cf8febSAlex Vesker 
7229cf8febSAlex Vesker 	MLX5_SET64(mkc, mkc, len, length);
7329cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, pd, pd);
7429cf8febSAlex Vesker 	MLX5_SET(mkc, mkc, qpn, 0xffffff);
7529cf8febSAlex Vesker 	MLX5_SET64(mkc, mkc, start_addr, start_addr);
7629cf8febSAlex Vesker 
7729cf8febSAlex Vesker 	return mlx5_core_create_mkey(mdev, mkey, in, inlen);
7829cf8febSAlex Vesker }
7929cf8febSAlex Vesker 
mlx5dr_icm_pool_get_chunk_mr_addr(struct mlx5dr_icm_chunk * chunk)80003f4f9aSRongwei Liu u64 mlx5dr_icm_pool_get_chunk_mr_addr(struct mlx5dr_icm_chunk *chunk)
81003f4f9aSRongwei Liu {
82003f4f9aSRongwei Liu 	u32 offset = mlx5dr_icm_pool_dm_type_to_entry_size(chunk->buddy_mem->pool->icm_type);
83003f4f9aSRongwei Liu 
84003f4f9aSRongwei Liu 	return (u64)offset * chunk->seg;
85003f4f9aSRongwei Liu }
86003f4f9aSRongwei Liu 
mlx5dr_icm_pool_get_chunk_rkey(struct mlx5dr_icm_chunk * chunk)87003f4f9aSRongwei Liu u32 mlx5dr_icm_pool_get_chunk_rkey(struct mlx5dr_icm_chunk *chunk)
88003f4f9aSRongwei Liu {
89003f4f9aSRongwei Liu 	return chunk->buddy_mem->icm_mr->mkey;
90003f4f9aSRongwei Liu }
91003f4f9aSRongwei Liu 
mlx5dr_icm_pool_get_chunk_icm_addr(struct mlx5dr_icm_chunk * chunk)925c4f9b6eSRongwei Liu u64 mlx5dr_icm_pool_get_chunk_icm_addr(struct mlx5dr_icm_chunk *chunk)
935c4f9b6eSRongwei Liu {
945c4f9b6eSRongwei Liu 	u32 size = mlx5dr_icm_pool_dm_type_to_entry_size(chunk->buddy_mem->pool->icm_type);
955c4f9b6eSRongwei Liu 
965c4f9b6eSRongwei Liu 	return (u64)chunk->buddy_mem->icm_mr->icm_start_addr + size * chunk->seg;
975c4f9b6eSRongwei Liu }
985c4f9b6eSRongwei Liu 
mlx5dr_icm_pool_get_chunk_byte_size(struct mlx5dr_icm_chunk * chunk)99f51bb517SRongwei Liu u32 mlx5dr_icm_pool_get_chunk_byte_size(struct mlx5dr_icm_chunk *chunk)
100f51bb517SRongwei Liu {
101f51bb517SRongwei Liu 	return mlx5dr_icm_pool_chunk_size_to_byte(chunk->size,
102f51bb517SRongwei Liu 			chunk->buddy_mem->pool->icm_type);
103f51bb517SRongwei Liu }
104f51bb517SRongwei Liu 
mlx5dr_icm_pool_get_chunk_num_of_entries(struct mlx5dr_icm_chunk * chunk)105f51bb517SRongwei Liu u32 mlx5dr_icm_pool_get_chunk_num_of_entries(struct mlx5dr_icm_chunk *chunk)
106f51bb517SRongwei Liu {
107f51bb517SRongwei Liu 	return mlx5dr_icm_pool_chunk_size_to_entries(chunk->size);
108f51bb517SRongwei Liu }
109f51bb517SRongwei Liu 
11029cf8febSAlex Vesker static struct mlx5dr_icm_mr *
dr_icm_pool_mr_create(struct mlx5dr_icm_pool * pool)111dff8e2d1SErez Shitrit dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool)
11229cf8febSAlex Vesker {
11329cf8febSAlex Vesker 	struct mlx5_core_dev *mdev = pool->dmn->mdev;
114108ff821SYevgeny Kliteynik 	enum mlx5_sw_icm_type dm_type = 0;
11529cf8febSAlex Vesker 	struct mlx5dr_icm_mr *icm_mr;
116108ff821SYevgeny Kliteynik 	size_t log_align_base = 0;
11729cf8febSAlex Vesker 	int err;
11829cf8febSAlex Vesker 
11929cf8febSAlex Vesker 	icm_mr = kvzalloc(sizeof(*icm_mr), GFP_KERNEL);
12029cf8febSAlex Vesker 	if (!icm_mr)
12129cf8febSAlex Vesker 		return NULL;
12229cf8febSAlex Vesker 
123a00cd878SYevgeny Kliteynik 	icm_mr->dmn = pool->dmn;
12429cf8febSAlex Vesker 
12529cf8febSAlex Vesker 	icm_mr->dm.length = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
126dff8e2d1SErez Shitrit 							       pool->icm_type);
12729cf8febSAlex Vesker 
128108ff821SYevgeny Kliteynik 	switch (pool->icm_type) {
129108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_STE:
130dff8e2d1SErez Shitrit 		dm_type = MLX5_SW_ICM_TYPE_STEERING;
131dff8e2d1SErez Shitrit 		log_align_base = ilog2(icm_mr->dm.length);
132108ff821SYevgeny Kliteynik 		break;
133108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_MODIFY_ACTION:
134dff8e2d1SErez Shitrit 		dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY;
135dff8e2d1SErez Shitrit 		/* Align base is 64B */
136dff8e2d1SErez Shitrit 		log_align_base = ilog2(DR_ICM_MODIFY_HDR_ALIGN_BASE);
137108ff821SYevgeny Kliteynik 		break;
138108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_MODIFY_HDR_PTRN:
139108ff821SYevgeny Kliteynik 		dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY_PATTERN;
140108ff821SYevgeny Kliteynik 		/* Align base is 64B */
141108ff821SYevgeny Kliteynik 		log_align_base = ilog2(DR_ICM_MODIFY_HDR_ALIGN_BASE);
142108ff821SYevgeny Kliteynik 		break;
143108ff821SYevgeny Kliteynik 	default:
144108ff821SYevgeny Kliteynik 		WARN_ON(pool->icm_type);
145dff8e2d1SErez Shitrit 	}
146108ff821SYevgeny Kliteynik 
147dff8e2d1SErez Shitrit 	icm_mr->dm.type = dm_type;
148dff8e2d1SErez Shitrit 
149dff8e2d1SErez Shitrit 	err = mlx5_dm_sw_icm_alloc(mdev, icm_mr->dm.type, icm_mr->dm.length,
150dff8e2d1SErez Shitrit 				   log_align_base, 0, &icm_mr->dm.addr,
151dff8e2d1SErez Shitrit 				   &icm_mr->dm.obj_id);
15229cf8febSAlex Vesker 	if (err) {
15329cf8febSAlex Vesker 		mlx5dr_err(pool->dmn, "Failed to allocate SW ICM memory, err (%d)\n", err);
15429cf8febSAlex Vesker 		goto free_icm_mr;
15529cf8febSAlex Vesker 	}
15629cf8febSAlex Vesker 
15729cf8febSAlex Vesker 	/* Register device memory */
15829cf8febSAlex Vesker 	err = dr_icm_create_dm_mkey(mdev, pool->dmn->pdn,
15929cf8febSAlex Vesker 				    icm_mr->dm.length,
16029cf8febSAlex Vesker 				    icm_mr->dm.addr,
16129cf8febSAlex Vesker 				    MLX5_MKC_ACCESS_MODE_SW_ICM,
16229cf8febSAlex Vesker 				    &icm_mr->mkey);
16329cf8febSAlex Vesker 	if (err) {
16429cf8febSAlex Vesker 		mlx5dr_err(pool->dmn, "Failed to create SW ICM MKEY, err (%d)\n", err);
16529cf8febSAlex Vesker 		goto free_dm;
16629cf8febSAlex Vesker 	}
16729cf8febSAlex Vesker 
16829cf8febSAlex Vesker 	icm_mr->icm_start_addr = icm_mr->dm.addr;
16929cf8febSAlex Vesker 
170dff8e2d1SErez Shitrit 	if (icm_mr->icm_start_addr & (BIT(log_align_base) - 1)) {
171dff8e2d1SErez Shitrit 		mlx5dr_err(pool->dmn, "Failed to get Aligned ICM mem (asked: %zu)\n",
172dff8e2d1SErez Shitrit 			   log_align_base);
173dff8e2d1SErez Shitrit 		goto free_mkey;
174dff8e2d1SErez Shitrit 	}
17529cf8febSAlex Vesker 
17629cf8febSAlex Vesker 	return icm_mr;
17729cf8febSAlex Vesker 
178dff8e2d1SErez Shitrit free_mkey:
17983fec3f1SAharon Landau 	mlx5_core_destroy_mkey(mdev, icm_mr->mkey);
18029cf8febSAlex Vesker free_dm:
18129cf8febSAlex Vesker 	mlx5_dm_sw_icm_dealloc(mdev, icm_mr->dm.type, icm_mr->dm.length, 0,
18229cf8febSAlex Vesker 			       icm_mr->dm.addr, icm_mr->dm.obj_id);
18329cf8febSAlex Vesker free_icm_mr:
18429cf8febSAlex Vesker 	kvfree(icm_mr);
18529cf8febSAlex Vesker 	return NULL;
18629cf8febSAlex Vesker }
18729cf8febSAlex Vesker 
dr_icm_pool_mr_destroy(struct mlx5dr_icm_mr * icm_mr)18829cf8febSAlex Vesker static void dr_icm_pool_mr_destroy(struct mlx5dr_icm_mr *icm_mr)
18929cf8febSAlex Vesker {
190a00cd878SYevgeny Kliteynik 	struct mlx5_core_dev *mdev = icm_mr->dmn->mdev;
19129cf8febSAlex Vesker 	struct mlx5dr_icm_dm *dm = &icm_mr->dm;
19229cf8febSAlex Vesker 
19383fec3f1SAharon Landau 	mlx5_core_destroy_mkey(mdev, icm_mr->mkey);
19429cf8febSAlex Vesker 	mlx5_dm_sw_icm_dealloc(mdev, dm->type, dm->length, 0,
19529cf8febSAlex Vesker 			       dm->addr, dm->obj_id);
19629cf8febSAlex Vesker 	kvfree(icm_mr);
19729cf8febSAlex Vesker }
19829cf8febSAlex Vesker 
dr_icm_buddy_get_ste_size(struct mlx5dr_icm_buddy_mem * buddy)199e5b2bc30SYevgeny Kliteynik static int dr_icm_buddy_get_ste_size(struct mlx5dr_icm_buddy_mem *buddy)
20029cf8febSAlex Vesker {
201e5b2bc30SYevgeny Kliteynik 	/* We support only one type of STE size, both for ConnectX-5 and later
202e5b2bc30SYevgeny Kliteynik 	 * devices. Once the support for match STE which has a larger tag is
203e5b2bc30SYevgeny Kliteynik 	 * added (32B instead of 16B), the STE size for devices later than
204e5b2bc30SYevgeny Kliteynik 	 * ConnectX-5 needs to account for that.
205e5b2bc30SYevgeny Kliteynik 	 */
206e5b2bc30SYevgeny Kliteynik 	return DR_STE_SIZE_REDUCED;
207e5b2bc30SYevgeny Kliteynik }
20829cf8febSAlex Vesker 
dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk * chunk,int offset)209e5b2bc30SYevgeny Kliteynik static void dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk *chunk, int offset)
210e5b2bc30SYevgeny Kliteynik {
21106ab4a40SYevgeny Kliteynik 	int num_of_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk);
212e5b2bc30SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem;
21306ab4a40SYevgeny Kliteynik 	int ste_size = dr_icm_buddy_get_ste_size(buddy);
214e5b2bc30SYevgeny Kliteynik 	int index = offset / DR_STE_SIZE;
21529cf8febSAlex Vesker 
216e5b2bc30SYevgeny Kliteynik 	chunk->ste_arr = &buddy->ste_arr[index];
217e5b2bc30SYevgeny Kliteynik 	chunk->miss_list = &buddy->miss_list[index];
21806ab4a40SYevgeny Kliteynik 	chunk->hw_ste_arr = buddy->hw_ste_arr + index * ste_size;
21929cf8febSAlex Vesker 
22006ab4a40SYevgeny Kliteynik 	memset(chunk->hw_ste_arr, 0, num_of_entries * ste_size);
221e5b2bc30SYevgeny Kliteynik 	memset(chunk->ste_arr, 0,
222f51bb517SRongwei Liu 	       num_of_entries * sizeof(chunk->ste_arr[0]));
22329cf8febSAlex Vesker }
22429cf8febSAlex Vesker 
dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem * buddy)225e5b2bc30SYevgeny Kliteynik static int dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem *buddy)
226e5b2bc30SYevgeny Kliteynik {
227e5b2bc30SYevgeny Kliteynik 	int num_of_entries =
228e5b2bc30SYevgeny Kliteynik 		mlx5dr_icm_pool_chunk_size_to_entries(buddy->pool->max_log_chunk_sz);
229e5b2bc30SYevgeny Kliteynik 
230e5b2bc30SYevgeny Kliteynik 	buddy->ste_arr = kvcalloc(num_of_entries,
231e5b2bc30SYevgeny Kliteynik 				  sizeof(struct mlx5dr_ste), GFP_KERNEL);
232e5b2bc30SYevgeny Kliteynik 	if (!buddy->ste_arr)
233e5b2bc30SYevgeny Kliteynik 		return -ENOMEM;
234e5b2bc30SYevgeny Kliteynik 
235e5b2bc30SYevgeny Kliteynik 	/* Preallocate full STE size on non-ConnectX-5 devices since
236e5b2bc30SYevgeny Kliteynik 	 * we need to support both full and reduced with the same cache.
237e5b2bc30SYevgeny Kliteynik 	 */
238e5b2bc30SYevgeny Kliteynik 	buddy->hw_ste_arr = kvcalloc(num_of_entries,
239e5b2bc30SYevgeny Kliteynik 				     dr_icm_buddy_get_ste_size(buddy), GFP_KERNEL);
240e5b2bc30SYevgeny Kliteynik 	if (!buddy->hw_ste_arr)
241e5b2bc30SYevgeny Kliteynik 		goto free_ste_arr;
242e5b2bc30SYevgeny Kliteynik 
243e5b2bc30SYevgeny Kliteynik 	buddy->miss_list = kvmalloc(num_of_entries * sizeof(struct list_head), GFP_KERNEL);
244e5b2bc30SYevgeny Kliteynik 	if (!buddy->miss_list)
245e5b2bc30SYevgeny Kliteynik 		goto free_hw_ste_arr;
246e5b2bc30SYevgeny Kliteynik 
247e5b2bc30SYevgeny Kliteynik 	return 0;
248e5b2bc30SYevgeny Kliteynik 
249e5b2bc30SYevgeny Kliteynik free_hw_ste_arr:
250e5b2bc30SYevgeny Kliteynik 	kvfree(buddy->hw_ste_arr);
251e5b2bc30SYevgeny Kliteynik free_ste_arr:
252e5b2bc30SYevgeny Kliteynik 	kvfree(buddy->ste_arr);
253e5b2bc30SYevgeny Kliteynik 	return -ENOMEM;
254e5b2bc30SYevgeny Kliteynik }
255e5b2bc30SYevgeny Kliteynik 
dr_icm_buddy_cleanup_ste_cache(struct mlx5dr_icm_buddy_mem * buddy)256e5b2bc30SYevgeny Kliteynik static void dr_icm_buddy_cleanup_ste_cache(struct mlx5dr_icm_buddy_mem *buddy)
257e5b2bc30SYevgeny Kliteynik {
258e5b2bc30SYevgeny Kliteynik 	kvfree(buddy->ste_arr);
259e5b2bc30SYevgeny Kliteynik 	kvfree(buddy->hw_ste_arr);
260e5b2bc30SYevgeny Kliteynik 	kvfree(buddy->miss_list);
261e5b2bc30SYevgeny Kliteynik }
262e5b2bc30SYevgeny Kliteynik 
dr_icm_buddy_create(struct mlx5dr_icm_pool * pool)263a00cd878SYevgeny Kliteynik static int dr_icm_buddy_create(struct mlx5dr_icm_pool *pool)
26429cf8febSAlex Vesker {
265a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy;
266a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_mr *icm_mr;
26729cf8febSAlex Vesker 
268a00cd878SYevgeny Kliteynik 	icm_mr = dr_icm_pool_mr_create(pool);
269a00cd878SYevgeny Kliteynik 	if (!icm_mr)
270a00cd878SYevgeny Kliteynik 		return -ENOMEM;
271a00cd878SYevgeny Kliteynik 
272a00cd878SYevgeny Kliteynik 	buddy = kvzalloc(sizeof(*buddy), GFP_KERNEL);
273a00cd878SYevgeny Kliteynik 	if (!buddy)
274a00cd878SYevgeny Kliteynik 		goto free_mr;
275a00cd878SYevgeny Kliteynik 
276a00cd878SYevgeny Kliteynik 	if (mlx5dr_buddy_init(buddy, pool->max_log_chunk_sz))
277a00cd878SYevgeny Kliteynik 		goto err_free_buddy;
278a00cd878SYevgeny Kliteynik 
279a00cd878SYevgeny Kliteynik 	buddy->icm_mr = icm_mr;
280a00cd878SYevgeny Kliteynik 	buddy->pool = pool;
281a00cd878SYevgeny Kliteynik 
282e5b2bc30SYevgeny Kliteynik 	if (pool->icm_type == DR_ICM_TYPE_STE) {
283e5b2bc30SYevgeny Kliteynik 		/* Reduce allocations by preallocating and reusing the STE structures */
284e5b2bc30SYevgeny Kliteynik 		if (dr_icm_buddy_init_ste_cache(buddy))
285e5b2bc30SYevgeny Kliteynik 			goto err_cleanup_buddy;
286e5b2bc30SYevgeny Kliteynik 	}
287e5b2bc30SYevgeny Kliteynik 
288a00cd878SYevgeny Kliteynik 	/* add it to the -start- of the list in order to search in it first */
289a00cd878SYevgeny Kliteynik 	list_add(&buddy->list_node, &pool->buddy_mem_list);
290a00cd878SYevgeny Kliteynik 
291*57295e06SYevgeny Kliteynik 	pool->dmn->num_buddies[pool->icm_type]++;
292*57295e06SYevgeny Kliteynik 
293a00cd878SYevgeny Kliteynik 	return 0;
294a00cd878SYevgeny Kliteynik 
295e5b2bc30SYevgeny Kliteynik err_cleanup_buddy:
296e5b2bc30SYevgeny Kliteynik 	mlx5dr_buddy_cleanup(buddy);
297a00cd878SYevgeny Kliteynik err_free_buddy:
298a00cd878SYevgeny Kliteynik 	kvfree(buddy);
299a00cd878SYevgeny Kliteynik free_mr:
300a00cd878SYevgeny Kliteynik 	dr_icm_pool_mr_destroy(icm_mr);
301a00cd878SYevgeny Kliteynik 	return -ENOMEM;
30229cf8febSAlex Vesker }
30329cf8febSAlex Vesker 
dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem * buddy)304a00cd878SYevgeny Kliteynik static void dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem *buddy)
30529cf8febSAlex Vesker {
306*57295e06SYevgeny Kliteynik 	enum mlx5dr_icm_type icm_type = buddy->pool->icm_type;
307*57295e06SYevgeny Kliteynik 
308a00cd878SYevgeny Kliteynik 	dr_icm_pool_mr_destroy(buddy->icm_mr);
309a00cd878SYevgeny Kliteynik 
310a00cd878SYevgeny Kliteynik 	mlx5dr_buddy_cleanup(buddy);
311a00cd878SYevgeny Kliteynik 
312*57295e06SYevgeny Kliteynik 	if (icm_type == DR_ICM_TYPE_STE)
313e5b2bc30SYevgeny Kliteynik 		dr_icm_buddy_cleanup_ste_cache(buddy);
314e5b2bc30SYevgeny Kliteynik 
315*57295e06SYevgeny Kliteynik 	buddy->pool->dmn->num_buddies[icm_type]--;
316*57295e06SYevgeny Kliteynik 
317a00cd878SYevgeny Kliteynik 	kvfree(buddy);
31829cf8febSAlex Vesker }
31929cf8febSAlex Vesker 
320edaea001SYevgeny Kliteynik static void
dr_icm_chunk_init(struct mlx5dr_icm_chunk * chunk,struct mlx5dr_icm_pool * pool,enum mlx5dr_icm_chunk_size chunk_size,struct mlx5dr_icm_buddy_mem * buddy_mem_pool,unsigned int seg)321edaea001SYevgeny Kliteynik dr_icm_chunk_init(struct mlx5dr_icm_chunk *chunk,
322edaea001SYevgeny Kliteynik 		  struct mlx5dr_icm_pool *pool,
323a00cd878SYevgeny Kliteynik 		  enum mlx5dr_icm_chunk_size chunk_size,
324a00cd878SYevgeny Kliteynik 		  struct mlx5dr_icm_buddy_mem *buddy_mem_pool,
325a00cd878SYevgeny Kliteynik 		  unsigned int seg)
32629cf8febSAlex Vesker {
327a00cd878SYevgeny Kliteynik 	int offset;
32829cf8febSAlex Vesker 
329a00cd878SYevgeny Kliteynik 	chunk->seg = seg;
330f51bb517SRongwei Liu 	chunk->size = chunk_size;
331e5b2bc30SYevgeny Kliteynik 	chunk->buddy_mem = buddy_mem_pool;
332a00cd878SYevgeny Kliteynik 
333edaea001SYevgeny Kliteynik 	if (pool->icm_type == DR_ICM_TYPE_STE) {
334edaea001SYevgeny Kliteynik 		offset = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type) * seg;
335e5b2bc30SYevgeny Kliteynik 		dr_icm_chunk_ste_init(chunk, offset);
336edaea001SYevgeny Kliteynik 	}
33729cf8febSAlex Vesker 
338f51bb517SRongwei Liu 	buddy_mem_pool->used_memory += mlx5dr_icm_pool_get_chunk_byte_size(chunk);
339a00cd878SYevgeny Kliteynik }
340a00cd878SYevgeny Kliteynik 
dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool * pool)341a00cd878SYevgeny Kliteynik static bool dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool *pool)
34229cf8febSAlex Vesker {
34372b2cff6SYevgeny Kliteynik 	return pool->hot_memory_size > pool->th;
344a00cd878SYevgeny Kliteynik }
345a00cd878SYevgeny Kliteynik 
dr_icm_pool_clear_hot_chunks_arr(struct mlx5dr_icm_pool * pool)3464519fc45SYevgeny Kliteynik static void dr_icm_pool_clear_hot_chunks_arr(struct mlx5dr_icm_pool *pool)
3474519fc45SYevgeny Kliteynik {
3484519fc45SYevgeny Kliteynik 	struct mlx5dr_icm_hot_chunk *hot_chunk;
3494519fc45SYevgeny Kliteynik 	u32 i, num_entries;
3504519fc45SYevgeny Kliteynik 
3514519fc45SYevgeny Kliteynik 	for (i = 0; i < pool->hot_chunks_num; i++) {
3524519fc45SYevgeny Kliteynik 		hot_chunk = &pool->hot_chunks_arr[i];
3534519fc45SYevgeny Kliteynik 		num_entries = mlx5dr_icm_pool_chunk_size_to_entries(hot_chunk->size);
3544519fc45SYevgeny Kliteynik 		mlx5dr_buddy_free_mem(hot_chunk->buddy_mem,
3554519fc45SYevgeny Kliteynik 				      hot_chunk->seg, ilog2(num_entries));
3564519fc45SYevgeny Kliteynik 		hot_chunk->buddy_mem->used_memory -=
3574519fc45SYevgeny Kliteynik 			mlx5dr_icm_pool_chunk_size_to_byte(hot_chunk->size,
3584519fc45SYevgeny Kliteynik 							   pool->icm_type);
3594519fc45SYevgeny Kliteynik 	}
3604519fc45SYevgeny Kliteynik 
3614519fc45SYevgeny Kliteynik 	pool->hot_chunks_num = 0;
3624519fc45SYevgeny Kliteynik 	pool->hot_memory_size = 0;
3634519fc45SYevgeny Kliteynik }
3644519fc45SYevgeny Kliteynik 
dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool * pool)365a00cd878SYevgeny Kliteynik static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool)
36629cf8febSAlex Vesker {
367a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy, *tmp_buddy;
368a00cd878SYevgeny Kliteynik 	int err;
369a00cd878SYevgeny Kliteynik 
370a00cd878SYevgeny Kliteynik 	err = mlx5dr_cmd_sync_steering(pool->dmn->mdev);
371a00cd878SYevgeny Kliteynik 	if (err) {
372a00cd878SYevgeny Kliteynik 		mlx5dr_err(pool->dmn, "Failed to sync to HW (err: %d)\n", err);
373a00cd878SYevgeny Kliteynik 		return err;
37429cf8febSAlex Vesker 	}
37529cf8febSAlex Vesker 
3764519fc45SYevgeny Kliteynik 	dr_icm_pool_clear_hot_chunks_arr(pool);
3774519fc45SYevgeny Kliteynik 
378a00cd878SYevgeny Kliteynik 	list_for_each_entry_safe(buddy, tmp_buddy, &pool->buddy_mem_list, list_node) {
379284836d9SYevgeny Kliteynik 		if (!buddy->used_memory && pool->icm_type == DR_ICM_TYPE_STE)
380284836d9SYevgeny Kliteynik 			dr_icm_buddy_destroy(buddy);
381a00cd878SYevgeny Kliteynik 	}
382a00cd878SYevgeny Kliteynik 
383a00cd878SYevgeny Kliteynik 	return 0;
384a00cd878SYevgeny Kliteynik }
385a00cd878SYevgeny Kliteynik 
dr_icm_handle_buddies_get_mem(struct mlx5dr_icm_pool * pool,enum mlx5dr_icm_chunk_size chunk_size,struct mlx5dr_icm_buddy_mem ** buddy,unsigned int * seg)386a00cd878SYevgeny Kliteynik static int dr_icm_handle_buddies_get_mem(struct mlx5dr_icm_pool *pool,
387a00cd878SYevgeny Kliteynik 					 enum mlx5dr_icm_chunk_size chunk_size,
388a00cd878SYevgeny Kliteynik 					 struct mlx5dr_icm_buddy_mem **buddy,
389a00cd878SYevgeny Kliteynik 					 unsigned int *seg)
39029cf8febSAlex Vesker {
391a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy_mem_pool;
392a00cd878SYevgeny Kliteynik 	bool new_mem = false;
393a00cd878SYevgeny Kliteynik 	int err;
39429cf8febSAlex Vesker 
395a00cd878SYevgeny Kliteynik alloc_buddy_mem:
396a00cd878SYevgeny Kliteynik 	/* find the next free place from the buddy list */
397a00cd878SYevgeny Kliteynik 	list_for_each_entry(buddy_mem_pool, &pool->buddy_mem_list, list_node) {
398a00cd878SYevgeny Kliteynik 		err = mlx5dr_buddy_alloc_mem(buddy_mem_pool,
399a00cd878SYevgeny Kliteynik 					     chunk_size, seg);
400a00cd878SYevgeny Kliteynik 		if (!err)
401a00cd878SYevgeny Kliteynik 			goto found;
40229cf8febSAlex Vesker 
403a00cd878SYevgeny Kliteynik 		if (WARN_ON(new_mem)) {
404a00cd878SYevgeny Kliteynik 			/* We have new memory pool, first in the list */
405a00cd878SYevgeny Kliteynik 			mlx5dr_err(pool->dmn,
406a00cd878SYevgeny Kliteynik 				   "No memory for order: %d\n",
407a00cd878SYevgeny Kliteynik 				   chunk_size);
408a00cd878SYevgeny Kliteynik 			goto out;
40929cf8febSAlex Vesker 		}
41029cf8febSAlex Vesker 	}
41129cf8febSAlex Vesker 
412a00cd878SYevgeny Kliteynik 	/* no more available allocators in that pool, create new */
413a00cd878SYevgeny Kliteynik 	err = dr_icm_buddy_create(pool);
414a00cd878SYevgeny Kliteynik 	if (err) {
415a00cd878SYevgeny Kliteynik 		mlx5dr_err(pool->dmn,
416a00cd878SYevgeny Kliteynik 			   "Failed creating buddy for order %d\n",
417a00cd878SYevgeny Kliteynik 			   chunk_size);
418a00cd878SYevgeny Kliteynik 		goto out;
41929cf8febSAlex Vesker 	}
42029cf8febSAlex Vesker 
421a00cd878SYevgeny Kliteynik 	/* mark we have new memory, first in list */
422a00cd878SYevgeny Kliteynik 	new_mem = true;
423a00cd878SYevgeny Kliteynik 	goto alloc_buddy_mem;
42429cf8febSAlex Vesker 
425a00cd878SYevgeny Kliteynik found:
426a00cd878SYevgeny Kliteynik 	*buddy = buddy_mem_pool;
427a00cd878SYevgeny Kliteynik out:
428a00cd878SYevgeny Kliteynik 	return err;
42929cf8febSAlex Vesker }
43029cf8febSAlex Vesker 
43129cf8febSAlex Vesker /* Allocate an ICM chunk, each chunk holds a piece of ICM memory and
43229cf8febSAlex Vesker  * also memory used for HW STE management for optimizations.
43329cf8febSAlex Vesker  */
43429cf8febSAlex Vesker struct mlx5dr_icm_chunk *
mlx5dr_icm_alloc_chunk(struct mlx5dr_icm_pool * pool,enum mlx5dr_icm_chunk_size chunk_size)43529cf8febSAlex Vesker mlx5dr_icm_alloc_chunk(struct mlx5dr_icm_pool *pool,
43629cf8febSAlex Vesker 		       enum mlx5dr_icm_chunk_size chunk_size)
43729cf8febSAlex Vesker {
438a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_chunk *chunk = NULL;
439a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy;
440a00cd878SYevgeny Kliteynik 	unsigned int seg;
441a00cd878SYevgeny Kliteynik 	int ret;
44229cf8febSAlex Vesker 
44329cf8febSAlex Vesker 	if (chunk_size > pool->max_log_chunk_sz)
44429cf8febSAlex Vesker 		return NULL;
44529cf8febSAlex Vesker 
446a00cd878SYevgeny Kliteynik 	mutex_lock(&pool->mutex);
447a00cd878SYevgeny Kliteynik 	/* find mem, get back the relevant buddy pool and seg in that mem */
448a00cd878SYevgeny Kliteynik 	ret = dr_icm_handle_buddies_get_mem(pool, chunk_size, &buddy, &seg);
449a00cd878SYevgeny Kliteynik 	if (ret)
45029cf8febSAlex Vesker 		goto out;
45129cf8febSAlex Vesker 
452edaea001SYevgeny Kliteynik 	chunk = kmem_cache_alloc(pool->chunks_kmem_cache, GFP_KERNEL);
453a00cd878SYevgeny Kliteynik 	if (!chunk)
454a00cd878SYevgeny Kliteynik 		goto out_err;
455a00cd878SYevgeny Kliteynik 
456edaea001SYevgeny Kliteynik 	dr_icm_chunk_init(chunk, pool, chunk_size, buddy, seg);
457edaea001SYevgeny Kliteynik 
458a00cd878SYevgeny Kliteynik 	goto out;
459a00cd878SYevgeny Kliteynik 
460a00cd878SYevgeny Kliteynik out_err:
461a00cd878SYevgeny Kliteynik 	mlx5dr_buddy_free_mem(buddy, seg, chunk_size);
46229cf8febSAlex Vesker out:
463a00cd878SYevgeny Kliteynik 	mutex_unlock(&pool->mutex);
46429cf8febSAlex Vesker 	return chunk;
46529cf8febSAlex Vesker }
46629cf8febSAlex Vesker 
mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk * chunk)46729cf8febSAlex Vesker void mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk *chunk)
46829cf8febSAlex Vesker {
469a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem;
4703eb1006aSYevgeny Kliteynik 	struct mlx5dr_icm_pool *pool = buddy->pool;
4714519fc45SYevgeny Kliteynik 	struct mlx5dr_icm_hot_chunk *hot_chunk;
4724519fc45SYevgeny Kliteynik 	struct kmem_cache *chunks_cache;
47329cf8febSAlex Vesker 
4744519fc45SYevgeny Kliteynik 	chunks_cache = pool->chunks_kmem_cache;
4754519fc45SYevgeny Kliteynik 
4764519fc45SYevgeny Kliteynik 	/* move the chunk to the waiting chunks array, AKA "hot" memory */
4773eb1006aSYevgeny Kliteynik 	mutex_lock(&pool->mutex);
4784519fc45SYevgeny Kliteynik 
479f51bb517SRongwei Liu 	pool->hot_memory_size += mlx5dr_icm_pool_get_chunk_byte_size(chunk);
4803eb1006aSYevgeny Kliteynik 
4814519fc45SYevgeny Kliteynik 	hot_chunk = &pool->hot_chunks_arr[pool->hot_chunks_num++];
4824519fc45SYevgeny Kliteynik 	hot_chunk->buddy_mem = chunk->buddy_mem;
4834519fc45SYevgeny Kliteynik 	hot_chunk->seg = chunk->seg;
4844519fc45SYevgeny Kliteynik 	hot_chunk->size = chunk->size;
4854519fc45SYevgeny Kliteynik 
4864519fc45SYevgeny Kliteynik 	kmem_cache_free(chunks_cache, chunk);
4874519fc45SYevgeny Kliteynik 
4883eb1006aSYevgeny Kliteynik 	/* Check if we have chunks that are waiting for sync-ste */
4893eb1006aSYevgeny Kliteynik 	if (dr_icm_pool_is_sync_required(pool))
4903eb1006aSYevgeny Kliteynik 		dr_icm_pool_sync_all_buddy_pools(pool);
4913eb1006aSYevgeny Kliteynik 
4923eb1006aSYevgeny Kliteynik 	mutex_unlock(&pool->mutex);
49329cf8febSAlex Vesker }
49429cf8febSAlex Vesker 
mlx5dr_icm_pool_alloc_htbl(struct mlx5dr_icm_pool * pool)495fb628b71SYevgeny Kliteynik struct mlx5dr_ste_htbl *mlx5dr_icm_pool_alloc_htbl(struct mlx5dr_icm_pool *pool)
496fb628b71SYevgeny Kliteynik {
497fb628b71SYevgeny Kliteynik 	return kmem_cache_alloc(pool->dmn->htbls_kmem_cache, GFP_KERNEL);
498fb628b71SYevgeny Kliteynik }
499fb628b71SYevgeny Kliteynik 
mlx5dr_icm_pool_free_htbl(struct mlx5dr_icm_pool * pool,struct mlx5dr_ste_htbl * htbl)500fb628b71SYevgeny Kliteynik void mlx5dr_icm_pool_free_htbl(struct mlx5dr_icm_pool *pool, struct mlx5dr_ste_htbl *htbl)
501fb628b71SYevgeny Kliteynik {
502fb628b71SYevgeny Kliteynik 	kmem_cache_free(pool->dmn->htbls_kmem_cache, htbl);
503fb628b71SYevgeny Kliteynik }
504fb628b71SYevgeny Kliteynik 
mlx5dr_icm_pool_create(struct mlx5dr_domain * dmn,enum mlx5dr_icm_type icm_type)50529cf8febSAlex Vesker struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn,
50629cf8febSAlex Vesker 					       enum mlx5dr_icm_type icm_type)
50729cf8febSAlex Vesker {
50872b2cff6SYevgeny Kliteynik 	u32 num_of_chunks, entry_size;
50929cf8febSAlex Vesker 	struct mlx5dr_icm_pool *pool;
51072b2cff6SYevgeny Kliteynik 	u32 max_hot_size = 0;
51129cf8febSAlex Vesker 
51229cf8febSAlex Vesker 	pool = kvzalloc(sizeof(*pool), GFP_KERNEL);
51329cf8febSAlex Vesker 	if (!pool)
51429cf8febSAlex Vesker 		return NULL;
51529cf8febSAlex Vesker 
51629cf8febSAlex Vesker 	pool->dmn = dmn;
51729cf8febSAlex Vesker 	pool->icm_type = icm_type;
518fd785e52SYevgeny Kliteynik 	pool->chunks_kmem_cache = dmn->chunks_kmem_cache;
51929cf8febSAlex Vesker 
520a00cd878SYevgeny Kliteynik 	INIT_LIST_HEAD(&pool->buddy_mem_list);
521a00cd878SYevgeny Kliteynik 	mutex_init(&pool->mutex);
52229cf8febSAlex Vesker 
523108ff821SYevgeny Kliteynik 	switch (icm_type) {
524108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_STE:
525108ff821SYevgeny Kliteynik 		pool->max_log_chunk_sz = dmn->info.max_log_sw_icm_sz;
52672b2cff6SYevgeny Kliteynik 		max_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
52772b2cff6SYevgeny Kliteynik 								  pool->icm_type) *
52872b2cff6SYevgeny Kliteynik 			       DR_ICM_POOL_STE_HOT_MEM_PERCENT / 100;
529108ff821SYevgeny Kliteynik 		break;
530108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_MODIFY_ACTION:
531108ff821SYevgeny Kliteynik 		pool->max_log_chunk_sz = dmn->info.max_log_action_icm_sz;
53272b2cff6SYevgeny Kliteynik 		max_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
53372b2cff6SYevgeny Kliteynik 								  pool->icm_type) *
53472b2cff6SYevgeny Kliteynik 			       DR_ICM_POOL_MODIFY_ACTION_HOT_MEM_PERCENT / 100;
535108ff821SYevgeny Kliteynik 		break;
536108ff821SYevgeny Kliteynik 	case DR_ICM_TYPE_MODIFY_HDR_PTRN:
537108ff821SYevgeny Kliteynik 		pool->max_log_chunk_sz = dmn->info.max_log_modify_hdr_pattern_icm_sz;
53872b2cff6SYevgeny Kliteynik 		max_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
53972b2cff6SYevgeny Kliteynik 								  pool->icm_type) *
54072b2cff6SYevgeny Kliteynik 			       DR_ICM_POOL_MODIFY_HDR_PTRN_HOT_MEM_PERCENT / 100;
541108ff821SYevgeny Kliteynik 		break;
542108ff821SYevgeny Kliteynik 	default:
543108ff821SYevgeny Kliteynik 		WARN_ON(icm_type);
544108ff821SYevgeny Kliteynik 	}
545108ff821SYevgeny Kliteynik 
5464519fc45SYevgeny Kliteynik 	entry_size = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type);
5474519fc45SYevgeny Kliteynik 
5484519fc45SYevgeny Kliteynik 	num_of_chunks = DIV_ROUND_UP(max_hot_size, entry_size) + 1;
54972b2cff6SYevgeny Kliteynik 	pool->th = max_hot_size;
5504519fc45SYevgeny Kliteynik 
5514519fc45SYevgeny Kliteynik 	pool->hot_chunks_arr = kvcalloc(num_of_chunks,
5524519fc45SYevgeny Kliteynik 					sizeof(struct mlx5dr_icm_hot_chunk),
5534519fc45SYevgeny Kliteynik 					GFP_KERNEL);
5544519fc45SYevgeny Kliteynik 	if (!pool->hot_chunks_arr)
5554519fc45SYevgeny Kliteynik 		goto free_pool;
5564519fc45SYevgeny Kliteynik 
55729cf8febSAlex Vesker 	return pool;
5584519fc45SYevgeny Kliteynik 
5594519fc45SYevgeny Kliteynik free_pool:
5604519fc45SYevgeny Kliteynik 	kvfree(pool);
5614519fc45SYevgeny Kliteynik 	return NULL;
56229cf8febSAlex Vesker }
56329cf8febSAlex Vesker 
mlx5dr_icm_pool_destroy(struct mlx5dr_icm_pool * pool)56429cf8febSAlex Vesker void mlx5dr_icm_pool_destroy(struct mlx5dr_icm_pool *pool)
56529cf8febSAlex Vesker {
566a00cd878SYevgeny Kliteynik 	struct mlx5dr_icm_buddy_mem *buddy, *tmp_buddy;
56729cf8febSAlex Vesker 
5684519fc45SYevgeny Kliteynik 	dr_icm_pool_clear_hot_chunks_arr(pool);
5694519fc45SYevgeny Kliteynik 
570a00cd878SYevgeny Kliteynik 	list_for_each_entry_safe(buddy, tmp_buddy, &pool->buddy_mem_list, list_node)
571a00cd878SYevgeny Kliteynik 		dr_icm_buddy_destroy(buddy);
57229cf8febSAlex Vesker 
5734519fc45SYevgeny Kliteynik 	kvfree(pool->hot_chunks_arr);
574a00cd878SYevgeny Kliteynik 	mutex_destroy(&pool->mutex);
57529cf8febSAlex Vesker 	kvfree(pool);
57629cf8febSAlex Vesker }
577