vhost: interrupt mode support with mq
[vpp.git] / src / vnet / devices / virtio / vhost_user_output.c
index d48e437..d3e38bf 100644 (file)
 #include <vlib/vlib.h>
 #include <vlib/unix/unix.h>
 
-#include <vnet/ip/ip.h>
-
 #include <vnet/ethernet/ethernet.h>
 #include <vnet/devices/devices.h>
 #include <vnet/feature/feature.h>
 
-#include <vnet/devices/virtio/virtio.h>
 #include <vnet/devices/virtio/vhost_user.h>
 #include <vnet/devices/virtio/vhost_user_inline.h>
 
@@ -121,24 +118,13 @@ vhost_user_name_renumber (vnet_hw_interface_t * hi, u32 new_dev_instance)
   return 0;
 }
 
-/**
- * @brief Try once to lock the vring
- * @return 0 on success, non-zero on failure.
- */
-static_always_inline int
-vhost_user_vring_try_lock (vhost_user_intf_t * vui, u32 qid)
-{
-  return clib_atomic_test_and_set (vui->vring_locks[qid]);
-}
-
 /**
  * @brief Spin until the vring is successfully locked
  */
 static_always_inline void
 vhost_user_vring_lock (vhost_user_intf_t * vui, u32 qid)
 {
-  while (vhost_user_vring_try_lock (vui, qid))
-    ;
+  clib_spinlock_lock_if_init (&vui->vrings[qid].vring_lock);
 }
 
 /**
@@ -147,7 +133,7 @@ vhost_user_vring_lock (vhost_user_intf_t * vui, u32 qid)
 static_always_inline void
 vhost_user_vring_unlock (vhost_user_intf_t * vui, u32 qid)
 {
-  clib_atomic_release (vui->vring_locks[qid]);
+  clib_spinlock_unlock_if_init (&vui->vrings[qid].vring_lock);
 }
 
 static_always_inline void
@@ -166,18 +152,18 @@ vhost_user_tx_trace (vhost_trace_t * t,
   t->qid = qid;
 
   hdr_desc = &rxvq->desc[desc_current];
-  if (rxvq->desc[desc_current].flags & VIRTQ_DESC_F_INDIRECT)
+  if (rxvq->desc[desc_current].flags & VRING_DESC_F_INDIRECT)
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_INDIRECT;
       /* Header is the first here */
       hdr_desc = map_guest_mem (vui, rxvq->desc[desc_current].addr, &hint);
     }
-  if (rxvq->desc[desc_current].flags & VIRTQ_DESC_F_NEXT)
+  if (rxvq->desc[desc_current].flags & VRING_DESC_F_NEXT)
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_SIMPLE_CHAINED;
     }
-  if (!(rxvq->desc[desc_current].flags & VIRTQ_DESC_F_NEXT) &&
-      !(rxvq->desc[desc_current].flags & VIRTQ_DESC_F_INDIRECT))
+  if (!(rxvq->desc[desc_current].flags & VRING_DESC_F_NEXT) &&
+      !(rxvq->desc[desc_current].flags & VRING_DESC_F_INDIRECT))
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_SINGLE_DESC;
     }
@@ -208,8 +194,8 @@ vhost_user_tx_copy (vhost_user_intf_t * vui, vhost_copy_t * cpy,
              (!(dst3 = map_guest_mem (vui, cpy[3].dst, map_hint))))
            return 1;
 
-         CLIB_PREFETCH ((void *) cpy[2].src, 64, LOAD);
-         CLIB_PREFETCH ((void *) cpy[3].src, 64, LOAD);
+         clib_prefetch_load ((void *) cpy[2].src);
+         clib_prefetch_load ((void *) cpy[3].src);
 
          clib_memcpy_fast (dst0, (void *) cpy[0].src, cpy[0].len);
          clib_memcpy_fast (dst1, (void *) cpy[1].src, cpy[1].len);
