VPP-84 af_packet retry on EAGAIN, count on errors 34/1234/4
authorChris Luke <chrisy@flirble.org>
Tue, 24 May 2016 01:30:26 +0000 (21:30 -0400)
committerDamjan Marion <damarion@cisco.com>
Sat, 28 May 2016 16:34:39 +0000 (16:34 +0000)
When af_packet signals the kernel that there are packets in the tx
ring with sendto() the kernel sometimes responds with EAGAIN.
Previously the af_packet driver would treat any error from sendto()
as fatal.

Whilst there's not much we can do about this, count the errors
and let's try to not die on the spot or sit in a loop forever.

Change-Id: Id76ba5e07b744f1ed6f348ec838a1ac506a381c9
Signed-off-by: Chris Luke <chrisy@flirble.org>
vnet/vnet/devices/af_packet/af_packet.c
vnet/vnet/devices/af_packet/device.c

index b41eaf3..e3ed385 100644 (file)
@@ -115,7 +115,7 @@ create_packet_v2_sock(u8 * name, tpacket_req_t * rx_req, tpacket_req_t * tx_req,
   int opt = 1;
   if ((err = setsockopt(*fd, SOL_PACKET, PACKET_LOSS, &opt, sizeof(opt))) < 0)
     {
-      DBG_SOCK("Failed to set rx packet interface version");
+      DBG_SOCK("Failed to set packet tx ring error handling option");
       ret = VNET_API_ERROR_SYSCALL_ERROR_1;
       goto error;
     }
index 0671d9e..f572632 100644 (file)
 
 #include <vnet/devices/af_packet/af_packet.h>
 
-#define foreach_af_packet_tx_func_error               \
-_(FRAME_NOT_READY, "tx frame not ready")
+#define foreach_af_packet_tx_func_error               \
+_(FRAME_NOT_READY, "tx frame not ready")              \
+_(TXRING_EAGAIN,   "tx sendto temporary failure")     \
+_(TXRING_FATAL,    "tx sendto fatal failure")         \
+_(TXRING_OVERRUN,  "tx ring overrun")
 
 typedef enum {
 #define _(f,s) AF_PACKET_TX_ERROR_##f,
@@ -96,7 +99,7 @@ af_packet_interface_tx (vlib_main_t * vm,
 
       tph = (struct tpacket2_hdr *) (block_start + tx_frame * frame_size);
 
-      if(tph->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))
+      if (PREDICT_FALSE(tph->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)))
        {
          frame_not_ready++;
          goto next;
@@ -116,21 +119,41 @@ af_packet_interface_tx (vlib_main_t * vm,
       tph->tp_status = TP_STATUS_SEND_REQUEST;
       n_sent++;
 next:
+      /* check if we've exhausted the ring */
+      if (PREDICT_FALSE(frame_not_ready + n_sent == frame_num))
+        break;
+
       tx_frame = (tx_frame + 1) % frame_num;
     }
 
   CLIB_MEMORY_BARRIER();
 
-  if (n_sent)
+  if (PREDICT_TRUE(n_sent))
     {
       apif->next_tx_frame = tx_frame;
-      if (sendto(apif->fd, NULL, 0, MSG_DONTWAIT, NULL, 0) == -1)
-       clib_unix_error("tx sendto failure");
+
+      if (PREDICT_FALSE(sendto(apif->fd, NULL, 0,
+                               MSG_DONTWAIT, NULL, 0) == -1))
+        {
+          /* Uh-oh, drop & move on, but count whether it was fatal or not.
+           * Note that we have no reliable way to properly determine the
+           * disposition of the packets we just enqueued for delivery.
+           */
+          vlib_error_count (vm, node->node_index,
+                            unix_error_is_fatal(errno) ?
+                            AF_PACKET_TX_ERROR_TXRING_FATAL :
+                            AF_PACKET_TX_ERROR_TXRING_EAGAIN,
+                            n_sent);
+        }
     }
 
-  if (frame_not_ready)
+  if (PREDICT_FALSE(frame_not_ready))
     vlib_error_count (vm, node->node_index, AF_PACKET_TX_ERROR_FRAME_NOT_READY,
-                     frame_not_ready);
+                      frame_not_ready);
+
+  if (PREDICT_FALSE(frame_not_ready + n_sent == frame_num))
+    vlib_error_count (vm, node->node_index, AF_PACKET_TX_ERROR_TXRING_OVERRUN,
+                      n_left);
 
   vlib_buffer_free (vm, vlib_frame_args (frame), frame->n_vectors);
   return frame->n_vectors;