diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
index 948c65968a..6a8ccd564b 100644
--- a/drivers/net/sfc/meson.build
+++ b/drivers/net/sfc/meson.build
@@ -90,6 +90,7 @@ sources = files(
         'sfc_mae.c',
         'sfc_mae_counter.c',
         'sfc_flow.c',
+	'sfc_flow_tunnel.c',
         'sfc_dp.c',
         'sfc_ef10_rx.c',
         'sfc_ef10_essb_rx.c',
diff --git a/drivers/net/sfc/sfc_dp_rx.h b/drivers/net/sfc/sfc_dp_rx.h
index b6c44085ce..1be301ced6 100644
--- a/drivers/net/sfc/sfc_dp_rx.h
+++ b/drivers/net/sfc/sfc_dp_rx.h
@@ -92,6 +92,9 @@ struct sfc_dp_rx_qcreate_info {
 	efsys_dma_addr_t	fcw_offset;
 	/** VI window size shift */
 	unsigned int		vi_window_shift;
+
+	/** Mask to extract user bits from Rx prefix mark field */
+	uint32_t		user_mark_mask;
 };
 
 /**
diff --git a/drivers/net/sfc/sfc_ef100_rx.c b/drivers/net/sfc/sfc_ef100_rx.c
index 7d0d6b3d00..3219c972db 100644
--- a/drivers/net/sfc/sfc_ef100_rx.c
+++ b/drivers/net/sfc/sfc_ef100_rx.c
@@ -20,7 +20,9 @@
 #include "efx_regs_ef100.h"
 #include "efx.h"
 
+#include "sfc.h"
 #include "sfc_debug.h"
+#include "sfc_flow_tunnel.h"
 #include "sfc_tweak.h"
 #include "sfc_dp_rx.h"
 #include "sfc_kvargs.h"
@@ -74,6 +76,7 @@ struct sfc_ef100_rxq {
 	uint64_t			rearm_data;
 	uint16_t			buf_size;
 	uint16_t			prefix_size;
+	uint32_t			user_mark_mask;
 
 	unsigned int			evq_hw_index;
 	volatile void			*evq_prime;
@@ -420,10 +423,13 @@ sfc_ef100_rx_prefix_to_offloads(const struct sfc_ef100_rxq *rxq,
 
 	if (rxq->flags & SFC_EF100_RXQ_USER_MARK) {
 		uint32_t user_mark;
+		uint32_t mark;
 
 		/* EFX_OWORD_FIELD converts little-endian to CPU */
-		user_mark = EFX_OWORD_FIELD(rx_prefix[0],
-					    ESF_GZ_RX_PREFIX_USER_MARK);
+		mark = EFX_OWORD_FIELD(rx_prefix[0],
+				       ESF_GZ_RX_PREFIX_USER_MARK);
+
+		user_mark = mark & rxq->user_mark_mask;
 		if (user_mark != SFC_EF100_USER_MARK_INVALID) {
 			ol_flags |= PKT_RX_FDIR | PKT_RX_FDIR_ID;
 			m->hash.fdir.hi = user_mark;
@@ -745,6 +751,10 @@ sfc_ef100_rx_qcreate(uint16_t port_id, uint16_t queue_id,
 	rxq->max_fill_level = info->max_fill_level;
 	rxq->refill_threshold = info->refill_threshold;
 	rxq->prefix_size = info->prefix_size;
+
+	SFC_ASSERT(info->user_mark_mask != 0);
+	rxq->user_mark_mask = info->user_mark_mask;
+
 	rxq->buf_size = info->buf_size;
 	rxq->refill_mb_pool = info->refill_mb_pool;
 	rxq->rxq_hw_ring = info->rxq_hw_ring;
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 75c3da2e52..ab25674929 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -26,6 +26,7 @@
 #include "sfc_rx.h"
 #include "sfc_tx.h"
 #include "sfc_flow.h"
+#include "sfc_flow_tunnel.h"
 #include "sfc_dp.h"
 #include "sfc_dp_rx.h"
 #include "sfc_sw_stats.h"
@@ -1873,6 +1874,9 @@ sfc_rx_meta_negotiate(struct rte_eth_dev *dev, uint64_t *features)
 	if ((sa->priv.dp_rx->features & SFC_DP_RX_FEAT_FLOW_MARK) != 0)
 		supported |= RTE_ETH_RX_META_USER_MARK;
 
+	if (sfc_flow_tunnel_is_supported(sa))
+		supported |= RTE_ETH_RX_META_TUNNEL_ID;
+
 	sa->negotiated_rx_meta = supported & *features;
 	*features = sa->negotiated_rx_meta;
 
diff --git a/drivers/net/sfc/sfc_flow.c b/drivers/net/sfc/sfc_flow.c
index 57cf1ad02b..7510cbb95b 100644
--- a/drivers/net/sfc/sfc_flow.c
+++ b/drivers/net/sfc/sfc_flow.c
@@ -22,6 +22,7 @@
 #include "sfc_rx.h"
 #include "sfc_filter.h"
 #include "sfc_flow.h"
+#include "sfc_flow_tunnel.h"
 #include "sfc_log.h"
 #include "sfc_dp_rx.h"
 #include "sfc_mae_counter.h"
@@ -1740,8 +1741,13 @@ sfc_flow_parse_mark(struct sfc_adapter *sa,
 	struct sfc_flow_spec *spec = &flow->spec;
 	struct sfc_flow_spec_filter *spec_filter = &spec->filter;
 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
+	uint32_t mark_max;
 
-	if (mark == NULL || mark->id > encp->enc_filter_action_mark_max)
+	mark_max = encp->enc_filter_action_mark_max;
+	if (sfc_flow_tunnel_is_active(sa))
+		mark_max = RTE_MIN(mark_max, SFC_FT_USER_MARK_MASK);
+
+	if (mark == NULL || mark->id > mark_max)
 		return EINVAL;
 
 	spec_filter->template.efs_flags |= EFX_FILTER_FLAG_ACTION_MARK;
diff --git a/drivers/net/sfc/sfc_flow_tunnel.c b/drivers/net/sfc/sfc_flow_tunnel.c
new file mode 100644
index 0000000000..06b4a27a65
--- /dev/null
+++ b/drivers/net/sfc/sfc_flow_tunnel.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2021 Xilinx, Inc.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "sfc.h"
+#include "sfc_dp_rx.h"
+#include "sfc_flow_tunnel.h"
+#include "sfc_mae.h"
+
+bool
+sfc_flow_tunnel_is_supported(struct sfc_adapter *sa)
+{
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	return ((sa->priv.dp_rx->features & SFC_DP_RX_FEAT_FLOW_MARK) != 0 &&
+		sa->mae.status == SFC_MAE_STATUS_SUPPORTED);
+}
+
+bool
+sfc_flow_tunnel_is_active(struct sfc_adapter *sa)
+{
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	return ((sa->negotiated_rx_meta & RTE_ETH_RX_META_TUNNEL_ID) != 0);
+}
diff --git a/drivers/net/sfc/sfc_flow_tunnel.h b/drivers/net/sfc/sfc_flow_tunnel.h
new file mode 100644
index 0000000000..fec891fdad
--- /dev/null
+++ b/drivers/net/sfc/sfc_flow_tunnel.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2021 Xilinx, Inc.
+ */
+
+#ifndef _SFC_FLOW_TUNNEL_H
+#define _SFC_FLOW_TUNNEL_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Flow Tunnel (FT) SW entry ID */
+typedef uint8_t sfc_ft_id_t;
+
+#define SFC_FT_TUNNEL_MARK_BITS \
+	(sizeof(sfc_ft_id_t) * CHAR_BIT)
+
+#define SFC_FT_USER_MARK_BITS \
+	(sizeof(uint32_t) * CHAR_BIT - SFC_FT_TUNNEL_MARK_BITS)
+
+#define SFC_FT_USER_MARK_MASK \
+	RTE_LEN2MASK(SFC_FT_USER_MARK_BITS, uint32_t)
+
+struct sfc_adapter;
+
+bool sfc_flow_tunnel_is_supported(struct sfc_adapter *sa);
+
+bool sfc_flow_tunnel_is_active(struct sfc_adapter *sa);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SFC_FLOW_TUNNEL_H */
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index 5ecad7347a..2b80492e59 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -16,6 +16,7 @@
 #include "efx.h"
 
 #include "sfc.h"
+#include "sfc_flow_tunnel.h"
 #include "sfc_mae_counter.h"
 #include "sfc_log.h"
 #include "sfc_switch.h"
@@ -2793,6 +2794,11 @@ sfc_mae_rule_parse_action_mark(struct sfc_adapter *sa,
 {
 	int rc;
 
+	if (conf->id > SFC_FT_USER_MARK_MASK) {
+		sfc_err(sa, "the mark value is too large");
+		return EINVAL;
+	}
+
 	rc = efx_mae_action_set_populate_mark(spec, conf->id);
 	if (rc != 0)
 		sfc_err(sa, "failed to request action MARK: %s", strerror(rc));
diff --git a/drivers/net/sfc/sfc_rx.c b/drivers/net/sfc/sfc_rx.c
index a3331c5089..3ef43d7c38 100644
--- a/drivers/net/sfc/sfc_rx.c
+++ b/drivers/net/sfc/sfc_rx.c
@@ -13,6 +13,7 @@
 
 #include "sfc.h"
 #include "sfc_debug.h"
+#include "sfc_flow_tunnel.h"
 #include "sfc_log.h"
 #include "sfc_ev.h"
 #include "sfc_rx.h"
@@ -1181,7 +1182,8 @@ sfc_rx_qinit(struct sfc_adapter *sa, sfc_sw_index_t sw_index,
 	if ((sa->negotiated_rx_meta & RTE_ETH_RX_META_USER_FLAG) != 0)
 		rxq_info->type_flags |= EFX_RXQ_FLAG_USER_FLAG;
 
-	if ((sa->negotiated_rx_meta & RTE_ETH_RX_META_USER_MARK) != 0)
+	if ((sa->negotiated_rx_meta & RTE_ETH_RX_META_USER_MARK) != 0 ||
+	    sfc_flow_tunnel_is_active(sa))
 		rxq_info->type_flags |= EFX_RXQ_FLAG_USER_MARK;
 
 	rc = sfc_ev_qinit(sa, SFC_EVQ_TYPE_RX, sw_index,
@@ -1231,6 +1233,12 @@ sfc_rx_qinit(struct sfc_adapter *sa, sfc_sw_index_t sw_index,
 	info.buf_size = buf_size;
 	info.batch_max = encp->enc_rx_batch_max;
 	info.prefix_size = encp->enc_rx_prefix_size;
+
+	if (sfc_flow_tunnel_is_active(sa))
+		info.user_mark_mask = SFC_FT_USER_MARK_MASK;
+	else
+		info.user_mark_mask = UINT32_MAX;
+
 	info.flags = rxq_info->rxq_flags;
 	info.rxq_entries = rxq_info->entries;
 	info.rxq_hw_ring = rxq->mem.esm_base;
