1108ff821SYevgeny Kliteynik // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2108ff821SYevgeny Kliteynik // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3108ff821SYevgeny Kliteynik 
4108ff821SYevgeny Kliteynik #include "dr_types.h"
5da5d0027SYevgeny Kliteynik #include "mlx5_ifc_dr_ste_v1.h"
6da5d0027SYevgeny Kliteynik 
7da5d0027SYevgeny Kliteynik enum dr_ptrn_modify_hdr_action_id {
8da5d0027SYevgeny Kliteynik 	DR_PTRN_MODIFY_HDR_ACTION_ID_NOP = 0x00,
9da5d0027SYevgeny Kliteynik 	DR_PTRN_MODIFY_HDR_ACTION_ID_COPY = 0x05,
10da5d0027SYevgeny Kliteynik 	DR_PTRN_MODIFY_HDR_ACTION_ID_SET = 0x06,
11da5d0027SYevgeny Kliteynik 	DR_PTRN_MODIFY_HDR_ACTION_ID_ADD = 0x07,
12da5d0027SYevgeny Kliteynik 	DR_PTRN_MODIFY_HDR_ACTION_ID_INSERT_INLINE = 0x0a,
13da5d0027SYevgeny Kliteynik };
14108ff821SYevgeny Kliteynik 
15108ff821SYevgeny Kliteynik struct mlx5dr_ptrn_mgr {
16108ff821SYevgeny Kliteynik 	struct mlx5dr_domain *dmn;
17108ff821SYevgeny Kliteynik 	struct mlx5dr_icm_pool *ptrn_icm_pool;
18da5d0027SYevgeny Kliteynik 	/* cache for modify_header ptrn */
19da5d0027SYevgeny Kliteynik 	struct list_head ptrn_list;
20da5d0027SYevgeny Kliteynik 	struct mutex modify_hdr_mutex; /* protect the pattern cache */
21108ff821SYevgeny Kliteynik };
22108ff821SYevgeny Kliteynik 
23da5d0027SYevgeny Kliteynik /* Cache structure and functions */
dr_ptrn_compare_modify_hdr(size_t cur_num_of_actions,__be64 cur_hw_actions[],size_t num_of_actions,__be64 hw_actions[])24da5d0027SYevgeny Kliteynik static bool dr_ptrn_compare_modify_hdr(size_t cur_num_of_actions,
25da5d0027SYevgeny Kliteynik 				       __be64 cur_hw_actions[],
26da5d0027SYevgeny Kliteynik 				       size_t num_of_actions,
27da5d0027SYevgeny Kliteynik 				       __be64 hw_actions[])
28da5d0027SYevgeny Kliteynik {
29da5d0027SYevgeny Kliteynik 	int i;
30da5d0027SYevgeny Kliteynik 
31da5d0027SYevgeny Kliteynik 	if (cur_num_of_actions != num_of_actions)
32da5d0027SYevgeny Kliteynik 		return false;
33da5d0027SYevgeny Kliteynik 
34da5d0027SYevgeny Kliteynik 	for (i = 0; i < num_of_actions; i++) {
35da5d0027SYevgeny Kliteynik 		u8 action_id =
36da5d0027SYevgeny Kliteynik 			MLX5_GET(ste_double_action_set_v1, &hw_actions[i], action_id);
37da5d0027SYevgeny Kliteynik 
38da5d0027SYevgeny Kliteynik 		if (action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_COPY) {
39da5d0027SYevgeny Kliteynik 			if (hw_actions[i] != cur_hw_actions[i])
40da5d0027SYevgeny Kliteynik 				return false;
41da5d0027SYevgeny Kliteynik 		} else {
42da5d0027SYevgeny Kliteynik 			if ((__force __be32)hw_actions[i] !=
43da5d0027SYevgeny Kliteynik 			    (__force __be32)cur_hw_actions[i])
44da5d0027SYevgeny Kliteynik 				return false;
45da5d0027SYevgeny Kliteynik 		}
46da5d0027SYevgeny Kliteynik 	}
47da5d0027SYevgeny Kliteynik 
48da5d0027SYevgeny Kliteynik 	return true;
49da5d0027SYevgeny Kliteynik }
50da5d0027SYevgeny Kliteynik 
51da5d0027SYevgeny Kliteynik static struct mlx5dr_ptrn_obj *
dr_ptrn_find_cached_pattern(struct mlx5dr_ptrn_mgr * mgr,size_t num_of_actions,__be64 hw_actions[])52da5d0027SYevgeny Kliteynik dr_ptrn_find_cached_pattern(struct mlx5dr_ptrn_mgr *mgr,
53da5d0027SYevgeny Kliteynik 			    size_t num_of_actions,
54da5d0027SYevgeny Kliteynik 			    __be64 hw_actions[])
55da5d0027SYevgeny Kliteynik {
56da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *cached_pattern;
57da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *tmp;
58da5d0027SYevgeny Kliteynik 
59da5d0027SYevgeny Kliteynik 	list_for_each_entry_safe(cached_pattern, tmp, &mgr->ptrn_list, list) {
60da5d0027SYevgeny Kliteynik 		if (dr_ptrn_compare_modify_hdr(cached_pattern->num_of_actions,
61da5d0027SYevgeny Kliteynik 					       (__be64 *)cached_pattern->data,
62da5d0027SYevgeny Kliteynik 					       num_of_actions,
63da5d0027SYevgeny Kliteynik 					       hw_actions)) {
64da5d0027SYevgeny Kliteynik 			/* Put this pattern in the head of the list,
65da5d0027SYevgeny Kliteynik 			 * as we will probably use it more.
66da5d0027SYevgeny Kliteynik 			 */
67da5d0027SYevgeny Kliteynik 			list_del_init(&cached_pattern->list);
68da5d0027SYevgeny Kliteynik 			list_add(&cached_pattern->list, &mgr->ptrn_list);
69da5d0027SYevgeny Kliteynik 			return cached_pattern;
70da5d0027SYevgeny Kliteynik 		}
71da5d0027SYevgeny Kliteynik 	}
72da5d0027SYevgeny Kliteynik 
73da5d0027SYevgeny Kliteynik 	return NULL;
74da5d0027SYevgeny Kliteynik }
75da5d0027SYevgeny Kliteynik 
76da5d0027SYevgeny Kliteynik static struct mlx5dr_ptrn_obj *
dr_ptrn_alloc_pattern(struct mlx5dr_ptrn_mgr * mgr,u16 num_of_actions,u8 * data)77da5d0027SYevgeny Kliteynik dr_ptrn_alloc_pattern(struct mlx5dr_ptrn_mgr *mgr,
78da5d0027SYevgeny Kliteynik 		      u16 num_of_actions, u8 *data)
79da5d0027SYevgeny Kliteynik {
80da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *pattern;
81da5d0027SYevgeny Kliteynik 	struct mlx5dr_icm_chunk *chunk;
82da5d0027SYevgeny Kliteynik 	u32 chunk_size;
83da5d0027SYevgeny Kliteynik 	u32 index;
84da5d0027SYevgeny Kliteynik 
85*8bfe1e19SYevgeny Kliteynik 	chunk_size = ilog2(roundup_pow_of_two(num_of_actions));
86da5d0027SYevgeny Kliteynik 	/* HW modify action index granularity is at least 64B */
87da5d0027SYevgeny Kliteynik 	chunk_size = max_t(u32, chunk_size, DR_CHUNK_SIZE_8);
88da5d0027SYevgeny Kliteynik 
89da5d0027SYevgeny Kliteynik 	chunk = mlx5dr_icm_alloc_chunk(mgr->ptrn_icm_pool, chunk_size);
90da5d0027SYevgeny Kliteynik 	if (!chunk)
91da5d0027SYevgeny Kliteynik 		return NULL;
92da5d0027SYevgeny Kliteynik 
93da5d0027SYevgeny Kliteynik 	index = (mlx5dr_icm_pool_get_chunk_icm_addr(chunk) -
94da5d0027SYevgeny Kliteynik 		 mgr->dmn->info.caps.hdr_modify_pattern_icm_addr) /
95da5d0027SYevgeny Kliteynik 		DR_ACTION_CACHE_LINE_SIZE;
96da5d0027SYevgeny Kliteynik 
97da5d0027SYevgeny Kliteynik 	pattern = kzalloc(sizeof(*pattern), GFP_KERNEL);
98da5d0027SYevgeny Kliteynik 	if (!pattern)
99da5d0027SYevgeny Kliteynik 		goto free_chunk;
100da5d0027SYevgeny Kliteynik 
101da5d0027SYevgeny Kliteynik 	pattern->data = kzalloc(num_of_actions * DR_MODIFY_ACTION_SIZE *
102da5d0027SYevgeny Kliteynik 				sizeof(*pattern->data), GFP_KERNEL);
103da5d0027SYevgeny Kliteynik 	if (!pattern->data)
104da5d0027SYevgeny Kliteynik 		goto free_pattern;
105da5d0027SYevgeny Kliteynik 
106da5d0027SYevgeny Kliteynik 	memcpy(pattern->data, data, num_of_actions * DR_MODIFY_ACTION_SIZE);
107da5d0027SYevgeny Kliteynik 	pattern->chunk = chunk;
108da5d0027SYevgeny Kliteynik 	pattern->index = index;
109da5d0027SYevgeny Kliteynik 	pattern->num_of_actions = num_of_actions;
110da5d0027SYevgeny Kliteynik 
111da5d0027SYevgeny Kliteynik 	list_add(&pattern->list, &mgr->ptrn_list);
112da5d0027SYevgeny Kliteynik 	refcount_set(&pattern->refcount, 1);
113da5d0027SYevgeny Kliteynik 
114da5d0027SYevgeny Kliteynik 	return pattern;
115da5d0027SYevgeny Kliteynik 
116da5d0027SYevgeny Kliteynik free_pattern:
117da5d0027SYevgeny Kliteynik 	kfree(pattern);
118da5d0027SYevgeny Kliteynik free_chunk:
119da5d0027SYevgeny Kliteynik 	mlx5dr_icm_free_chunk(chunk);
120da5d0027SYevgeny Kliteynik 	return NULL;
121da5d0027SYevgeny Kliteynik }
122da5d0027SYevgeny Kliteynik 
123da5d0027SYevgeny Kliteynik static void
dr_ptrn_free_pattern(struct mlx5dr_ptrn_obj * pattern)124da5d0027SYevgeny Kliteynik dr_ptrn_free_pattern(struct mlx5dr_ptrn_obj *pattern)
125da5d0027SYevgeny Kliteynik {
126da5d0027SYevgeny Kliteynik 	list_del(&pattern->list);
127da5d0027SYevgeny Kliteynik 	mlx5dr_icm_free_chunk(pattern->chunk);
128da5d0027SYevgeny Kliteynik 	kfree(pattern->data);
129da5d0027SYevgeny Kliteynik 	kfree(pattern);
130da5d0027SYevgeny Kliteynik }
131da5d0027SYevgeny Kliteynik 
132da5d0027SYevgeny Kliteynik struct mlx5dr_ptrn_obj *
mlx5dr_ptrn_cache_get_pattern(struct mlx5dr_ptrn_mgr * mgr,u16 num_of_actions,u8 * data)133da5d0027SYevgeny Kliteynik mlx5dr_ptrn_cache_get_pattern(struct mlx5dr_ptrn_mgr *mgr,
134da5d0027SYevgeny Kliteynik 			      u16 num_of_actions,
135da5d0027SYevgeny Kliteynik 			      u8 *data)
136da5d0027SYevgeny Kliteynik {
137da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *pattern;
138da5d0027SYevgeny Kliteynik 	u64 *hw_actions;
139da5d0027SYevgeny Kliteynik 	u8 action_id;
140da5d0027SYevgeny Kliteynik 	int i;
141da5d0027SYevgeny Kliteynik 
142da5d0027SYevgeny Kliteynik 	mutex_lock(&mgr->modify_hdr_mutex);
143da5d0027SYevgeny Kliteynik 	pattern = dr_ptrn_find_cached_pattern(mgr,
144da5d0027SYevgeny Kliteynik 					      num_of_actions,
145da5d0027SYevgeny Kliteynik 					      (__be64 *)data);
146da5d0027SYevgeny Kliteynik 	if (!pattern) {
147da5d0027SYevgeny Kliteynik 		/* Alloc and add new pattern to cache */
148da5d0027SYevgeny Kliteynik 		pattern = dr_ptrn_alloc_pattern(mgr, num_of_actions, data);
149da5d0027SYevgeny Kliteynik 		if (!pattern)
150da5d0027SYevgeny Kliteynik 			goto out_unlock;
151da5d0027SYevgeny Kliteynik 
152da5d0027SYevgeny Kliteynik 		hw_actions = (u64 *)pattern->data;
153da5d0027SYevgeny Kliteynik 		/* Here we mask the pattern data to create a valid pattern
154da5d0027SYevgeny Kliteynik 		 * since we do an OR operation between the arg and pattern
155da5d0027SYevgeny Kliteynik 		 */
156da5d0027SYevgeny Kliteynik 		for (i = 0; i < num_of_actions; i++) {
157da5d0027SYevgeny Kliteynik 			action_id = MLX5_GET(ste_double_action_set_v1, &hw_actions[i], action_id);
158da5d0027SYevgeny Kliteynik 
159da5d0027SYevgeny Kliteynik 			if (action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_SET ||
160da5d0027SYevgeny Kliteynik 			    action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_ADD ||
161da5d0027SYevgeny Kliteynik 			    action_id == DR_PTRN_MODIFY_HDR_ACTION_ID_INSERT_INLINE)
162da5d0027SYevgeny Kliteynik 				MLX5_SET(ste_double_action_set_v1, &hw_actions[i], inline_data, 0);
163da5d0027SYevgeny Kliteynik 		}
164da5d0027SYevgeny Kliteynik 
165da5d0027SYevgeny Kliteynik 		if (mlx5dr_send_postsend_pattern(mgr->dmn, pattern->chunk,
166da5d0027SYevgeny Kliteynik 						 num_of_actions, pattern->data)) {
167da5d0027SYevgeny Kliteynik 			refcount_dec(&pattern->refcount);
168da5d0027SYevgeny Kliteynik 			goto free_pattern;
169da5d0027SYevgeny Kliteynik 		}
170da5d0027SYevgeny Kliteynik 	} else {
171da5d0027SYevgeny Kliteynik 		refcount_inc(&pattern->refcount);
172da5d0027SYevgeny Kliteynik 	}
173da5d0027SYevgeny Kliteynik 
174da5d0027SYevgeny Kliteynik 	mutex_unlock(&mgr->modify_hdr_mutex);
175da5d0027SYevgeny Kliteynik 
176da5d0027SYevgeny Kliteynik 	return pattern;
177da5d0027SYevgeny Kliteynik 
178da5d0027SYevgeny Kliteynik free_pattern:
179da5d0027SYevgeny Kliteynik 	dr_ptrn_free_pattern(pattern);
180da5d0027SYevgeny Kliteynik out_unlock:
181da5d0027SYevgeny Kliteynik 	mutex_unlock(&mgr->modify_hdr_mutex);
182da5d0027SYevgeny Kliteynik 	return NULL;
183da5d0027SYevgeny Kliteynik }
184da5d0027SYevgeny Kliteynik 
185da5d0027SYevgeny Kliteynik void
mlx5dr_ptrn_cache_put_pattern(struct mlx5dr_ptrn_mgr * mgr,struct mlx5dr_ptrn_obj * pattern)186da5d0027SYevgeny Kliteynik mlx5dr_ptrn_cache_put_pattern(struct mlx5dr_ptrn_mgr *mgr,
187da5d0027SYevgeny Kliteynik 			      struct mlx5dr_ptrn_obj *pattern)
188da5d0027SYevgeny Kliteynik {
189da5d0027SYevgeny Kliteynik 	mutex_lock(&mgr->modify_hdr_mutex);
190da5d0027SYevgeny Kliteynik 
191da5d0027SYevgeny Kliteynik 	if (refcount_dec_and_test(&pattern->refcount))
192da5d0027SYevgeny Kliteynik 		dr_ptrn_free_pattern(pattern);
193da5d0027SYevgeny Kliteynik 
194da5d0027SYevgeny Kliteynik 	mutex_unlock(&mgr->modify_hdr_mutex);
195da5d0027SYevgeny Kliteynik }
196da5d0027SYevgeny Kliteynik 
mlx5dr_ptrn_mgr_create(struct mlx5dr_domain * dmn)197108ff821SYevgeny Kliteynik struct mlx5dr_ptrn_mgr *mlx5dr_ptrn_mgr_create(struct mlx5dr_domain *dmn)
198108ff821SYevgeny Kliteynik {
199108ff821SYevgeny Kliteynik 	struct mlx5dr_ptrn_mgr *mgr;
200108ff821SYevgeny Kliteynik 
201108ff821SYevgeny Kliteynik 	if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
202108ff821SYevgeny Kliteynik 		return NULL;
203108ff821SYevgeny Kliteynik 
204108ff821SYevgeny Kliteynik 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
205108ff821SYevgeny Kliteynik 	if (!mgr)
206108ff821SYevgeny Kliteynik 		return NULL;
207108ff821SYevgeny Kliteynik 
208108ff821SYevgeny Kliteynik 	mgr->dmn = dmn;
209108ff821SYevgeny Kliteynik 	mgr->ptrn_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_HDR_PTRN);
210108ff821SYevgeny Kliteynik 	if (!mgr->ptrn_icm_pool) {
211108ff821SYevgeny Kliteynik 		mlx5dr_err(dmn, "Couldn't get modify-header-pattern memory\n");
212108ff821SYevgeny Kliteynik 		goto free_mgr;
213108ff821SYevgeny Kliteynik 	}
214108ff821SYevgeny Kliteynik 
215da5d0027SYevgeny Kliteynik 	INIT_LIST_HEAD(&mgr->ptrn_list);
216fe5c2d3aSYevgeny Kliteynik 	mutex_init(&mgr->modify_hdr_mutex);
217fe5c2d3aSYevgeny Kliteynik 
218108ff821SYevgeny Kliteynik 	return mgr;
219108ff821SYevgeny Kliteynik 
220108ff821SYevgeny Kliteynik free_mgr:
221108ff821SYevgeny Kliteynik 	kfree(mgr);
222108ff821SYevgeny Kliteynik 	return NULL;
223108ff821SYevgeny Kliteynik }
224108ff821SYevgeny Kliteynik 
mlx5dr_ptrn_mgr_destroy(struct mlx5dr_ptrn_mgr * mgr)225108ff821SYevgeny Kliteynik void mlx5dr_ptrn_mgr_destroy(struct mlx5dr_ptrn_mgr *mgr)
226108ff821SYevgeny Kliteynik {
227da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *pattern;
228da5d0027SYevgeny Kliteynik 	struct mlx5dr_ptrn_obj *tmp;
229da5d0027SYevgeny Kliteynik 
230108ff821SYevgeny Kliteynik 	if (!mgr)
231108ff821SYevgeny Kliteynik 		return;
232108ff821SYevgeny Kliteynik 
233da5d0027SYevgeny Kliteynik 	WARN_ON(!list_empty(&mgr->ptrn_list));
234da5d0027SYevgeny Kliteynik 
235da5d0027SYevgeny Kliteynik 	list_for_each_entry_safe(pattern, tmp, &mgr->ptrn_list, list) {
236da5d0027SYevgeny Kliteynik 		list_del(&pattern->list);
237da5d0027SYevgeny Kliteynik 		kfree(pattern->data);
238da5d0027SYevgeny Kliteynik 		kfree(pattern);
239da5d0027SYevgeny Kliteynik 	}
240da5d0027SYevgeny Kliteynik 
241108ff821SYevgeny Kliteynik 	mlx5dr_icm_pool_destroy(mgr->ptrn_icm_pool);
242fe5c2d3aSYevgeny Kliteynik 	mutex_destroy(&mgr->modify_hdr_mutex);
243108ff821SYevgeny Kliteynik 	kfree(mgr);
244108ff821SYevgeny Kliteynik }
245