@@ -239,10 +225,11 @@ vhost_user_handle_tx_offload (vhost_user_intf_t * vui, vlib_buffer_t * b,
   generic_header_offset_t gho = { 0 };
   int is_ip4 = b->flags & VNET_BUFFER_F_IS_IP4;
   int is_ip6 = b->flags & VNET_BUFFER_F_IS_IP6;
+  vnet_buffer_oflags_t oflags = vnet_buffer (b)->oflags;
 
   ASSERT (!(is_ip4 && is_ip6));
   vnet_generic_header_offset_parser (b, &gho, 1 /* l2 */ , is_ip4, is_ip6);
-  if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM)
+  if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM)
     {
       ip4_header_t *ip4;
 
@@ -252,45 +239,39 @@ vhost_user_handle_tx_offload (vhost_user_intf_t * vui, vlib_buffer_t * b,
     }
 
   /* checksum offload */
-  if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
+  if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
     {
       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
       hdr->csum_start = gho.l4_hdr_offset;
       hdr->csum_offset = offsetof (udp_header_t, checksum);
-      udp_header_t *udp =
-       (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset);
-      udp->checksum = 0;
     }
-  else if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
+  else if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
     {
       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
       hdr->csum_start = gho.l4_hdr_offset;
       hdr->csum_offset = offsetof (tcp_header_t, checksum);
-      tcp_header_t *tcp =
-       (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset);
-      tcp->checksum = 0;
     }
 
   /* GSO offload */
   if (b->flags & VNET_BUFFER_F_GSO)
     {
-      if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
+      if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
        {
          if (is_ip4 &&
-             (vui->features & (1ULL << FEAT_VIRTIO_NET_F_GUEST_TSO4)))
+             (vui->features & VIRTIO_FEATURE (VIRTIO_NET_F_GUEST_TSO4)))
            {
              hdr->gso_size = vnet_buffer2 (b)->gso_size;
              hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
            }
          else if (is_ip6 &&
-                  (vui->features & (1ULL << FEAT_VIRTIO_NET_F_GUEST_TSO6)))
+                  (vui->features & VIRTIO_FEATURE (VIRTIO_NET_F_GUEST_TSO6)))
            {
              hdr->gso_size = vnet_buffer2 (b)->gso_size;
              hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
            }
        }
