+ /* Qs with auto-bind will be destroyed directly. */
+ rxq_ctrl->hairpin_status = 1;
+ txq_ctrl->hairpin_status = 1;
+ mlx5_txq_release(dev, i);
+ mlx5_rxq_release(dev, txq_ctrl->hairpin_conf.peers[0].queue);
+ }
+ return 0;
+error:
+ mlx5_txq_release(dev, i);
+ mlx5_rxq_release(dev, txq_ctrl->hairpin_conf.peers[0].queue);
+ return -rte_errno;
+}
+
+/*
+ * Fetch the peer queue's SW & HW information.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param peer_queue
+ * Index of the queue to fetch the information.
+ * @param current_info
+ * Pointer to the input peer information, not used currently.
+ * @param peer_info
+ * Pointer to the structure to store the information, output.
+ * @param direction
+ * Positive to get the RxQ information, zero to get the TxQ information.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_hairpin_queue_peer_update(struct rte_eth_dev *dev, uint16_t peer_queue,
+ struct rte_hairpin_peer_info *current_info,
+ struct rte_hairpin_peer_info *peer_info,
+ uint32_t direction)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ RTE_SET_USED(current_info);
+
+ if (dev->data->dev_started == 0) {
+ rte_errno = EBUSY;
+ DRV_LOG(ERR, "peer port %u is not started",
+ dev->data->port_id);
+ return -rte_errno;
+ }
+ /*
+ * Peer port used as egress. In the current design, hairpin Tx queue
+ * will be bound to the peer Rx queue. Indeed, only the information of
+ * peer Rx queue needs to be fetched.
+ */
+ if (direction == 0) {
+ struct mlx5_txq_ctrl *txq_ctrl;
+
+ txq_ctrl = mlx5_txq_get(dev, peer_queue);
+ if (txq_ctrl == NULL) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "Failed to get port %u Tx queue %d",
+ dev->data->port_id, peer_queue);
+ return -rte_errno;
+ }
+ if (txq_ctrl->type != MLX5_TXQ_TYPE_HAIRPIN) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "port %u queue %d is not a hairpin Txq",
+ dev->data->port_id, peer_queue);
+ mlx5_txq_release(dev, peer_queue);
+ return -rte_errno;
+ }
+ if (txq_ctrl->obj == NULL || txq_ctrl->obj->sq == NULL) {
+ rte_errno = ENOMEM;
+ DRV_LOG(ERR, "port %u no Txq object found: %d",
+ dev->data->port_id, peer_queue);
+ mlx5_txq_release(dev, peer_queue);
+ return -rte_errno;
+ }
+ peer_info->qp_id = txq_ctrl->obj->sq->id;
+ peer_info->vhca_id = priv->config.hca_attr.vhca_id;
+ /* 1-to-1 mapping, only the first one is used. */
+ peer_info->peer_q = txq_ctrl->hairpin_conf.peers[0].queue;
+ peer_info->tx_explicit = txq_ctrl->hairpin_conf.tx_explicit;
+ peer_info->manual_bind = txq_ctrl->hairpin_conf.manual_bind;
+ mlx5_txq_release(dev, peer_queue);
+ } else { /* Peer port used as ingress. */
+ struct mlx5_rxq_ctrl *rxq_ctrl;
+
+ rxq_ctrl = mlx5_rxq_get(dev, peer_queue);
+ if (rxq_ctrl == NULL) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "Failed to get port %u Rx queue %d",
+ dev->data->port_id, peer_queue);
+ return -rte_errno;
+ }
+ if (rxq_ctrl->type != MLX5_RXQ_TYPE_HAIRPIN) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "port %u queue %d is not a hairpin Rxq",
+ dev->data->port_id, peer_queue);
+ mlx5_rxq_release(dev, peer_queue);
+ return -rte_errno;
+ }
+ if (rxq_ctrl->obj == NULL || rxq_ctrl->obj->rq == NULL) {
+ rte_errno = ENOMEM;
+ DRV_LOG(ERR, "port %u no Rxq object found: %d",
+ dev->data->port_id, peer_queue);
+ mlx5_rxq_release(dev, peer_queue);
+ return -rte_errno;
+ }
+ peer_info->qp_id = rxq_ctrl->obj->rq->id;
+ peer_info->vhca_id = priv->config.hca_attr.vhca_id;
+ peer_info->peer_q = rxq_ctrl->hairpin_conf.peers[0].queue;
+ peer_info->tx_explicit = rxq_ctrl->hairpin_conf.tx_explicit;
+ peer_info->manual_bind = rxq_ctrl->hairpin_conf.manual_bind;
+ mlx5_rxq_release(dev, peer_queue);
+ }
+ return 0;
+}
+
+/*
+ * Bind the hairpin queue with the peer HW information.
+ * This needs to be called twice both for Tx and Rx queues of a pair.
+ * If the queue is already bound, it is considered successful.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param cur_queue
+ * Index of the queue to change the HW configuration to bind.
+ * @param peer_info
+ * Pointer to information of the peer queue.
+ * @param direction
+ * Positive to configure the TxQ, zero to configure the RxQ.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_hairpin_queue_peer_bind(struct rte_eth_dev *dev, uint16_t cur_queue,
+ struct rte_hairpin_peer_info *peer_info,
+ uint32_t direction)
+{
+ int ret = 0;
+
+ /*
+ * Consistency checking of the peer queue: opposite direction is used
+ * to get the peer queue info with ethdev port ID, no need to check.
+ */
+ if (peer_info->peer_q != cur_queue) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "port %u queue %d and peer queue %d mismatch",
+ dev->data->port_id, cur_queue, peer_info->peer_q);
+ return -rte_errno;
+ }
+ if (direction != 0) {
+ struct mlx5_txq_ctrl *txq_ctrl;
+ struct mlx5_devx_modify_sq_attr sq_attr = { 0 };
+
+ txq_ctrl = mlx5_txq_get(dev, cur_queue);
+ if (txq_ctrl == NULL) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "Failed to get port %u Tx queue %d",
+ dev->data->port_id, cur_queue);
+ return -rte_errno;
+ }
+ if (txq_ctrl->type != MLX5_TXQ_TYPE_HAIRPIN) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "port %u queue %d not a hairpin Txq",
+ dev->data->port_id, cur_queue);
+ mlx5_txq_release(dev, cur_queue);
+ return -rte_errno;
+ }
+ if (txq_ctrl->obj == NULL || txq_ctrl->obj->sq == NULL) {
+ rte_errno = ENOMEM;
+ DRV_LOG(ERR, "port %u no Txq object found: %d",
+ dev->data->port_id, cur_queue);
+ mlx5_txq_release(dev, cur_queue);
+ return -rte_errno;
+ }
+ if (txq_ctrl->hairpin_status != 0) {
+ DRV_LOG(DEBUG, "port %u Tx queue %d is already bound",
+ dev->data->port_id, cur_queue);
+ mlx5_txq_release(dev, cur_queue);
+ return 0;
+ }
+ /*
+ * All queues' of one port consistency checking is done in the
+ * bind() function, and that is optional.
+ */
+ if (peer_info->tx_explicit !=
+ txq_ctrl->hairpin_conf.tx_explicit) {
+ rte_errno = EINVAL;
+ DRV_LOG(ERR, "port %u Tx queue %d and peer Tx rule mode"
+ " mismatch", dev->data->port_id, cur_queue);
+ mlx5_txq_release(dev, cur_queue);
+ return -rte_errno;