diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index b0c3a2286d..041240e6fe 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -791,6 +791,8 @@ struct mlx5_flow_rss_desc {
 	uint32_t key_len; /**< RSS hash key len. */
 	uint32_t tunnel; /**< Queue in tunnel. */
 	uint32_t shared_rss; /**< Shared RSS index. */
+	struct mlx5_ind_table_obj *ind_tbl;
+	/**< Indirection table for shared RSS hash RX queues. */
 	union {
 		uint16_t *queue; /**< Destination queues. */
 		const uint16_t *const_q; /**< Const pointer convert. */
@@ -830,7 +832,7 @@ struct mlx5_ind_table_obj {
 		struct mlx5_devx_obj *rqt; /* DevX RQT object. */
 	};
 	uint32_t queues_n; /**< Number of queues in the list. */
-	uint16_t queues[]; /**< Queue list. */
+	uint16_t *queues; /**< Queue list. */
 };
 
 /* Hash Rx queue. */
@@ -906,6 +908,10 @@ struct mlx5_obj_ops {
 	void (*rxq_obj_release)(struct mlx5_rxq_obj *rxq_obj);
 	int (*ind_table_new)(struct rte_eth_dev *dev, const unsigned int log_n,
 			     struct mlx5_ind_table_obj *ind_tbl);
+	int (*ind_table_modify)(struct rte_eth_dev *dev,
+				const unsigned int log_n,
+				const uint16_t *queues, const uint32_t queues_n,
+				struct mlx5_ind_table_obj *ind_tbl);
 	void (*ind_table_destroy)(struct mlx5_ind_table_obj *ind_tbl);
 	int (*hrxq_new)(struct rte_eth_dev *dev, struct mlx5_hrxq *hrxq,
 			int tunnel __rte_unused);
diff --git a/drivers/net/mlx5/mlx5_devx.c b/drivers/net/mlx5/mlx5_devx.c
index 34044fcb0c..73ee147246 100644
--- a/drivers/net/mlx5/mlx5_devx.c
+++ b/drivers/net/mlx5/mlx5_devx.c
@@ -694,7 +694,7 @@ mlx5_rxq_devx_obj_new(struct rte_eth_dev *dev, uint16_t idx)
 }
 
 /**
- * Create RQT using DevX API as a filed of indirection table.
+ * Prepare RQT attribute structure for DevX RQT API.
  *
  * @param dev
  *   Pointer to Ethernet device.
@@ -704,30 +704,31 @@ mlx5_rxq_devx_obj_new(struct rte_eth_dev *dev, uint16_t idx)
  *   DevX indirection table object.
  *
  * @return
- *   0 on success, a negative errno value otherwise and rte_errno is set.
+ *   The RQT attr object initialized, NULL otherwise and rte_errno is set.
  */
-static int
-mlx5_devx_ind_table_new(struct rte_eth_dev *dev, const unsigned int log_n,
-			struct mlx5_ind_table_obj *ind_tbl)
+static struct mlx5_devx_rqt_attr *
+mlx5_devx_ind_table_create_rqt_attr(struct rte_eth_dev *dev,
+				     const unsigned int log_n,
+				     const uint16_t *queues,
+				     const uint32_t queues_n)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_devx_rqt_attr *rqt_attr = NULL;
 	const unsigned int rqt_n = 1 << log_n;
 	unsigned int i, j;
 
-	MLX5_ASSERT(ind_tbl);
 	rqt_attr = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*rqt_attr) +
 			      rqt_n * sizeof(uint32_t), 0, SOCKET_ID_ANY);
 	if (!rqt_attr) {
 		DRV_LOG(ERR, "Port %u cannot allocate RQT resources.",
 			dev->data->port_id);
 		rte_errno = ENOMEM;
-		return -rte_errno;
+		return NULL;
 	}
 	rqt_attr->rqt_max_size = priv->config.ind_table_max_size;
 	rqt_attr->rqt_actual_size = rqt_n;