-      else if ((vui->features & (1ULL << FEAT_VIRTIO_NET_F_GUEST_UFO)) &&
-              (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM))
+      else if ((vui->features & VIRTIO_FEATURE (VIRTIO_NET_F_GUEST_UFO)) &&
+              (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM))
        {
          hdr->gso_size = vnet_buffer2 (b)->gso_size;
          hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP;
@@ -299,7 +280,8 @@ vhost_user_handle_tx_offload (vhost_user_intf_t * vui, vlib_buffer_t * b,
 }
 
 static_always_inline void
-vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_vring_t * rxvq,
+vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_intf_t * vui,
+                               vhost_user_vring_t * rxvq,
                                u16 * n_descs_processed, u8 chained,
                                vlib_frame_t * frame, u32 n_left)
 {
@@ -312,10 +294,10 @@ vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_vring_t * rxvq,
 
   if (rxvq->used_wrap_counter)
     flags = desc_table[last_used_idx & rxvq->qsz_mask].flags |
-      (VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
+      (VRING_DESC_F_AVAIL | VRING_DESC_F_USED);
   else
     flags = desc_table[last_used_idx & rxvq->qsz_mask].flags &
-      ~(VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
+      ~(VRING_DESC_F_AVAIL | VRING_DESC_F_USED);
 
   vhost_user_advance_last_used_idx (rxvq);
 
@@ -323,10 +305,10 @@ vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_vring_t * rxvq,
     {
       if (rxvq->used_wrap_counter)
        desc_table[rxvq->last_used_idx & rxvq->qsz_mask].flags |=
-         (VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
+         (VRING_DESC_F_AVAIL | VRING_DESC_F_USED);
       else
        desc_table[rxvq->last_used_idx & rxvq->qsz_mask].flags &=
-         ~(VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
+         ~(VRING_DESC_F_AVAIL | VRING_DESC_F_USED);
       vhost_user_advance_last_used_idx (rxvq);
     }
 
@@ -339,7 +321,7 @@ vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_vring_t * rxvq,
       vring_packed_desc_t *desc_table = rxvq->packed_desc;
 
       while (desc_table[rxvq->last_used_idx & rxvq->qsz_mask].flags &
-            VIRTQ_DESC_F_NEXT)
+            VRING_DESC_F_NEXT)
        vhost_user_advance_last_used_idx (rxvq);
 
       /* Advance past the current chained table entries */
@@ -354,7 +336,7 @@ vhost_user_mark_desc_available (vlib_main_t * vm, vhost_user_vring_t * rxvq,
 
       rxvq->n_since_last_int += frame->n_vectors - n_left;
       if (rxvq->n_since_last_int > vum->coalesce_frames)
-       vhost_user_send_call (vm, rxvq);
+       vhost_user_send_call (vm, vui, rxvq);
     }
 }
 
@@ -374,19 +356,19 @@ vhost_user_tx_trace_packed (vhost_trace_t * t, vhost_user_intf_t * vui,
   t->qid = qid;
 
   hdr_desc = &rxvq->packed_desc[desc_current];
-  if (rxvq->packed_desc[desc_current].flags & VIRTQ_DESC_F_INDIRECT)
+  if (rxvq->packed_desc[desc_current].flags & VRING_DESC_F_INDIRECT)
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_INDIRECT;
       /* Header is the first here */
       hdr_desc = map_guest_mem (vui, rxvq->packed_desc[desc_current].addr,
                                &hint);
     }
-  if (rxvq->packed_desc[desc_current].flags & VIRTQ_DESC_F_NEXT)
+  if (rxvq->packed_desc[desc_current].flags & VRING_DESC_F_NEXT)
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_SIMPLE_CHAINED;
     }
-  if (!(rxvq->packed_desc[desc_current].flags & VIRTQ_DESC_F_NEXT) &&
-      !(rxvq->packed_desc[desc_current].flags & VIRTQ_DESC_F_INDIRECT))
+  if (!(rxvq->packed_desc[desc_current].flags & VRING_DESC_F_NEXT) &&
+      !(rxvq->packed_desc[desc_current].flags & VRING_DESC_F_INDIRECT))
     {
       t->virtio_ring_flags |= 1 << VIRTIO_TRACE_F_SINGLE_DESC;
     }
@@ -463,7 +445,7 @@ retry:
        * Go deeper in case of indirect descriptor.
        * To test it, turn off mrg_rxbuf.
        */
-      if (desc_table[desc_head].flags & VIRTQ_DESC_F_INDIRECT)
+      if (desc_table[desc_head].flags & VRING_DESC_F_INDIRECT)
        {
          indirect = 1;
          if (PREDICT_FALSE (desc_table[desc_head].len <
@@ -482,7 +464,7 @@ retry:
            }
          desc_index = 0;
        }
-      else if (rxvq->packed_desc[desc_head].flags & VIRTQ_DESC_F_NEXT)
+      else if (rxvq->packed_desc[desc_head].flags & VRING_DESC_F_NEXT)
        chained = 1;
 
       desc_len = vui->virtio_net_hdr_sz;
@@ -496,13 +478,11 @@ retry:
       hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE;
       hdr->num_buffers = 1;
 
-      or_flags = (b0->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM) ||
-       (b0->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) ||
-       (b0->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM);
+      or_flags = (b0->flags & VNET_BUFFER_F_OFFLOAD);
 
       /* Guest supports csum offload and buffer requires checksum offload? */
       if (or_flags &&
-         (vui->features & (1ULL << FEAT_VIRTIO_NET_F_GUEST_CSUM)))
+         (vui->features & VIRTIO_FEATURE (VIRTIO_NET_F_GUEST_CSUM)))
        vhost_user_handle_tx_offload (vui, b0, &hdr->hdr);
 
       /* Prepare a copy order executed later for the header */
@@ -529,7 +509,7 @@ retry:
                   * Test it with both indirect and mrg_rxbuf off
                   */
                  if (PREDICT_FALSE (!(desc_table[desc_index].flags &
-                                      VIRTQ_DESC_F_NEXT)))
+                                      VRING_DESC_F_NEXT)))
                    {
                      /*
                       * Last descriptor in chain.
@@ -621,7 +601,7 @@ retry:
          buffer_map_addr += cpy->len;
          desc_len += cpy->len;
 
-         CLIB_PREFETCH (&rxvq->packed_desc, CLIB_CACHE_LINE_BYTES, LOAD);
+         clib_prefetch_load (&rxvq->packed_desc);
 
          /* Check if vlib buffer has more data. If not, get more or break */
          if (PREDICT_TRUE (!bytes_left))
@@ -665,7 +645,7 @@ retry:
          copy_len = 0;
 
          /* give buffers back to driver */
-         vhost_user_mark_desc_available (vm, rxvq, &n_descs_processed,
+         vhost_user_mark_desc_available (vm, vui, rxvq, &n_descs_processed,
                                          chained, frame, n_left);
        }
 
@@ -680,8 +660,8 @@ done:
        vlib_error_count (vm, node->node_index,
                          VHOST_USER_TX_FUNC_ERROR_MMAP_FAIL, 1);
 
-      vhost_user_mark_desc_available (vm, rxvq, &n_descs_processed, chained,
-                                     frame, n_left);
+      vhost_user_mark_desc_available (vm, vui, rxvq, &n_descs_processed,
+                                     chained, frame, n_left);
     }
 
   /*
@@ -801,7 +781,7 @@ retry:
 
       /* Go deeper in case of indirect descriptor
        * I don't know of any driver providing indirect for RX. */
