From 4b46c84901b6ea393a5118d7835d77aa6b3cef58 Mon Sep 17 00:00:00 2001 From: Chris Luke Date: Mon, 23 May 2016 21:30:26 -0400 Subject: VPP-84 af_packet retry on EAGAIN, count on errors 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 --- vnet/vnet/devices/af_packet/af_packet.c | 2 +- vnet/vnet/devices/af_packet/device.c | 39 ++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'vnet') diff --git a/vnet/vnet/devices/af_packet/af_packet.c b/vnet/vnet/devices/af_packet/af_packet.c index b41eaf3ba82..e3ed3857982 100644 --- a/vnet/vnet/devices/af_packet/af_packet.c +++ b/vnet/vnet/devices/af_packet/af_packet.c @@ -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; } diff --git a/vnet/vnet/devices/af_packet/device.c b/vnet/vnet/devices/af_packet/device.c index 0671d9e247d..f572632ce37 100644 --- a/vnet/vnet/devices/af_packet/device.c +++ b/vnet/vnet/devices/af_packet/device.c @@ -26,8 +26,11 @@ #include -#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; -- cgit 1.2.3-korg