-	for (i = 0; i != ind_tbl->queues_n; ++i) {
-		struct mlx5_rxq_data *rxq = (*priv->rxqs)[ind_tbl->queues[i]];
+	for (i = 0; i != queues_n; ++i) {
+		struct mlx5_rxq_data *rxq = (*priv->rxqs)[queues[i]];
 		struct mlx5_rxq_ctrl *rxq_ctrl =
 				container_of(rxq, struct mlx5_rxq_ctrl, rxq);
 
@@ -736,6 +737,35 @@ mlx5_devx_ind_table_new(struct rte_eth_dev *dev, const unsigned int log_n,
 	MLX5_ASSERT(i > 0);
 	for (j = 0; i != rqt_n; ++j, ++i)
 		rqt_attr->rq_list[i] = rqt_attr->rq_list[j];
+	return rqt_attr;
+}
+
+/**
+ * Create RQT using DevX API as a filed of indirection table.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param log_n
+ *   Log of number of queues in the array.
+ * @param ind_tbl
+ *   DevX indirection table object.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_devx_ind_table_new(struct rte_eth_dev *dev, const unsigned int log_n,
+			struct mlx5_ind_table_obj *ind_tbl)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_devx_rqt_attr *rqt_attr = NULL;
+
+	MLX5_ASSERT(ind_tbl);
+	rqt_attr = mlx5_devx_ind_table_create_rqt_attr(dev, log_n,
+							ind_tbl->queues,
+							ind_tbl->queues_n);
+	if (!rqt_attr)
+		return -rte_errno;
 	ind_tbl->rqt = mlx5_devx_cmd_create_rqt(priv->sh->ctx, rqt_attr);
 	mlx5_free(rqt_attr);
 	if (!ind_tbl->rqt) {
@@ -747,6 +777,41 @@ mlx5_devx_ind_table_new(struct rte_eth_dev *dev, const unsigned int log_n,
 	return 0;
 }
 
+/**
+ * Modify RQT using DevX API as a filed of indirection table.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param log_n
+ *   Log of number of queues in the array.
+ * @param ind_tbl
+ *   DevX indirection table object.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_devx_ind_table_modify(struct rte_eth_dev *dev, const unsigned int log_n,
+			   const uint16_t *queues, const uint32_t queues_n,
+			   struct mlx5_ind_table_obj *ind_tbl)
+{
+	int ret = 0;
+	struct mlx5_devx_rqt_attr *rqt_attr = NULL;
+
+	MLX5_ASSERT(ind_tbl);
+	rqt_attr = mlx5_devx_ind_table_create_rqt_attr(dev, log_n,
+							queues,
+							queues_n);
+	if (!rqt_attr)
+		return -rte_errno;
+	ret = mlx5_devx_cmd_modify_rqt(ind_tbl->rqt, rqt_attr);
+	mlx5_free(rqt_attr);
+	if (ret)
+		DRV_LOG(ERR, "Port %u cannot modify DevX RQT.",
+			dev->data->port_id);
+	return ret;
+}
+
 /**
  * Destroy the DevX RQT object.
  *
@@ -1472,6 +1537,7 @@ struct mlx5_obj_ops devx_obj_ops = {
 	.rxq_obj_modify = mlx5_devx_modify_rq,
 	.rxq_obj_release = mlx5_rxq_devx_obj_release,
 	.ind_table_new = mlx5_devx_ind_table_new,
+	.ind_table_modify = mlx5_devx_ind_table_modify,
 	.ind_table_destroy = mlx5_devx_ind_table_destroy,
 	.hrxq_new = mlx5_devx_hrxq_new,
 	.hrxq_destroy = mlx5_devx_tir_destroy,
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 088fa21e4d..a249c292e3 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -1079,11 +1079,13 @@ struct mlx5_shared_action_rss {
 	uint32_t refcnt; /**< Atomically accessed refcnt. */
 	struct rte_flow_action_rss origin; /**< Original rte RSS action. */
 	uint8_t key[MLX5_RSS_HASH_KEY_LEN]; /**< RSS hash key. */
-	uint16_t *queue; /**< Queue indices to use. */
+	struct mlx5_ind_table_obj *ind_tbl;
+	/**< Hash RX queues (hrxq, hrxq_tunnel fields) indirection table. */
 	uint32_t hrxq[MLX5_RSS_HASH_FIELDS_LEN];
 	/**< Hash RX queue indexes mapped to mlx5_rss_hash_fields */
 	uint32_t hrxq_tunnel[MLX5_RSS_HASH_FIELDS_LEN];
 	/**< Hash RX queue indexes for tunneled RSS */
+	rte_spinlock_t action_rss_sl; /**< Shared RSS action spinlock. */
 };
 
 struct rte_flow_shared_action {
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index fa2e8f2d9e..2aecec1f63 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -11348,20 +11348,26 @@ __flow_dv_action_rss_hrxqs_release(struct rte_eth_dev *dev,
  */
 static int
 __flow_dv_action_rss_setup(struct rte_eth_dev *dev,
-			uint32_t action_idx,
-			struct mlx5_shared_action_rss *action,
-			struct rte_flow_error *error)
+			   uint32_t action_idx,
+			   struct mlx5_shared_action_rss *action,
+			   struct rte_flow_error *error)
 {
 	struct mlx5_flow_rss_desc rss_desc = { 0 };
 	size_t i;
 	int err;
 
+	if (mlx5_ind_table_obj_setup(dev, action->ind_tbl)) {
+		return rte_flow_error_set(error, rte_errno,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "cannot setup indirection table");
+	}
 	memcpy(rss_desc.key, action->origin.key, MLX5_RSS_HASH_KEY_LEN);
 	rss_desc.key_len = MLX5_RSS_HASH_KEY_LEN;
 	rss_desc.const_q = action->origin.queue;
 	rss_desc.queue_num = action->origin.queue_num;
 	/* Set non-zero value to indicate a shared RSS. */
 	rss_desc.shared_rss = action_idx;
+	rss_desc.ind_tbl = action->ind_tbl;
 	for (i = 0; i < MLX5_RSS_HASH_FIELDS_LEN; i++) {
 		uint32_t hrxq_idx;
 		uint64_t hash_fields = mlx5_rss_hash_fields[i];
@@ -11387,6 +11393,8 @@ __flow_dv_action_rss_setup(struct rte_eth_dev *dev,
 error_hrxq_new:
 	err = rte_errno;
 	__flow_dv_action_rss_hrxqs_release(dev, action);
+	if (!mlx5_ind_table_obj_release(dev, action->ind_tbl, true))
+		action->ind_tbl = NULL;
 	rte_errno = err;
 	return -rte_errno;
 }
@@ -11439,7 +11447,18 @@ __flow_dv_action_rss_create(struct rte_eth_dev *dev,
 				   "rss action number out of range");
 		goto error_rss_init;
 	}
-	shared_action->queue = queue;
+	shared_action->ind_tbl = mlx5_malloc(MLX5_MEM_ZERO,
+					     sizeof(*shared_action->ind_tbl),
+					     0, SOCKET_ID_ANY);
+	if (!shared_action->ind_tbl) {
+		rte_flow_error_set(error, ENOMEM,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "cannot allocate resource memory");
+		goto error_rss_init;
+	}
+	memcpy(queue, rss->queue, queue_size);
+	shared_action->ind_tbl->queues = queue;
+	shared_action->ind_tbl->queues_n = rss->queue_num;
 	origin = &shared_action->origin;
 	origin->func = rss->func;
 	origin->level = rss->level;
@@ -11450,11 +11469,11 @@ __flow_dv_action_rss_create(struct rte_eth_dev *dev,
 	memcpy(shared_action->key, rss_key, MLX5_RSS_HASH_KEY_LEN);
 	origin->key = &shared_action->key[0];
 	origin->key_len = MLX5_RSS_HASH_KEY_LEN;
-	memcpy(shared_action->queue, rss->queue, queue_size);
-	origin->queue = shared_action->queue;
+	origin->queue = queue;
 	origin->queue_num = rss->queue_num;
 	if (__flow_dv_action_rss_setup(dev, idx, shared_action, error))
 		goto error_rss_init;
+	rte_spinlock_init(&shared_action->action_rss_sl);
 	__atomic_add_fetch(&shared_action->refcnt, 1, __ATOMIC_RELAXED);
 	rte_spinlock_lock(&priv->shared_act_sl);
 	ILIST_INSERT(priv->sh->ipool[MLX5_IPOOL_RSS_SHARED_ACTIONS],
@@ -11462,9 +11481,12 @@ __flow_dv_action_rss_create(struct rte_eth_dev *dev,
 	rte_spinlock_unlock(&priv->shared_act_sl);
 	return idx;
 error_rss_init:
-	if (shared_action)
+	if (shared_action) {
+		if (shared_action->ind_tbl)
+			mlx5_free(shared_action->ind_tbl);
 		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_RSS_SHARED_ACTIONS],
 				idx);
+	}
 	if (queue)
 		mlx5_free(queue);
 	return 0;
@@ -11494,6 +11516,7 @@ __flow_dv_action_rss_release(struct rte_eth_dev *dev, uint32_t idx,
 	    mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_RSS_SHARED_ACTIONS], idx);
 	uint32_t old_refcnt = 1;
 	int remaining;
+	uint16_t *queue = NULL;
 
 	if (!shared_rss)
 		return rte_flow_error_set(error, EINVAL,
@@ -11505,6 +11528,14 @@ __flow_dv_action_rss_release(struct rte_eth_dev *dev, uint32_t idx,
 					  RTE_FLOW_ERROR_TYPE_ACTION,
 					  NULL,
 					  "shared rss hrxq has references");
+	queue = shared_rss->ind_tbl->queues;
+	remaining = mlx5_ind_table_obj_release(dev, shared_rss->ind_tbl, true);
+	if (remaining)
+		return rte_flow_error_set(error, ETOOMANYREFS,
+					  RTE_FLOW_ERROR_TYPE_ACTION,
+					  NULL,
+					  "shared rss indirection table has"
+					  " references");
 	if (!__atomic_compare_exchange_n(&shared_rss->refcnt, &old_refcnt,
 					 0, 0, __ATOMIC_ACQUIRE,
 					 __ATOMIC_RELAXED))
@@ -11512,7 +11543,7 @@ __flow_dv_action_rss_release(struct rte_eth_dev *dev, uint32_t idx,
 					  RTE_FLOW_ERROR_TYPE_ACTION,
 					  NULL,
 					  "shared rss has references");
-	rte_free(shared_rss->queue);
+	mlx5_free(queue);
 	rte_spinlock_lock(&priv->shared_act_sl);
 	ILIST_REMOVE(priv->sh->ipool[MLX5_IPOOL_RSS_SHARED_ACTIONS],
 		     &priv->rss_shared_actions, idx, shared_rss, next);
@@ -11650,11 +11681,9 @@ __flow_dv_action_rss_update(struct rte_eth_dev *dev, uint32_t idx,
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_shared_action_rss *shared_rss =
 	    mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_RSS_SHARED_ACTIONS], idx);
-	size_t i;
-	int ret;
+	int ret = 0;
 	void *queue = NULL;
-	const uint8_t *rss_key;
-	uint32_t rss_key_len;
+	uint16_t *queue_old = NULL;
 	uint32_t queue_size = action_conf->queue_num * sizeof(uint16_t);
 
 	if (!shared_rss)
@@ -11669,42 +11698,24 @@ __flow_dv_action_rss_update(struct rte_eth_dev *dev, uint32_t idx,
 					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
 					  NULL,
 					  "cannot allocate resource memory");
-	if (action_conf->key) {
-		rss_key = action_conf->key;
-		rss_key_len = action_conf->key_len;
+	memcpy(queue, action_conf->queue, queue_size);
+	MLX5_ASSERT(shared_rss->ind_tbl);
+	rte_spinlock_lock(&shared_rss->action_rss_sl);
+	queue_old = shared_rss->ind_tbl->queues;
+	ret = mlx5_ind_table_obj_modify(dev, shared_rss->ind_tbl,
+					queue, action_conf->queue_num, true);
+	if (ret) {
+		mlx5_free(queue);
+		ret = rte_flow_error_set(error, rte_errno,
+					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+					  "cannot update indirection table");
 	} else {
-		rss_key = rss_hash_default_key;
-		rss_key_len = MLX5_RSS_HASH_KEY_LEN;
+		mlx5_free(queue_old);
+		shared_rss->origin.queue = queue;
+		shared_rss->origin.queue_num = action_conf->queue_num;
 	}
-	for (i = 0; i < MLX5_RSS_HASH_FIELDS_LEN; i++) {
-		uint32_t hrxq_idx;
-		uint64_t hash_fields = mlx5_rss_hash_fields[i];
-		int tunnel;
-
-		for (tunnel = 0; tunnel < 2; tunnel++) {
-			hrxq_idx = __flow_dv_action_rss_hrxq_lookup
-					(dev, idx, hash_fields, tunnel);
-			MLX5_ASSERT(hrxq_idx);
-			ret = mlx5_hrxq_modify
-				(dev, hrxq_idx,
-				 rss_key, rss_key_len,
-				 hash_fields,
-				 action_conf->queue, action_conf->queue_num);
-			if (ret) {
-				mlx5_free(queue);
-				return rte_flow_error_set
-					(error, rte_errno,
-					 RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-					 "cannot update hash queue");
-			}
-		}
-	}
-	mlx5_free(shared_rss->queue);
-	shared_rss->queue = queue;
-	memcpy(shared_rss->queue, action_conf->queue, queue_size);
-	shared_rss->origin.queue = shared_rss->queue;
-	shared_rss->origin.queue_num = action_conf->queue_num;
-	return 0;
+	rte_spinlock_unlock(&shared_rss->action_rss_sl);
+	return ret;
 }
 
 /**
diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
index 0693c924d0..f298327005 100644
--- a/drivers/net/mlx5/mlx5_rxq.c
+++ b/drivers/net/mlx5/mlx5_rxq.c
@@ -1994,6 +1994,50 @@ mlx5_ind_table_obj_verify(struct rte_eth_dev *dev)
 	return ret;
 }
 
+/**
+ * Setup an indirection table structure fields.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param ind_table
+ *   Indirection table to modify.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_ind_table_obj_setup(struct rte_eth_dev *dev,
+			 struct mlx5_ind_table_obj *ind_tbl)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	uint32_t queues_n = ind_tbl->queues_n;
+	uint16_t *queues = ind_tbl->queues;
+	unsigned int i, j;
+	int ret = 0, err;
+	const unsigned int n = rte_is_power_of_2(queues_n) ?
+			       log2above(queues_n) :
+			       log2above(priv->config.ind_table_max_size);
+
+	for (i = 0; i != queues_n; ++i) {
+		if (!mlx5_rxq_get(dev, queues[i])) {
+			ret = -rte_errno;
+			goto error;
+		}
+	}
+	ret = priv->obj_ops.ind_table_new(dev, n, ind_tbl);
+	if (ret)
+		goto error;
+	__atomic_fetch_add(&ind_tbl->refcnt, 1, __ATOMIC_RELAXED);
+	return 0;
+error:
+	err = rte_errno;
+	for (j = 0; j < i; j++)
+		mlx5_rxq_release(dev, ind_tbl->queues[j]);
+	rte_errno = err;
+	DEBUG("Port %u cannot setup indirection table.", dev->data->port_id);
+	return ret;
+}
+
 /**
  * Create an indirection table.
  *
@@ -2015,10 +2059,6 @@ mlx5_ind_table_obj_new(struct rte_eth_dev *dev, const uint16_t *queues,
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_ind_table_obj *ind_tbl;
-	const unsigned int n = rte_is_power_of_2(queues_n) ?
-			       log2above(queues_n) :
-			       log2above(priv->config.ind_table_max_size);
-	unsigned int i, j;
 	int ret;
 
 	ind_tbl = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*ind_tbl) +
@@ -2028,27 +2068,83 @@ mlx5_ind_table_obj_new(struct rte_eth_dev *dev, const uint16_t *queues,
 		return NULL;
 	}
 	ind_tbl->queues_n = queues_n;
-	for (i = 0; i != queues_n; ++i) {
-		struct mlx5_rxq_ctrl *rxq = mlx5_rxq_get(dev, queues[i]);
-		if (!rxq)
-			goto error;
-		ind_tbl->queues[i] = queues[i];
+	ind_tbl->queues = (uint16_t *)(ind_tbl + 1);
+	memcpy(ind_tbl->queues, queues, queues_n * sizeof(*queues));
+	ret = mlx5_ind_table_obj_setup(dev, ind_tbl);
+	if (ret < 0) {
+		mlx5_free(ind_tbl);
+		return NULL;
 	}
-	ret = priv->obj_ops.ind_table_new(dev, n, ind_tbl);
-	if (ret < 0)
-		goto error;
-	__atomic_fetch_add(&ind_tbl->refcnt, 1, __ATOMIC_RELAXED);
 	if (!standalone)
 		LIST_INSERT_HEAD(&priv->ind_tbls, ind_tbl, next);
 	return ind_tbl;
+}
+
+/**
+ * Modify an indirection table.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param ind_table
+ *   Indirection table to modify.
+ * @param queues
+ *   Queues replacement for the indirection table.
+ * @param queues_n
+ *   Number of queues in the array.
+ * @param standalone
+ *   Indirection table for Standalone queue.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_ind_table_obj_modify(struct rte_eth_dev *dev,
+			  struct mlx5_ind_table_obj *ind_tbl,
+			  uint16_t *queues, const uint32_t queues_n,
+			  bool standalone)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	unsigned int i, j;
+	int ret = 0, err;
+	const unsigned int n = rte_is_power_of_2(queues_n) ?
+			       log2above(queues_n) :
+			       log2above(priv->config.ind_table_max_size);
+
+	MLX5_ASSERT(standalone);
+	RTE_SET_USED(standalone);
+	if (__atomic_load_n(&ind_tbl->refcnt, __ATOMIC_RELAXED) > 1) {
+		/*
+		 * Modification of indirection ntables having more than 1
+		 * reference unsupported. Intended for standalone indirection
+		 * tables only.
+		 */
+		DEBUG("Port %u cannot modify indirection table (refcnt> 1).",
+		      dev->data->port_id);
+		rte_errno = EINVAL;
+		return -rte_errno;
+	}
+	for (i = 0; i != queues_n; ++i) {
+		if (!mlx5_rxq_get(dev, queues[i])) {
+			ret = -rte_errno;
+			goto error;
+		}
+	}
+	MLX5_ASSERT(priv->obj_ops.ind_table_modify);
+	ret = priv->obj_ops.ind_table_modify(dev, n, queues, queues_n, ind_tbl);
+	if (ret)
+		goto error;
+	for (j = 0; j < ind_tbl->queues_n; j++)
+		mlx5_rxq_release(dev, ind_tbl->queues[j]);
+	ind_tbl->queues_n = queues_n;
+	ind_tbl->queues = queues;
+	return 0;
 error:
-	ret = rte_errno;
+	err = rte_errno;
 	for (j = 0; j < i; j++)
 		mlx5_rxq_release(dev, ind_tbl->queues[j]);
-	rte_errno = ret;
-	mlx5_free(ind_tbl);
-	DEBUG("Port %u cannot create indirection table.", dev->data->port_id);
-	return NULL;
+	rte_errno = err;
+	DEBUG("Port %u cannot setup indirection table.", dev->data->port_id);
+	return ret;
 }
 
 /**
@@ -2136,6 +2232,14 @@ mlx5_hrxq_modify(struct rte_eth_dev *dev, uint32_t hrxq_idx,
 					    queues, queues_n)) {
 		ind_tbl = hrxq->ind_table;
 	} else {
+		if (hrxq->standalone) {
+			/*
+			 * Replacement of indirection table unsupported for
+			 * stanalone hrxq objects (used by shared RSS).
+			 */
+			rte_errno = ENOTSUP;
+			return -rte_errno;
+		}
 		ind_tbl = mlx5_ind_table_obj_get(dev, queues, queues_n);
 		if (!ind_tbl)
 			ind_tbl = mlx5_ind_table_obj_new(dev, queues, queues_n,
@@ -2153,6 +2257,7 @@ mlx5_hrxq_modify(struct rte_eth_dev *dev, uint32_t hrxq_idx,
 		goto error;
 	}
 	if (ind_tbl != hrxq->ind_table) {
+		MLX5_ASSERT(!hrxq->standalone);
 		mlx5_ind_table_obj_release(dev, hrxq->ind_table,
 					   hrxq->standalone);
 		hrxq->ind_table = ind_tbl;
@@ -2162,8 +2267,10 @@ mlx5_hrxq_modify(struct rte_eth_dev *dev, uint32_t hrxq_idx,
 	return 0;
 error:
 	err = rte_errno;
-	if (ind_tbl != hrxq->ind_table)
+	if (ind_tbl != hrxq->ind_table) {
+		MLX5_ASSERT(!hrxq->standalone);
 		mlx5_ind_table_obj_release(dev, ind_tbl, hrxq->standalone);
+	}
 	rte_errno = err;
 	return -rte_errno;
 }
@@ -2177,7 +2284,10 @@ __mlx5_hrxq_remove(struct rte_eth_dev *dev, struct mlx5_hrxq *hrxq)
 	mlx5_glue->destroy_flow_action(hrxq->action);
 #endif
 	priv->obj_ops.hrxq_destroy(hrxq);
-	mlx5_ind_table_obj_release(dev, hrxq->ind_table, hrxq->standalone);
+	if (!hrxq->standalone) {
+		mlx5_ind_table_obj_release(dev, hrxq->ind_table,
+					   hrxq->standalone);
+	}
 	mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_HRXQ], hrxq->idx);
 }
 
