+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+ dev = &rte_eth_devices[port_id];
+ if (tx_queue_id >= dev->data->nb_tx_queues) {
+ RTE_ETHDEV_LOG(ERR, "Invalid TX queue_id=%u\n", tx_queue_id);
+ return -EINVAL;
+ }
+ ret = rte_eth_dev_hairpin_capability_get(port_id, &cap);
+ if (ret != 0)
+ return ret;
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->tx_hairpin_queue_setup,
+ -ENOTSUP);
+ /* if nb_rx_desc is zero use max number of desc from the driver. */
+ if (nb_tx_desc == 0)
+ nb_tx_desc = cap.max_nb_desc;
+ if (nb_tx_desc > cap.max_nb_desc) {
+ RTE_ETHDEV_LOG(ERR,
+ "Invalid value for nb_tx_desc(=%hu), should be: <= %hu",
+ nb_tx_desc, cap.max_nb_desc);
+ return -EINVAL;
+ }
+ if (conf->peer_count > cap.max_tx_2_rx) {
+ RTE_ETHDEV_LOG(ERR,
+ "Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
+ conf->peer_count, cap.max_tx_2_rx);
+ return -EINVAL;
+ }
+ if (conf->peer_count == 0) {
+ RTE_ETHDEV_LOG(ERR,
+ "Invalid value for number of peers for Tx queue(=%u), should be: > 0",
+ conf->peer_count);
+ return -EINVAL;
+ }
+ for (i = 0, count = 0; i < dev->data->nb_tx_queues &&
+ cap.max_nb_queues != UINT16_MAX; i++) {
+ if (i == tx_queue_id || rte_eth_dev_is_tx_hairpin_queue(dev, i))
+ count++;
+ }
+ if (count > cap.max_nb_queues) {
+ RTE_ETHDEV_LOG(ERR, "To many Tx hairpin queues max is %d",
+ cap.max_nb_queues);
+ return -EINVAL;
+ }
+ if (dev->data->dev_started)
+ return -EBUSY;
+ txq = dev->data->tx_queues;
+ if (txq[tx_queue_id] != NULL) {
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->tx_queue_release,
+ -ENOTSUP);
+ (*dev->dev_ops->tx_queue_release)(txq[tx_queue_id]);
+ txq[tx_queue_id] = NULL;
+ }
+ ret = (*dev->dev_ops->tx_hairpin_queue_setup)
+ (dev, tx_queue_id, nb_tx_desc, conf);
+ if (ret == 0)
+ dev->data->tx_queue_state[tx_queue_id] =
+ RTE_ETH_QUEUE_STATE_HAIRPIN;
+ return eth_err(port_id, ret);
+}
+
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+ struct rte_eth_dev *dev;
+ int ret;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+ dev = &rte_eth_devices[tx_port];
+ if (dev->data->dev_started == 0) {
+ RTE_ETHDEV_LOG(ERR, "Tx port %d is not started\n", tx_port);
+ return -EBUSY;
+ }
+
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+ ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+ if (ret != 0)
+ RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin Tx %d"
+ " to Rx %d (%d - all ports)\n",
+ tx_port, rx_port, RTE_MAX_ETHPORTS);
+
+ return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+ struct rte_eth_dev *dev;
+ int ret;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+ dev = &rte_eth_devices[tx_port];
+ if (dev->data->dev_started == 0) {
+ RTE_ETHDEV_LOG(ERR, "Tx port %d is already stopped\n", tx_port);
+ return -EBUSY;
+ }
+
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+ ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+ if (ret != 0)
+ RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin Tx %d"
+ " from Rx %d (%d - all ports)\n",
+ tx_port, rx_port, RTE_MAX_ETHPORTS);
+
+ return ret;
+}
+
+int
+rte_eth_hairpin_get_peer_ports(uint16_t port_id, uint16_t *peer_ports,
+ size_t len, uint32_t direction)
+{
+ struct rte_eth_dev *dev;
+ int ret;
+
+ if (peer_ports == NULL || len == 0)
+ return -EINVAL;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+ dev = &rte_eth_devices[port_id];
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+ -ENOTSUP);
+
+ ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+ len, direction);
+ if (ret < 0)
+ RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports\n",
+ port_id, direction ? "Rx" : "Tx");
+
+ return ret;
+}
+
+void
+rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
+ void *userdata __rte_unused)
+{
+ rte_pktmbuf_free_bulk(pkts, unsent);
+}
+
+void
+rte_eth_tx_buffer_count_callback(struct rte_mbuf **pkts, uint16_t unsent,
+ void *userdata)
+{
+ uint64_t *count = userdata;
+
+ rte_pktmbuf_free_bulk(pkts, unsent);
+ *count += unsent;
+}
+
+int
+rte_eth_tx_buffer_set_err_callback(struct rte_eth_dev_tx_buffer *buffer,
+ buffer_tx_error_fn cbfn, void *userdata)
+{
+ buffer->error_callback = cbfn;
+ buffer->error_userdata = userdata;
+ return 0;
+}
+
+int
+rte_eth_tx_buffer_init(struct rte_eth_dev_tx_buffer *buffer, uint16_t size)
+{
+ int ret = 0;
+
+ if (buffer == NULL)
+ return -EINVAL;