diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 2a4073c126..892ed2f2d9 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -2627,6 +2627,126 @@ mlx5_flow_validate_item_geneve(const struct rte_flow_item *item,
 	return 0;
 }
 
+/**
+ * Validate Geneve TLV option item.
+ *
+ * @param[in] item
+ *   Item specification.
+ * @param[in] last_item
+ *   Previous validated item in the pattern items.
+ * @param[in] dev
+ *   Pointer to the rte_eth_dev structure.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_flow_validate_item_geneve_opt(const struct rte_flow_item *item,
+				   uint64_t last_item,
+				   struct rte_eth_dev *dev,
+				   struct rte_flow_error *error)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_dev_ctx_shared *sh = priv->sh;
+	struct mlx5_geneve_tlv_option_resource *geneve_opt_resource;
+	struct mlx5_hca_attr *hca_attr = &priv->config.hca_attr;
+	uint8_t data_max_supported =
+			hca_attr->max_geneve_tlv_option_data_len * 4;
+	struct mlx5_dev_config *config = &priv->config;
+	const struct rte_flow_item_geneve_opt *spec = item->spec;
+	const struct rte_flow_item_geneve_opt *mask = item->mask;
+	unsigned int i;
+	unsigned int data_len;
+	const struct rte_flow_item_geneve_opt full_mask = {
+		.option_class = RTE_BE16(0xffff),
+		.option_type = 0xff,
+		.option_len = 0x1f,
+	};
+
+	if (!mask)
+		mask = &rte_flow_item_geneve_opt_mask;
+	if (!spec)
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt class/type/length must be specified");
+	if ((uint32_t)(spec->option_len) > MLX5_GENEVE_OPTLEN_MASK)
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt length exceeeds the limit (31)");
+	/* Check if class type and length masks are full. */
+	if (full_mask.option_class != mask->option_class ||
+	    full_mask.option_type != mask->option_type ||
+	    full_mask.option_len != (mask->option_len & full_mask.option_len))
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt class/type/length masks must be full");
+	/* Check if length is supported */
+	if ((uint32_t)(spec->option_len) >
+			config->hca_attr.max_geneve_tlv_option_data_len)
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt length not supported");
+	if (config->hca_attr.max_geneve_tlv_options > 1)
+		DRV_LOG(DEBUG,
+			"max_geneve_tlv_options supports more than 1 option");
+	/* Check GENEVE item preceding. */
+	if (!(last_item & MLX5_FLOW_LAYER_GENEVE))
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve opt item must be preceded with Geneve item");
+	/* Check if length is 0 or data is 0. */
+	if (spec->data == NULL || spec->option_len == 0)
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt with zero data/length not supported");
+	/* Check not all data & mask are 0. */
+	data_len = spec->option_len * 4;
+	if (mask->data == NULL) {
+		for (i = 0; i < data_len; i++)
+			if (spec->data[i])
+				break;
+		if (i == data_len)
+			return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Can't match on Geneve option data 0");
+	} else {
+		for (i = 0; i < data_len; i++)
+			if (spec->data[i] & mask->data[i])
+				break;
+		if (i == data_len)
+			return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Can't match on Geneve option data and mask 0");
+		/* Check data mask supported. */
+		for (i = data_max_supported; i < data_len ; i++)
+			if (mask->data[i])
+				return rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Data mask is of unsupported size");
+	}
+	/* Check GENEVE option is supported in NIC. */
+	if (!config->hca_attr.geneve_tlv_opt)
+		return rte_flow_error_set
+			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Geneve TLV opt not supported");
+	/* Check if we already have geneve option with different type/class. */
+	rte_spinlock_lock(&sh->geneve_tlv_opt_sl);
+	geneve_opt_resource = sh->geneve_tlv_option_resource;
+	if (geneve_opt_resource != NULL)
+		if (geneve_opt_resource->option_class != spec->option_class ||
+		    geneve_opt_resource->option_type != spec->option_type ||
+		    geneve_opt_resource->length != spec->option_len) {
+			rte_spinlock_unlock(&sh->geneve_tlv_opt_sl);
+			return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Only one Geneve TLV option supported");
+		}
+	rte_spinlock_unlock(&sh->geneve_tlv_opt_sl);
+	return 0;
+}
+
 /**
  * Validate MPLS item.
  *
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 0c8861964c..28db5faee1 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -138,6 +138,9 @@ enum mlx5_feature_name {
 #define MLX5_FLOW_LAYER_OUTER_L3_IPV6_FRAG_EXT (1u << 30)
 #define MLX5_FLOW_LAYER_INNER_L3_IPV6_FRAG_EXT (1u << 31)
 
+/* Pattern tunnel Layer bits (continued). */
+#define MLX5_FLOW_LAYER_GENEVE_OPT (UINT64_C(1) << 32)
+
 /* Outer Masks. */
 #define MLX5_FLOW_LAYER_OUTER_L3 \
 	(MLX5_FLOW_LAYER_OUTER_L3_IPV4 | MLX5_FLOW_LAYER_OUTER_L3_IPV6)
@@ -1396,6 +1399,10 @@ int mlx5_flow_validate_item_geneve(const struct rte_flow_item *item,
 				   uint64_t item_flags,
 				   struct rte_eth_dev *dev,
 				   struct rte_flow_error *error);
+int mlx5_flow_validate_item_geneve_opt(const struct rte_flow_item *item,
+				   uint64_t last_item,
+				   struct rte_eth_dev *dev,
+				   struct rte_flow_error *error);
 int mlx5_flow_validate_item_ecpri(const struct rte_flow_item *item,
 				  uint64_t item_flags,
 				  uint64_t last_item,
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 0a657cea41..c24123fbf3 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -5520,6 +5520,15 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 				return ret;
 			last_item = MLX5_FLOW_LAYER_GENEVE;
 			break;
+		case RTE_FLOW_ITEM_TYPE_GENEVE_OPT:
+			ret = mlx5_flow_validate_item_geneve_opt(items,
+								 last_item,
+								 dev,
+								 error);
+			if (ret < 0)
+				return ret;
+			last_item = MLX5_FLOW_LAYER_GENEVE_OPT;
+			break;
 		case RTE_FLOW_ITEM_TYPE_MPLS:
 			ret = mlx5_flow_validate_item_mpls(dev, items,
 							   item_flags,
@@ -5528,7 +5537,6 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 				return ret;
 			last_item = MLX5_FLOW_LAYER_MPLS;
 			break;
-
 		case RTE_FLOW_ITEM_TYPE_MARK:
 			ret = flow_dv_validate_item_mark(dev, items, attr,
 							 error);