@@ -2217,12 +2327,12 @@ __mlx5_hrxq_create(struct rte_eth_dev *dev,
 	uint32_t queues_n = rss_desc->queue_num;
 	struct mlx5_hrxq *hrxq = NULL;
 	uint32_t hrxq_idx = 0;
-	struct mlx5_ind_table_obj *ind_tbl;
+	struct mlx5_ind_table_obj *ind_tbl = rss_desc->ind_tbl;
 	int ret;
 
 	queues_n = rss_desc->hash_fields ? queues_n : 1;
-	ind_tbl = standalone ? NULL :
-		  mlx5_ind_table_obj_get(dev, queues, queues_n);
+	if (!ind_tbl)
+		ind_tbl = mlx5_ind_table_obj_get(dev, queues, queues_n);
 	if (!ind_tbl)
 		ind_tbl = mlx5_ind_table_obj_new(dev, queues, queues_n,
 						 standalone);
@@ -2242,7 +2352,8 @@ __mlx5_hrxq_create(struct rte_eth_dev *dev,
 		goto error;
 	return hrxq;
 error:
-	mlx5_ind_table_obj_release(dev, ind_tbl, standalone);
+	if (!rss_desc->ind_tbl)
+		mlx5_ind_table_obj_release(dev, ind_tbl, standalone);
 	if (hrxq)
 		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_HRXQ], hrxq_idx);
 	return NULL;
