diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 331e06bac6..2812d76cbb 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -312,6 +312,8 @@ struct sfc_adapter {
 	boolean_t			tso;
 	boolean_t			tso_encap;
 
+	uint64_t			negotiated_rx_meta;
+
 	uint32_t			rxd_wait_timeout_ns;
 };
 
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 2db0d000c3..57bcccdb1b 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -1859,6 +1859,27 @@ sfc_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t ethdev_qid)
 	return sap->dp_rx->intr_disable(rxq_info->dp);
 }
 
+static int
+sfc_negotiate_rx_meta(struct rte_eth_dev *dev, uint64_t *features)
+{
+	struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+
+	sfc_adapter_lock(sa);
+
+	if ((sa->priv.dp_rx->features & SFC_DP_RX_FEAT_FLOW_FLAG) != 0)
+		sa->negotiated_rx_meta |= RTE_ETH_RX_META_USER_FLAG;
+
+	if ((sa->priv.dp_rx->features & SFC_DP_RX_FEAT_FLOW_MARK) != 0)
+		sa->negotiated_rx_meta |= RTE_ETH_RX_META_USER_MARK;
+
+	sa->negotiated_rx_meta &= *features;
+	*features = sa->negotiated_rx_meta;
+
+	sfc_adapter_unlock(sa);
+
+	return 0;
+}
+
 static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_configure			= sfc_dev_configure,
 	.dev_start			= sfc_dev_start,
@@ -1906,6 +1927,7 @@ static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.xstats_get_by_id		= sfc_xstats_get_by_id,
 	.xstats_get_names_by_id		= sfc_xstats_get_names_by_id,
 	.pool_ops_supported		= sfc_pool_ops_supported,
+	.negotiate_rx_meta		= sfc_negotiate_rx_meta,
 };
 
 /**
@@ -1998,6 +2020,18 @@ sfc_eth_dev_set_ops(struct rte_eth_dev *dev)
 		goto fail_dp_rx_name;
 	}
 
+	if (strcmp(dp_rx->dp.name, SFC_KVARG_DATAPATH_EF10_ESSB) == 0) {
+		/*
+		 * Datapath EF10 ESSB is available only on EF10 NICs running
+		 * Rx FW variant DPDK, which always provides fields FLAG and
+		 * MARK in Rx prefix, so point this fact out below. This way,
+		 * legacy applications from EF10 era, which are not aware of
+		 * rte_eth_negotiate_rx_meta(), can keep the workflow intact.
+		 */
+		sa->negotiated_rx_meta |= RTE_ETH_RX_META_USER_FLAG;
+		sa->negotiated_rx_meta |= RTE_ETH_RX_META_USER_MARK;
+	}
+
 	sfc_notice(sa, "use %s Rx datapath", sas->dp_rx_name);
 
 	rc = sfc_kvargs_process(sa, SFC_KVARG_TX_DATAPATH,
diff --git a/drivers/net/sfc/sfc_flow.c b/drivers/net/sfc/sfc_flow.c
index 4f5993a68d..a2034b5f5e 100644
--- a/drivers/net/sfc/sfc_flow.c
+++ b/drivers/net/sfc/sfc_flow.c
@@ -1759,7 +1759,7 @@ sfc_flow_parse_actions(struct sfc_adapter *sa,
 	int rc;
 	struct sfc_flow_spec *spec = &flow->spec;
 	struct sfc_flow_spec_filter *spec_filter = &spec->filter;
-	const unsigned int dp_rx_features = sa->priv.dp_rx->features;
+	const uint64_t rx_meta = sa->negotiated_rx_meta;
 	uint32_t actions_set = 0;
 	const uint32_t fate_actions_mask = (1UL << RTE_FLOW_ACTION_TYPE_QUEUE) |
 					   (1UL << RTE_FLOW_ACTION_TYPE_RSS) |
@@ -1827,10 +1827,10 @@ sfc_flow_parse_actions(struct sfc_adapter *sa,
 			if ((actions_set & mark_actions_mask) != 0)
 				goto fail_actions_overlap;
 
-			if ((dp_rx_features & SFC_DP_RX_FEAT_FLOW_FLAG) == 0) {
+			if ((rx_meta & RTE_ETH_RX_META_USER_FLAG) == 0) {
 				rte_flow_error_set(error, ENOTSUP,
 					RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-					"FLAG action is not supported on the current Rx datapath");
+					"Action FLAG is unsupported on the current Rx datapath or has not been negotiated");
 				return -rte_errno;
 			}
 
@@ -1844,10 +1844,10 @@ sfc_flow_parse_actions(struct sfc_adapter *sa,
 			if ((actions_set & mark_actions_mask) != 0)
 				goto fail_actions_overlap;
 
-			if ((dp_rx_features & SFC_DP_RX_FEAT_FLOW_MARK) == 0) {
+			if ((rx_meta & RTE_ETH_RX_META_USER_MARK) == 0) {
 				rte_flow_error_set(error, ENOTSUP,
 					RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-					"MARK action is not supported on the current Rx datapath");
+					"Action MARK is unsupported on the current Rx datapath or has not been negotiated");
 				return -rte_errno;
 			}
 
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index 4b520bc619..89c161ef88 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -2963,6 +2963,7 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 			  efx_mae_actions_t *spec,
 			  struct rte_flow_error *error)
 {
+	const uint64_t rx_meta = sa->negotiated_rx_meta;
 	bool custom_error = B_FALSE;
 	int rc = 0;
 
@@ -3012,12 +3013,29 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 	case RTE_FLOW_ACTION_TYPE_FLAG:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_FLAG,
 				       bundle->actions_mask);
-		rc = efx_mae_action_set_populate_flag(spec);
+		if ((rx_meta & RTE_ETH_RX_META_USER_FLAG) != 0) {
+			rc = efx_mae_action_set_populate_flag(spec);
+		} else {
+			rc = rte_flow_error_set(error, ENOTSUP,
+						RTE_FLOW_ERROR_TYPE_ACTION,
+						action,
+						"Action FLAG has not been negotiated");
+			custom_error = B_TRUE;
+		}
 		break;
 	case RTE_FLOW_ACTION_TYPE_MARK:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_MARK,
 				       bundle->actions_mask);
-		rc = sfc_mae_rule_parse_action_mark(sa, action->conf, spec);
+		if ((rx_meta & RTE_ETH_RX_META_USER_MARK) != 0) {
+			rc = sfc_mae_rule_parse_action_mark(sa, action->conf,
+							    spec);
+		} else {
+			rc = rte_flow_error_set(error, ENOTSUP,
+						RTE_FLOW_ERROR_TYPE_ACTION,
+						action,
+						"Action MARK has not been negotiated");
+			custom_error = B_TRUE;
+		}
 		break;
 	case RTE_FLOW_ACTION_TYPE_PHY_PORT:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PHY_PORT,
