TCP/UDP checksum offload API
Change-Id: I2cb6ce4e29813f6602b14e6e61713fb381fbcef8
Signed-off-by: Dave Barach <dave@barachs.net>
diff --git a/src/vnet/interface_output.c b/src/vnet/interface_output.c
index cdf1873..c95f08b 100644
--- a/src/vnet/interface_output.c
+++ b/src/vnet/interface_output.c
@@ -38,6 +38,10 @@
*/
#include <vnet/vnet.h>
+#include <vnet/ip/icmp46_packet.h>
+#include <vnet/ip/ip4.h>
+#include <vnet/ip/ip6.h>
+#include <vnet/udp/udp_packet.h>
#include <vnet/feature/feature.h>
typedef struct
@@ -153,14 +157,58 @@
}
}
-uword
-vnet_interface_output_node (vlib_main_t * vm,
- vlib_node_runtime_t * node, vlib_frame_t * frame)
+static_always_inline void
+calc_checksums (vlib_main_t * vm, vlib_buffer_t * b)
{
- vnet_main_t *vnm = vnet_get_main ();
+ ip4_header_t *ip4;
+ ip6_header_t *ip6;
+ tcp_header_t *th;
+ udp_header_t *uh;
+
+ int is_ip4 = (b->flags & VNET_BUFFER_F_IS_IP4) != 0;
+ int is_ip6 = (b->flags & VNET_BUFFER_F_IS_IP6) != 0;
+
+ ASSERT (!(is_ip4 && is_ip6));
+
+ ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
+ ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
+ th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
+ uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
+
+ if (is_ip4)
+ {
+ ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
+ if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM)
+ ip4->checksum = ip4_header_checksum (ip4);
+ if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
+ th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
+ if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
+ uh->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
+ }
+ if (is_ip6)
+ {
+ int bogus;
+ ASSERT (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM);
+ if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
+ th->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
+ if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
+ uh->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
+ }
+
+ b->flags &= ~VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
+ b->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
+ b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
+}
+
+static_always_inline uword
+vnet_interface_output_node_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, vnet_main_t * vnm,
+ vnet_hw_interface_t * hi,
+ int do_tx_offloads)
+{
vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
vnet_sw_interface_t *si;
- vnet_hw_interface_t *hi;
u32 n_left_to_tx, *from, *from_end, *to_tx;
u32 n_bytes, n_buffers, n_packets;
u32 n_bytes_b0, n_bytes_b1, n_bytes_b2, n_bytes_b3;
@@ -234,6 +282,7 @@
u32 bi0, bi1, bi2, bi3;
vlib_buffer_t *b0, *b1, *b2, *b3;
u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
+ u32 or_flags;
/* Prefetch next iteration. */
vlib_prefetch_buffer_with_index (vm, from[4], LOAD);
@@ -324,6 +373,22 @@
thread_index, tx_swif3, 1,
n_bytes_b3);
}
+
+ or_flags = b0->flags | b1->flags | b2->flags | b3->flags;
+
+ if (do_tx_offloads)
+ {
+ if (or_flags &
+ (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
+ VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
+ VNET_BUFFER_F_OFFLOAD_IP_CKSUM))
+ {
+ calc_checksums (vm, b0);
+ calc_checksums (vm, b1);
+ calc_checksums (vm, b2);
+ calc_checksums (vm, b3);
+ }
+ }
}
while (from + 1 <= from_end && n_left_to_tx >= 1)
@@ -363,6 +428,9 @@
thread_index, tx_swif0, 1,
n_bytes_b0);
}
+
+ if (do_tx_offloads)
+ calc_checksums (vm, b0);
}
vlib_put_next_frame (vm, node, next_index, n_left_to_tx);
@@ -376,6 +444,23 @@
return n_buffers;
}
+static_always_inline uword
+vnet_interface_output_node (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_hw_interface_t *hi;
+ vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
+ hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
+
+ if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD)
+ return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
+ /* do_tx_offloads */ 0);
+ else
+ return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
+ /* do_tx_offloads */ 1);
+}
+
VLIB_NODE_FUNCTION_MULTIARCH_CLONE (vnet_interface_output_node);
CLIB_MULTIARCH_SELECT_FN (vnet_interface_output_node);