Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2025 Intel Corporation
3 : : */
4 : :
5 : : #ifndef _COMMON_INTEL_FLOW_CHECK_H_
6 : : #define _COMMON_INTEL_FLOW_CHECK_H_
7 : :
8 : : #include <bus_pci_driver.h>
9 : : #include <ethdev_driver.h>
10 : :
11 : : #include "log.h"
12 : :
13 : : #ifdef __cplusplus
14 : : extern "C" {
15 : : #endif
16 : :
17 : : /*
18 : : * Common attr and action validation code for Intel drivers.
19 : : */
20 : :
21 : : /**
22 : : * Maximum number of actions that can be stored in a parsed action list.
23 : : */
24 : : #define CI_FLOW_PARSED_ACTIONS_MAX 32
25 : :
26 : : /* Actions that are reasonably expected to have a conf structure */
27 : : static const enum rte_flow_action_type need_conf[] = {
28 : : RTE_FLOW_ACTION_TYPE_QUEUE,
29 : : RTE_FLOW_ACTION_TYPE_RSS,
30 : : RTE_FLOW_ACTION_TYPE_VF,
31 : : RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR,
32 : : RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT,
33 : : RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP,
34 : : RTE_FLOW_ACTION_TYPE_PROG,
35 : : RTE_FLOW_ACTION_TYPE_COUNT,
36 : : RTE_FLOW_ACTION_TYPE_MARK,
37 : : RTE_FLOW_ACTION_TYPE_SECURITY,
38 : : RTE_FLOW_ACTION_TYPE_END
39 : : };
40 : :
41 : : /**
42 : : * Is action type in this list of action types?
43 : : */
44 : : static inline bool
45 : : ci_flow_action_type_in_list(const enum rte_flow_action_type type,
46 : : const enum rte_flow_action_type list[])
47 : : {
48 : : size_t i = 0;
49 [ # # # # : 0 : while (list[i] != RTE_FLOW_ACTION_TYPE_END) {
# # # # ]
50 [ # # # # : 0 : if (type == list[i])
# # # # ]
51 : : return true;
52 : 0 : i++;
53 : : }
54 : : return false;
55 : : }
56 : :
57 : : /* Forward declarations */
58 : : struct ci_flow_actions;
59 : : struct ci_flow_actions_check_param;
60 : : struct ci_flow_attr_check_param;
61 : :
62 : : static inline const char *
63 : 0 : ci_flow_action_type_to_str(enum rte_flow_action_type type)
64 : : {
65 : 0 : const char *name = NULL;
66 : : int ret;
67 : :
68 : 0 : ret = rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
69 : 0 : &name, sizeof(name), (const void *)(uintptr_t)type, NULL);
70 [ # # # # ]: 0 : if (ret < 0 || name == NULL)
71 : 0 : return "UNKNOWN";
72 : :
73 : : return name;
74 : : }
75 : :
76 : : /**
77 : : * Driver-specific action list validation callback.
78 : : *
79 : : * Performs driver-specific validation of action parameter list.
80 : : * Called after all actions have been parsed and added to the list,
81 : : * allowing validation based on the complete action set.
82 : : *
83 : : * @param actions
84 : : * The complete list of parsed actions (for context-dependent validation).
85 : : * @param driver_ctx
86 : : * Opaque driver context (e.g., adapter/queue configuration).
87 : : * @param error
88 : : * Pointer to rte_flow_error for reporting failures.
89 : : * @return
90 : : * 0 on success, negative errno on failure.
91 : : */
92 : : typedef int (*ci_flow_actions_check_fn)(const struct ci_flow_actions *actions,
93 : : const struct ci_flow_actions_check_param *param,
94 : : struct rte_flow_error *error);
95 : :
96 : : /**
97 : : * List of actions that we know we've validated.
98 : : */
99 : : struct ci_flow_actions {
100 : : /* Number of actions in the list. */
101 : : uint8_t count;
102 : : /* Parsed actions array. */
103 : : struct rte_flow_action const *actions[CI_FLOW_PARSED_ACTIONS_MAX];
104 : : };
105 : :
106 : : /**
107 : : * Parameters for action list validation. Any element can be NULL/0 as checks are only performed
108 : : * against constraints specified.
109 : : */
110 : : struct ci_flow_actions_check_param {
111 : : /**
112 : : * Driver-specific context pointer (e.g., adapter/queue configuration). Can be NULL.
113 : : */
114 : : void *driver_ctx;
115 : : /**
116 : : * Driver-specific action list validation callback. Can be NULL.
117 : : */
118 : : ci_flow_actions_check_fn check;
119 : : /**
120 : : * Allowed action types for this parse parameter. Must be terminated with
121 : : * RTE_FLOW_ACTION_TYPE_END. Can be NULL.
122 : : */
123 : : const enum rte_flow_action_type *allowed_types;
124 : : size_t max_actions; /**< Maximum number of actions allowed. */
125 : : bool rss_queues_contig; /**< If true, RSS queues must be contiguous. */
126 : : };
127 : :
128 : : static inline int
129 : 0 : __flow_action_check_rss(const struct rte_flow_action_rss *rss,
130 : : const struct ci_flow_actions_check_param *param,
131 : : struct rte_flow_error *error)
132 : : {
133 : : uint32_t qnum, q;
134 : :
135 : 0 : qnum = rss->queue_num;
136 : :
137 : : /* either we have both queues and queue number, or we have neither */
138 [ # # ]: 0 : if ((qnum == 0) != (rss->queue == NULL)) {
139 : 0 : return rte_flow_error_set(error, EINVAL,
140 : : RTE_FLOW_ERROR_TYPE_ACTION_CONF, rss,
141 : : "If queue number is specified, queue array must also be specified");
142 : : }
143 : : /* check if queues are monotonic */
144 [ # # ]: 0 : for (q = 1; q < qnum; q++) {
145 [ # # ]: 0 : if (rss->queue[q] < rss->queue[q - 1]) {
146 : 0 : return rte_flow_error_set(error, EINVAL,
147 : : RTE_FLOW_ERROR_TYPE_ACTION_CONF, rss,
148 : : "RSS queues must be in ascending order");
149 : : }
150 : : /* if user has requested contiguousness, check that as well */
151 [ # # # # ]: 0 : if (param == NULL || !param->rss_queues_contig)
152 : 0 : continue;
153 [ # # ]: 0 : if (rss->queue[q] != rss->queue[0] + q) {
154 : 0 : return rte_flow_error_set(error, EINVAL,
155 : : RTE_FLOW_ERROR_TYPE_ACTION_CONF, rss,
156 : : "RSS queues must be contiguous");
157 : : }
158 : : }
159 : :
160 : : /* either we have both key and key length, or we have neither */
161 [ # # ]: 0 : if ((rss->key_len == 0) != (rss->key == NULL)) {
162 : 0 : return rte_flow_error_set(error, EINVAL,
163 : : RTE_FLOW_ERROR_TYPE_ACTION_CONF, rss,
164 : : "If RSS key is specified, key length must also be specified");
165 : : }
166 : : return 0;
167 : : }
168 : :
169 : : static inline int
170 : 0 : __flow_action_check_generic(const struct rte_flow_action *action,
171 : : const struct ci_flow_actions_check_param *param,
172 : : struct rte_flow_error *error)
173 : : {
174 : : /* is this action in our allowed list? */
175 [ # # # # : 0 : if (param != NULL && param->allowed_types != NULL &&
# # ]
176 : 0 : !ci_flow_action_type_in_list(action->type, param->allowed_types)) {
177 : 0 : return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
178 : : action, "Unsupported action");
179 : : }
180 : : /* do we need to validate presence of conf? */
181 [ # # ]: 0 : if (ci_flow_action_type_in_list(action->type, need_conf)) {
182 [ # # ]: 0 : if (action->conf == NULL) {
183 : 0 : return rte_flow_error_set(error, EINVAL,
184 : : RTE_FLOW_ERROR_TYPE_ACTION_CONF, action,
185 : : "Action requires configuration");
186 : : }
187 : : }
188 : :
189 : : /* type-specific validation */
190 [ # # ]: 0 : switch (action->type) {
191 : 0 : case RTE_FLOW_ACTION_TYPE_RSS:
192 : : {
193 : 0 : const struct rte_flow_action_rss *rss =
194 : : (const struct rte_flow_action_rss *)action->conf;
195 : : int ret;
196 : :
197 : 0 : ret = __flow_action_check_rss(rss, param, error);
198 : : if (ret < 0)
199 : : return ret;
200 : : break;
201 : : }
202 : : default:
203 : : /* no specific validation */
204 : : break;
205 : : }
206 : :
207 : : return 0;
208 : : }
209 : :
210 : : /**
211 : : * Validate and parse a list of rte_flow_action into a parsed action list.
212 : : *
213 : : * @param actions pointer to array of rte_flow_action, terminated by RTE_FLOW_ACTION_TYPE_END
214 : : * @param param pointer to ci_flow_actions_check_param structure (can be NULL)
215 : : * @param parsed_actions pointer to ci_flow_actions structure to store parsed actions
216 : : * @param error pointer to rte_flow_error structure for error reporting
217 : : *
218 : : * @return 0 on success, negative errno on failure.
219 : : */
220 : : static inline int
221 : 0 : ci_flow_check_actions(const struct rte_flow_action *actions,
222 : : const struct ci_flow_actions_check_param *param,
223 : : struct ci_flow_actions *parsed_actions,
224 : : struct rte_flow_error *error)
225 : : {
226 : : size_t i = 0;
227 : : int ret;
228 : :
229 [ # # ]: 0 : if (actions == NULL) {
230 : 0 : return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
231 : : NULL, "Missing actions");
232 : : }
233 : :
234 : : /* reset the list */
235 : 0 : *parsed_actions = (struct ci_flow_actions){0};
236 : :
237 [ # # ]: 0 : while (actions[i].type != RTE_FLOW_ACTION_TYPE_END) {
238 : 0 : const struct rte_flow_action *action = &actions[i++];
239 : :
240 : : /* skip VOID actions */
241 [ # # ]: 0 : if (action->type == RTE_FLOW_ACTION_TYPE_VOID)
242 : 0 : continue;
243 : :
244 : : /* generic validation for actions - this will check against param as well */
245 : 0 : ret = __flow_action_check_generic(action, param, error);
246 [ # # ]: 0 : if (ret < 0)
247 : 0 : return ret;
248 : :
249 : : /* check against global maximum number of actions */
250 [ # # ]: 0 : if (parsed_actions->count >= RTE_DIM(parsed_actions->actions)) {
251 : 0 : return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
252 : : action, "Too many actions");
253 : : }
254 : : /* user may have specified a maximum number of actions */
255 [ # # # # ]: 0 : if (param != NULL && param->max_actions != 0 &&
256 [ # # ]: 0 : parsed_actions->count >= param->max_actions) {
257 : 0 : return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
258 : : action, "Too many actions");
259 : : }
260 : : /* add action to the list */
261 : 0 : CI_DRV_LOG(DEBUG, "Parsed action %u: type=%s", parsed_actions->count,
262 : : ci_flow_action_type_to_str(action->type));
263 : 0 : parsed_actions->actions[parsed_actions->count++] = action;
264 : : }
265 : :
266 : : /* if we didn't parse anything, valid action list is empty */
267 [ # # ]: 0 : if (parsed_actions->count == 0) {
268 : 0 : return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
269 : : NULL, "No valid actions specified");
270 : : }
271 : :
272 : : /* now, call into user validation if specified */
273 [ # # # # ]: 0 : if (param != NULL && param->check != NULL) {
274 : 0 : ret = param->check(parsed_actions, param, error);
275 : : if (ret < 0)
276 : : return ret;
277 : : }
278 : : return 0;
279 : : }
280 : :
281 : : /**
282 : : * Parameter structure for attr check.
283 : : */
284 : : struct ci_flow_attr_check_param {
285 : : bool allow_priority; /**< True if priority attribute is allowed. */
286 : : bool allow_transfer; /**< True if transfer attribute is allowed. */
287 : : bool allow_group; /**< True if group attribute is allowed. */
288 : : bool require_egress; /**< True if egress attribute is required. */
289 : : };
290 : :
291 : : /**
292 : : * Validate rte_flow_attr structure against specified constraints.
293 : : *
294 : : * @param attr Pointer to rte_flow_attr structure to validate.
295 : : * @param attr_param Pointer to ci_flow_attr_check_param structure specifying constraints.
296 : : * @param error Pointer to rte_flow_error structure for error reporting.
297 : : *
298 : : * @return 0 on success, negative errno on failure.
299 : : */
300 : : static inline int
301 : 0 : ci_flow_check_attr(const struct rte_flow_attr *attr,
302 : : const struct ci_flow_attr_check_param *attr_param,
303 : : struct rte_flow_error *error)
304 : : {
305 [ # # ]: 0 : if (attr == NULL) {
306 : 0 : return rte_flow_error_set(error, EINVAL,
307 : : RTE_FLOW_ERROR_TYPE_ATTR, attr,
308 : : "NULL attribute");
309 : : }
310 : :
311 : : /* a rule can only be ingress or egress, never both or neither. */
312 [ # # # # ]: 0 : if (attr_param != NULL && attr_param->require_egress) {
313 [ # # ]: 0 : if (attr->egress != 1 || attr->ingress != 0) {
314 : 0 : return rte_flow_error_set(error, EINVAL,
315 : : RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr,
316 : : "Egress attribute is required");
317 : : }
318 : : } else {
319 [ # # ]: 0 : if (attr->ingress != 1 || attr->egress != 0) {
320 : 0 : return rte_flow_error_set(error, EINVAL,
321 : : RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr,
322 : : "Ingress attribute is required");
323 : : }
324 : : }
325 : :
326 : : /* May not be supported */
327 [ # # # # : 0 : if (attr->transfer && (attr_param == NULL || !attr_param->allow_transfer)) {
# # ]
328 : 0 : return rte_flow_error_set(error, EINVAL,
329 : : RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER, attr,
330 : : "Transfer not supported");
331 : : }
332 : :
333 : : /* May not be supported */
334 [ # # # # : 0 : if (attr->group && (attr_param == NULL || !attr_param->allow_group)) {
# # ]
335 : 0 : return rte_flow_error_set(error, EINVAL,
336 : : RTE_FLOW_ERROR_TYPE_ATTR_GROUP, attr,
337 : : "Group not supported");
338 : : }
339 : :
340 : : /* May not be supported */
341 [ # # # # : 0 : if (attr->priority && (attr_param == NULL || !attr_param->allow_priority)) {
# # ]
342 : 0 : return rte_flow_error_set(error, EINVAL,
343 : : RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr,
344 : : "Priority not supported");
345 : : }
346 : :
347 : : return 0;
348 : : }
349 : :
350 : : #ifdef __cplusplus
351 : : }
352 : : #endif
353 : :
354 : : #endif /* _COMMON_INTEL_FLOW_CHECK_H_ */
|