+static inline void
+netdev_dpdk_vhost_update_tx_counters(struct netdev_stats *stats,
+ struct dp_packet **packets,
+ int attempted,
+ int dropped)
+{
+ int i;
+ int sent = attempted - dropped;
+
+ stats->tx_packets += sent;
+ stats->tx_dropped += dropped;
+
+ for (i = 0; i < sent; i++) {
+ stats->tx_bytes += dp_packet_size(packets[i]);
+ }
+}
+
+static void
+__netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
+ struct dp_packet **pkts, int cnt,
+ bool may_steal)
+{
+ struct netdev_dpdk *vhost_dev = netdev_dpdk_cast(netdev);
+ struct virtio_net *virtio_dev = netdev_dpdk_get_virtio(vhost_dev);
+ struct rte_mbuf **cur_pkts = (struct rte_mbuf **) pkts;
+ unsigned int total_pkts = cnt;
+ uint64_t start = 0;
+
+ if (OVS_UNLIKELY(!is_vhost_running(virtio_dev))) {
+ rte_spinlock_lock(&vhost_dev->stats_lock);
+ vhost_dev->stats.tx_dropped+= cnt;
+ rte_spinlock_unlock(&vhost_dev->stats_lock);
+ goto out;
+ }
+
+ if (vhost_dev->txq_needs_locking) {
+ qid = qid % vhost_dev->real_n_txq;
+ rte_spinlock_lock(&vhost_dev->tx_q[qid].tx_lock);
+ }
+
+ do {
+ int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ;
+ unsigned int tx_pkts;
+
+ tx_pkts = rte_vhost_enqueue_burst(virtio_dev, vhost_qid,
+ cur_pkts, cnt);
+ if (OVS_LIKELY(tx_pkts)) {
+ /* Packets have been sent.*/
+ cnt -= tx_pkts;
+ /* Prepare for possible next iteration.*/
+ cur_pkts = &cur_pkts[tx_pkts];
+ } else {
+ uint64_t timeout = VHOST_ENQ_RETRY_USECS * rte_get_timer_hz() / 1E6;
+ unsigned int expired = 0;
+
+ if (!start) {
+ start = rte_get_timer_cycles();
+ }
+
+ /*
+ * Unable to enqueue packets to vhost interface.
+ * Check available entries before retrying.
+ */
+ while (!rte_vring_available_entries(virtio_dev, vhost_qid)) {
+ if (OVS_UNLIKELY((rte_get_timer_cycles() - start) > timeout)) {
+ expired = 1;
+ break;
+ }
+ }
+ if (expired) {
+ /* break out of main loop. */
+ break;
+ }
+ }
+ } while (cnt);
+
+ if (vhost_dev->txq_needs_locking) {
+ rte_spinlock_unlock(&vhost_dev->tx_q[qid].tx_lock);
+ }
+
+ rte_spinlock_lock(&vhost_dev->stats_lock);
+ netdev_dpdk_vhost_update_tx_counters(&vhost_dev->stats, pkts, total_pkts,
+ cnt);
+ rte_spinlock_unlock(&vhost_dev->stats_lock);
+
+out:
+ if (may_steal) {
+ int i;
+
+ for (i = 0; i < total_pkts; i++) {
+ dp_packet_delete(pkts[i]);
+ }
+ }
+}
+