+static int
+sfc_flow_parse_rte_to_mae(struct rte_eth_dev *dev,
+ const struct rte_flow_item pattern[],
+ const struct rte_flow_action actions[],
+ struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ struct sfc_flow_spec *spec = &flow->spec;
+ struct sfc_flow_spec_mae *spec_mae = &spec->mae;
+ int rc;
+
+ /*
+ * If the flow is meant to be a JUMP rule in tunnel offload,
+ * preparse its actions and save its properties in spec_mae.
+ */
+ rc = sfc_flow_tunnel_detect_jump_rule(sa, actions, spec_mae, error);
+ if (rc != 0)
+ goto fail;
+
+ rc = sfc_mae_rule_parse_pattern(sa, pattern, spec_mae, error);
+ if (rc != 0)
+ goto fail;
+
+ if (spec_mae->ft_rule_type == SFC_FT_RULE_JUMP) {
+ /*
+ * By design, this flow should be represented solely by the
+ * outer rule. But the HW/FW hasn't got support for setting
+ * Rx mark from RECIRC_ID on outer rule lookup yet. Neither
+ * does it support outer rule counters. As a workaround, an
+ * action rule of lower priority is used to do the job.
+ *
+ * So don't skip sfc_mae_rule_parse_actions() below.
+ */
+ }
+
+ rc = sfc_mae_rule_parse_actions(sa, actions, spec_mae, error);
+ if (rc != 0)
+ goto fail;
+
+ if (spec_mae->ft != NULL) {
+ if (spec_mae->ft_rule_type == SFC_FT_RULE_JUMP)
+ spec_mae->ft->jump_rule_is_set = B_TRUE;
+
+ ++(spec_mae->ft->refcnt);
+ }
+
+ return 0;
+
+fail:
+ /* Reset these values to avoid confusing sfc_mae_flow_cleanup(). */
+ spec_mae->ft_rule_type = SFC_FT_RULE_NONE;
+ spec_mae->ft = NULL;
+
+ return rc;
+}
+
+static int
+sfc_flow_parse(struct rte_eth_dev *dev,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item pattern[],
+ const struct rte_flow_action actions[],
+ struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ const struct sfc_flow_ops_by_spec *ops;
+ int rc;
+
+ rc = sfc_flow_parse_attr(sa, attr, flow, error);
+ if (rc != 0)
+ return rc;
+
+ ops = sfc_flow_get_ops_by_spec(flow);
+ if (ops == NULL || ops->parse == NULL) {
+ rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "No backend to handle this flow");
+ return -rte_errno;
+ }
+
+ return ops->parse(dev, pattern, actions, flow, error);
+}
+
+static struct rte_flow *
+sfc_flow_zmalloc(struct rte_flow_error *error)
+{
+ struct rte_flow *flow;
+
+ flow = rte_zmalloc("sfc_rte_flow", sizeof(*flow), 0);
+ if (flow == NULL) {
+ rte_flow_error_set(error, ENOMEM,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to allocate memory");
+ }
+
+ return flow;
+}
+
+static void
+sfc_flow_free(struct sfc_adapter *sa, struct rte_flow *flow)
+{
+ const struct sfc_flow_ops_by_spec *ops;
+
+ ops = sfc_flow_get_ops_by_spec(flow);
+ if (ops != NULL && ops->cleanup != NULL)
+ ops->cleanup(sa, flow);
+
+ rte_free(flow);
+}
+
+static int
+sfc_flow_insert(struct sfc_adapter *sa, struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ const struct sfc_flow_ops_by_spec *ops;
+ int rc;
+
+ ops = sfc_flow_get_ops_by_spec(flow);
+ if (ops == NULL || ops->insert == NULL) {
+ rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "No backend to handle this flow");
+ return rte_errno;
+ }
+
+ rc = ops->insert(sa, flow);
+ if (rc != 0) {
+ rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL, "Failed to insert the flow rule");
+ }
+
+ return rc;
+}
+
+static int
+sfc_flow_remove(struct sfc_adapter *sa, struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ const struct sfc_flow_ops_by_spec *ops;
+ int rc;
+
+ ops = sfc_flow_get_ops_by_spec(flow);
+ if (ops == NULL || ops->remove == NULL) {
+ rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "No backend to handle this flow");
+ return rte_errno;
+ }
+
+ rc = ops->remove(sa, flow);
+ if (rc != 0) {
+ rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL, "Failed to remove the flow rule");
+ }
+
+ return rc;
+}
+
+static int
+sfc_flow_verify(struct sfc_adapter *sa, struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ const struct sfc_flow_ops_by_spec *ops;
+ int rc = 0;
+
+ ops = sfc_flow_get_ops_by_spec(flow);
+ if (ops == NULL) {
+ rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "No backend to handle this flow");
+ return -rte_errno;
+ }
+
+ if (ops->verify != NULL) {
+ SFC_ASSERT(sfc_adapter_is_locked(sa));
+ rc = ops->verify(sa, flow);
+ }
+
+ if (rc != 0) {
+ rte_flow_error_set(error, rc,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to verify flow validity with FW");
+ return -rte_errno;
+ }
+
+ return 0;
+}
+