diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index 72c30bf..1b86b7b 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -2341,6 +2341,17 @@ struct mlx5_flow_id_pool *
 		goto error;
 	}
 	priv->config.flow_prio = err;
+	/* Query availibility of metadata reg_c's. */
+	err = mlx5_flow_discover_mreg_c(eth_dev);
+	if (err < 0) {
+		err = -err;
+		goto error;
+	}
+	if (!mlx5_flow_ext_mreg_supported(eth_dev)) {
+		DRV_LOG(DEBUG,
+			"port %u extensive metadata register is not supported",
+			eth_dev->data->port_id);
+	}
 	return eth_dev;
 error:
 	if (priv) {
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index f644998..6b82c6d 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -37,6 +37,7 @@
 #include "mlx5_autoconf.h"
 #include "mlx5_defs.h"
 #include "mlx5_glue.h"
+#include "mlx5_prm.h"
 
 enum {
 	PCI_VENDOR_ID_MELLANOX = 0x15b3,
@@ -252,6 +253,8 @@ struct mlx5_dev_config {
 	} mprq; /* Configurations for Multi-Packet RQ. */
 	int mps; /* Multi-packet send supported mode. */
 	unsigned int flow_prio; /* Number of flow priorities. */
+	enum modify_reg flow_mreg_c[MLX5_MREG_C_NUM];
+	/* Availibility of mreg_c's. */
 	unsigned int tso_max_payload_sz; /* Maximum TCP payload for TSO. */
 	unsigned int ind_table_max_size; /* Maximum indirection table size. */
 	unsigned int max_dump_files_num; /* Maximum dump files per queue. */
@@ -561,6 +564,10 @@ struct mlx5_flow_tbl_resource {
 
 #define MLX5_MAX_TABLES UINT16_MAX
 #define MLX5_HAIRPIN_TX_TABLE (UINT16_MAX - 1)
+/* Reserve the last two tables for metadata register copy. */
+#define MLX5_FLOW_MREG_ACT_TABLE_GROUP (MLX5_MAX_TABLES - 1)
+#define MLX5_FLOW_MREG_CP_TABLE_GROUP \
+	(MLX5_FLOW_MREG_ACT_TABLE_GROUP - 1)
 #define MLX5_MAX_TABLES_FDB UINT16_MAX
 
 #define MLX5_DBR_PAGE_SIZE 4096 /* Must be >= 512. */
@@ -786,7 +793,7 @@ int mlx5_dev_to_pci_addr(const char *dev_path,
 int mlx5_is_removed(struct rte_eth_dev *dev);
 eth_tx_burst_t mlx5_select_tx_function(struct rte_eth_dev *dev);
 eth_rx_burst_t mlx5_select_rx_function(struct rte_eth_dev *dev);
-struct mlx5_priv *mlx5_port_to_eswitch_info(uint16_t port);
+struct mlx5_priv *mlx5_port_to_eswitch_info(uint16_t port, bool valid);
 struct mlx5_priv *mlx5_dev_to_eswitch_info(struct rte_eth_dev *dev);
 int mlx5_sysfs_switch_info(unsigned int ifindex,
 			   struct mlx5_switch_info *info);
@@ -866,6 +873,8 @@ int mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
 
 /* mlx5_flow.c */
 
+int mlx5_flow_discover_mreg_c(struct rte_eth_dev *eth_dev);
+bool mlx5_flow_ext_mreg_supported(struct rte_eth_dev *dev);
 int mlx5_flow_discover_priorities(struct rte_eth_dev *dev);
 void mlx5_flow_print(struct rte_flow *flow);
 int mlx5_flow_validate(struct rte_eth_dev *dev,
diff --git a/drivers/net/mlx5/mlx5_ethdev.c b/drivers/net/mlx5/mlx5_ethdev.c
index c2bed2f..2b7c867 100644
--- a/drivers/net/mlx5/mlx5_ethdev.c
+++ b/drivers/net/mlx5/mlx5_ethdev.c
@@ -1793,6 +1793,10 @@ int mlx5_fw_version_get(struct rte_eth_dev *dev, char *fw_ver, size_t fw_size)
  *
  * @param[in] port
  *   Device port id.
+ * @param[in] valid
+ *   Device port id is valid, skip check. This flag is useful
+ *   when trials are performed from probing and device is not
+ *   flagged as valid yet (in attaching process).
  * @param[out] es_domain_id
  *   E-Switch domain id.
  * @param[out] es_port_id
@@ -1803,7 +1807,7 @@ int mlx5_fw_version_get(struct rte_eth_dev *dev, char *fw_ver, size_t fw_size)
  *   on success, NULL otherwise and rte_errno is set.
  */
 struct mlx5_priv *
-mlx5_port_to_eswitch_info(uint16_t port)
+mlx5_port_to_eswitch_info(uint16_t port, bool valid)
 {
 	struct rte_eth_dev *dev;
 	struct mlx5_priv *priv;
@@ -1812,7 +1816,7 @@ struct mlx5_priv *
 		rte_errno = EINVAL;
 		return NULL;
 	}
-	if (!rte_eth_dev_is_valid_port(port)) {
+	if (!valid && !rte_eth_dev_is_valid_port(port)) {
 		rte_errno = ENODEV;
 		return NULL;
 	}
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 6e6c845..f32ea8d 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -368,6 +368,33 @@ static enum modify_reg flow_get_reg_id(struct rte_eth_dev *dev,
 				  NULL, "invalid feature name");
 }
 
+
+/**
+ * Check extensive flow metadata register support.
+ *
+ * @param dev
+ *   Pointer to rte_eth_dev structure.
+ *
+ * @return
+ *   True if device supports extensive flow metadata register, otherwise false.
+ */
+bool
+mlx5_flow_ext_mreg_supported(struct rte_eth_dev *dev)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_dev_config *config = &priv->config;
+
+	/*
+	 * Having available reg_c can be regarded inclusively as supporting
+	 * extensive flow metadata register, which could mean,
+	 * - metadata register copy action by modify header.
+	 * - 16 modify header actions is supported.
+	 * - reg_c's are preserved across different domain (FDB and NIC) on
+	 *   packet loopback by flow lookup miss.
+	 */
+	return config->flow_mreg_c[2] != REG_NONE;
+}
+
 /**
  * Discover the maximum number of priority available.
  *
@@ -4033,3 +4060,74 @@ struct rte_flow *
 	}
 	return 0;
 }
+
+/**
+ * Discover availability of metadata reg_c's.
+ *
+ * Iteratively use test flows to check availability.
+ *
+ * @param[in] dev
+ *   Pointer to the Ethernet device structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_flow_discover_mreg_c(struct rte_eth_dev *dev)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_dev_config *config = &priv->config;
+	enum modify_reg idx;
+	int n = 0;
+
+	/* reg_c[0] and reg_c[1] are reserved. */
+	config->flow_mreg_c[n++] = REG_C_0;
+	config->flow_mreg_c[n++] = REG_C_1;
+	/* Discover availability of other reg_c's. */
+	for (idx = REG_C_2; idx <= REG_C_7; ++idx) {
+		struct rte_flow_attr attr = {
+			.group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
+			.priority = MLX5_FLOW_PRIO_RSVD,
+			.ingress = 1,
+		};
+		struct rte_flow_item items[] = {
+			[0] = {
+				.type = RTE_FLOW_ITEM_TYPE_END,
+			},
+		};
+		struct rte_flow_action actions[] = {
+			[0] = {
+				.type = MLX5_RTE_FLOW_ACTION_TYPE_COPY_MREG,
+				.conf = &(struct mlx5_flow_action_copy_mreg){
+					.src = REG_C_1,
+					.dst = idx,
+				},
+			},
+			[1] = {
+				.type = RTE_FLOW_ACTION_TYPE_JUMP,
+				.conf = &(struct rte_flow_action_jump){
+					.group = MLX5_FLOW_MREG_ACT_TABLE_GROUP,
+				},
+			},
+			[2] = {
+				.type = RTE_FLOW_ACTION_TYPE_END,
+			},
+		};
+		struct rte_flow *flow;
+		struct rte_flow_error error;
+
+		if (!config->dv_flow_en)
+			break;
+		/* Create internal flow, validation skips copy action. */
+		flow = flow_list_create(dev, NULL, &attr, items,
+					actions, false, &error);
+		if (!flow)
+			continue;
+		if (dev->data->dev_started || !flow_drv_apply(dev, flow, NULL))
+			config->flow_mreg_c[n++] = idx;
+		flow_list_destroy(dev, NULL, flow);
+	}
+	for (; n < MLX5_MREG_C_NUM; ++n)
+		config->flow_mreg_c[n] = REG_NONE;
+	return 0;
+}
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index b9a9507..f2b6726 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -27,19 +27,6 @@
 #include "mlx5.h"
 #include "mlx5_prm.h"
 
-enum modify_reg {
-	REG_A,
-	REG_B,
-	REG_C_0,
-	REG_C_1,
-	REG_C_2,
-	REG_C_3,
-	REG_C_4,
-	REG_C_5,
-	REG_C_6,
-	REG_C_7,
-};
-
 /* Private rte flow items. */
 enum mlx5_rte_flow_item_type {
 	MLX5_RTE_FLOW_ITEM_TYPE_END = INT_MIN,
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 853e879..8b93022 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -830,6 +830,7 @@ struct field_modify_info modify_tcp[] = {
 }
 
 static enum mlx5_modification_field reg_to_field[] = {
+	[REG_NONE] = MLX5_MODI_OUT_NONE,
 	[REG_A] = MLX5_MODI_META_DATA_REG_A,
 	[REG_B] = MLX5_MODI_META_DATA_REG_B,
 	[REG_C_0] = MLX5_MODI_META_REG_C_0,
@@ -1038,7 +1039,7 @@ struct field_modify_info modify_tcp[] = {
 		return ret;
 	if (!spec)
 		return 0;
-	esw_priv = mlx5_port_to_eswitch_info(spec->id);
+	esw_priv = mlx5_port_to_eswitch_info(spec->id, false);
 	if (!esw_priv)
 		return rte_flow_error_set(error, rte_errno,
 					  RTE_FLOW_ERROR_TYPE_ITEM_SPEC, spec,
@@ -2695,7 +2696,7 @@ struct field_modify_info modify_tcp[] = {
 					  "failed to obtain E-Switch info");
 	port_id = action->conf;
 	port = port_id->original ? dev->data->port_id : port_id->id;
-	act_priv = mlx5_port_to_eswitch_info(port);
+	act_priv = mlx5_port_to_eswitch_info(port, false);
 	if (!act_priv)
 		return rte_flow_error_set
 				(error, rte_errno,
@@ -5090,7 +5091,7 @@ struct field_modify_info modify_tcp[] = {
 
 	mask = pid_m ? pid_m->id : 0xffff;
 	id = pid_v ? pid_v->id : dev->data->port_id;
-	priv = mlx5_port_to_eswitch_info(id);
+	priv = mlx5_port_to_eswitch_info(id, item == NULL);
 	if (!priv)
 		return -rte_errno;
 	/* Translate to vport field or to metadata, depending on mode. */
@@ -5538,7 +5539,7 @@ struct field_modify_info modify_tcp[] = {
 			(const struct rte_flow_action_port_id *)action->conf;
 
 	port = conf->original ? dev->data->port_id : conf->id;
-	priv = mlx5_port_to_eswitch_info(port);
+	priv = mlx5_port_to_eswitch_info(port, false);
 	if (!priv)
 		return rte_flow_error_set(error, -rte_errno,
 					  RTE_FLOW_ERROR_TYPE_ACTION,
diff --git a/drivers/net/mlx5/mlx5_prm.h b/drivers/net/mlx5/mlx5_prm.h
index b9e53f5..c17ba66 100644
--- a/drivers/net/mlx5/mlx5_prm.h
+++ b/drivers/net/mlx5/mlx5_prm.h
@@ -392,6 +392,7 @@ enum {
 
 /* The field of packet to be modified. */
 enum mlx5_modification_field {
+	MLX5_MODI_OUT_NONE = -1,
 	MLX5_MODI_OUT_SMAC_47_16 = 1,
 	MLX5_MODI_OUT_SMAC_15_0,
 	MLX5_MODI_OUT_ETHERTYPE,
@@ -455,6 +456,23 @@ enum mlx5_modification_field {
 	MLX5_MODI_IN_TCP_ACK_NUM = 0x5C,
 };
 
+/* Total number of metadata reg_c's. */
+#define MLX5_MREG_C_NUM (MLX5_MODI_META_REG_C_7 - MLX5_MODI_META_REG_C_0 + 1)
+
+enum modify_reg {
+	REG_NONE = 0,
+	REG_A,
+	REG_B,
+	REG_C_0,
+	REG_C_1,
+	REG_C_2,
+	REG_C_3,
+	REG_C_4,
+	REG_C_5,
+	REG_C_6,
+	REG_C_7,
+};
+
 /* Modification sub command. */
 struct mlx5_modification_cmd {
 	union {