-      if (PREDICT_FALSE (rxvq->desc[desc_head].flags & VIRTQ_DESC_F_INDIRECT))
+      if (PREDICT_FALSE (rxvq->desc[desc_head].flags & VRING_DESC_F_INDIRECT))
        {
          if (PREDICT_FALSE
              (rxvq->desc[desc_head].len < sizeof (vring_desc_t)))
@@ -832,13 +812,11 @@ retry:
        hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE;
        hdr->num_buffers = 1;   //This is local, no need to check
 
-       or_flags = (b0->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM) ||
-         (b0->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) ||
-         (b0->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM);
+       or_flags = (b0->flags & VNET_BUFFER_F_OFFLOAD);
 
        /* Guest supports csum offload and buffer requires checksum offload? */
        if (or_flags
-           && (vui->features & (1ULL << FEAT_VIRTIO_NET_F_GUEST_CSUM)))
+           && (vui->features & VIRTIO_FEATURE (VIRTIO_NET_F_GUEST_CSUM)))
          vhost_user_handle_tx_offload (vui, b0, &hdr->hdr);
 
        // Prepare a copy order executed later for the header
@@ -858,7 +836,7 @@ retry:
        {
          if (buffer_len == 0)
            {                   //Get new output
-             if (desc_table[desc_index].flags & VIRTQ_DESC_F_NEXT)
+             if (desc_table[desc_index].flags & VRING_DESC_F_NEXT)
                {
                  //Next one is chained
                  desc_index = desc_table[desc_index].next;
@@ -898,7 +876,7 @@ retry:
                  desc_head = desc_index =
                    rxvq->avail->ring[rxvq->last_avail_idx & rxvq->qsz_mask];
                  if (PREDICT_FALSE
-                     (rxvq->desc[desc_head].flags & VIRTQ_DESC_F_INDIRECT))
+                     (rxvq->desc[desc_head].flags & VRING_DESC_F_INDIRECT))
                    {
                      //It is seriously unlikely that a driver will put indirect descriptor
                      //after non-indirect descriptor.
@@ -944,7 +922,7 @@ retry:
            buffer_map_addr += cpy->len;
            desc_len += cpy->len;
 
-           CLIB_PREFETCH (&rxvq->desc, CLIB_CACHE_LINE_BYTES, LOAD);
+           clib_prefetch_load (&rxvq->desc);
          }
 
          // Check if vlib buffer has more data. If not, get more or break.
@@ -1039,7 +1017,7 @@ done:
       rxvq->n_since_last_int += frame->n_vectors - n_left;
 
       if (rxvq->n_since_last_int > vum->coalesce_frames)
-       vhost_user_send_call (vm, rxvq);
+       vhost_user_send_call (vm, vui, rxvq);
     }
 
   vhost_user_vring_unlock (vui, qid);
@@ -1060,7 +1038,7 @@ done3:
 
 static __clib_unused clib_error_t *
 vhost_user_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index,
-                                    u32 qid, vnet_hw_interface_rx_mode mode)
+                                    u32 qid, vnet_hw_if_rx_mode mode)
 {
   vlib_main_t *vm = vnm->vlib_main;
   vnet_hw_interface_t *hif = vnet_get_hw_interface (vnm, hw_if_index);
@@ -1068,37 +1046,56 @@ vhost_user_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index,
   vhost_user_intf_t *vui =
     pool_elt_at_index (vum->vhost_user_interfaces, hif->dev_instance);
   vhost_user_vring_t *txvq = &vui->vrings[VHOST_VRING_IDX_TX (qid)];
+  vhost_cpu_t *cpu;
+
+  if (mode == txvq->mode)
+    return 0;
+
+  if ((mode != VNET_HW_IF_RX_MODE_POLLING) &&
+      (mode != VNET_HW_IF_RX_MODE_ADAPTIVE) &&
+      (mode != VNET_HW_IF_RX_MODE_INTERRUPT))
+    {
+      vu_log_err (vui, "unhandled mode %d changed for if %d queue %d", mode,
+                 hw_if_index, qid);
+      return clib_error_return (0, "unsupported");
+    }
 
-  if ((mode == VNET_HW_INTERFACE_RX_MODE_INTERRUPT) ||
-      (mode == VNET_HW_INTERFACE_RX_MODE_ADAPTIVE))
+  if (txvq->thread_index == ~0)
+    return clib_error_return (0, "Queue initialization is not finished yet");
+
+  cpu = vec_elt_at_index (vum->cpus, txvq->thread_index);
+  if ((mode == VNET_HW_IF_RX_MODE_INTERRUPT) ||
+      (mode == VNET_HW_IF_RX_MODE_ADAPTIVE))
     {
       if (txvq->kickfd_idx == ~0)
        {
          // We cannot support interrupt mode if the driver opts out
          return clib_error_return (0, "Driver does not support interrupt");
        }
-      if (txvq->mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
+      if (txvq->mode == VNET_HW_IF_RX_MODE_POLLING)
        {
+         ASSERT (cpu->polling_q_count != 0);
+         if (cpu->polling_q_count)
+           cpu->polling_q_count--;
          vum->ifq_count++;
          // Start the timer if this is the first encounter on interrupt
          // interface/queue
          if ((vum->ifq_count == 1) &&
-             (vum->coalesce_time > 0.0) && (vum->coalesce_frames > 0))
+             ((vum->coalesce_time > 0.0) || (vum->coalesce_frames > 0)))
            vlib_process_signal_event (vm,
                                       vhost_user_send_interrupt_node.index,
                                       VHOST_USER_EVENT_START_TIMER, 0);
        }
     }
-  else if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
+  else if (mode == VNET_HW_IF_RX_MODE_POLLING)
     {
-      if (((txvq->mode == VNET_HW_INTERFACE_RX_MODE_INTERRUPT) ||
-          (txvq->mode == VNET_HW_INTERFACE_RX_MODE_ADAPTIVE)) &&
-         vum->ifq_count)
+      if (((txvq->mode == VNET_HW_IF_RX_MODE_INTERRUPT) ||
+          (txvq->mode == VNET_HW_IF_RX_MODE_ADAPTIVE)) && vum->ifq_count)
        {
+         cpu->polling_q_count++;
          vum->ifq_count--;
          // Stop the timer if there is no more interrupt interface/queue
-         if ((vum->ifq_count == 0) &&
-             (vum->coalesce_time > 0.0) && (vum->coalesce_frames > 0))
+         if (vum->ifq_count == 0)
            vlib_process_signal_event (vm,
                                       vhost_user_send_interrupt_node.index,
                                       VHOST_USER_EVENT_STOP_TIMER, 0);
@@ -1106,17 +1103,7 @@ vhost_user_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index,
     }
 
   txvq->mode = mode;
-  if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING)
-    txvq->used->flags = VRING_USED_F_NO_NOTIFY;
-  else if ((mode == VNET_HW_INTERFACE_RX_MODE_ADAPTIVE) ||
-          (mode == VNET_HW_INTERFACE_RX_MODE_INTERRUPT))
-    txvq->used->flags = 0;
-  else
-    {
-      vu_log_err (vui, "unhandled mode %d changed for if %d queue %d", mode,
-                 hw_if_index, qid);
-      return clib_error_return (0, "unsupported");
-    }
+  vhost_user_set_operation_mode (vui, txvq);
 
   return 0;
 }