diff --git a/drivers/net/mlx5/mlx5_rxtx.h b/drivers/net/mlx5/mlx5_rxtx.h
index 2988554a90..7989a50403 100644
--- a/drivers/net/mlx5/mlx5_rxtx.h
+++ b/drivers/net/mlx5/mlx5_rxtx.h
@@ -352,6 +352,12 @@ struct mlx5_ind_table_obj *mlx5_ind_table_obj_get(struct rte_eth_dev *dev,
 int mlx5_ind_table_obj_release(struct rte_eth_dev *dev,
 			       struct mlx5_ind_table_obj *ind_tbl,
 			       bool standalone);
+int mlx5_ind_table_obj_setup(struct rte_eth_dev *dev,
+			     struct mlx5_ind_table_obj *ind_tbl);
+int mlx5_ind_table_obj_modify(struct rte_eth_dev *dev,
+			      struct mlx5_ind_table_obj *ind_tbl,
+			      uint16_t *queues, const uint32_t queues_n,
+			      bool standalone);
 struct mlx5_cache_entry *mlx5_hrxq_create_cb(struct mlx5_cache_list *list,
 		struct mlx5_cache_entry *entry __rte_unused, void *cb_ctx);
 int mlx5_hrxq_match_cb(struct mlx5_cache_list *list,
