net: Support network packet checksum calc offloading

Create infrastructure that allows ethernet device driver to tell
if it supports network packet checksum offloading. This applies only
to IPv4, UDP or TCP checksums. The driver can enable/disable checksum
offloading separately for Tx and Rx network packets.

If the device (ethernet in this case) can calculate the network
packet checksum for IPv4, UDP or TCP, then do not calculate the
corresponding checksum by the stack itself.

Fixes #2987

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2018-03-14 10:55:19 +02:00
commit 85a2459edb
14 changed files with 143 additions and 35 deletions

View file

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys_io.h> #include <sys_io.h>
#include <net/ethernet.h>
#include "eth_dw_priv.h" #include "eth_dw_priv.h"
@ -372,9 +373,9 @@ static struct eth_runtime eth_0_runtime = {
#endif #endif
}; };
static struct net_if_api api_funcs = { static const struct ethernet_api api_funcs = {
.init = eth_initialize, .iface_api.init = eth_initialize,
.send = eth_tx, .iface_api.send = eth_tx,
}; };
NET_DEVICE_INIT(eth_dw_0, CONFIG_ETH_DW_0_NAME, NET_DEVICE_INIT(eth_dw_0, CONFIG_ETH_DW_0_NAME,

View file

@ -705,9 +705,9 @@ static void eth_enc28j60_iface_init_0(struct net_if *iface)
context->iface = iface; context->iface = iface;
} }
static struct net_if_api api_funcs_0 = { static const struct ethernet_api api_funcs_0 = {
.init = eth_enc28j60_iface_init_0, .iface_api.init = eth_enc28j60_iface_init_0,
.send = eth_net_tx, .iface_api.send = eth_net_tx,
}; };
static struct eth_enc28j60_runtime eth_enc28j60_0_runtime; static struct eth_enc28j60_runtime eth_enc28j60_0_runtime;

View file

@ -601,9 +601,9 @@ static void eth_0_iface_init(struct net_if *iface)
context->iface = iface; context->iface = iface;
} }
static struct net_if_api api_funcs_0 = { static const struct ethernet_api api_funcs_0 = {
.init = eth_0_iface_init, .iface_api.init = eth_0_iface_init,
.send = eth_tx, .iface_api.send = eth_tx,
}; };
static void eth_mcux_rx_isr(void *p) static void eth_mcux_rx_isr(void *p)

View file

@ -228,9 +228,9 @@ static void eth_iface_init(struct net_if *iface)
} }
} }
static struct net_if_api eth_if_api = { static const struct ethernet_api eth_if_api = {
.init = eth_iface_init, .iface_api.init = eth_iface_init,
.send = eth_send, .iface_api.send = eth_send,
}; };
NET_DEVICE_INIT(eth_native_posix, CONFIG_ETH_NATIVE_POSIX_DRV_NAME, NET_DEVICE_INIT(eth_native_posix, CONFIG_ETH_NATIVE_POSIX_DRV_NAME,

View file

@ -30,6 +30,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <net/net_pkt.h> #include <net/net_pkt.h>
#include <net/net_if.h> #include <net/net_if.h>
#include <net/ethernet.h>
#include <i2c.h> #include <i2c.h>
#include <soc.h> #include <soc.h>
#include "phy_sam_gmac.h" #include "phy_sam_gmac.h"
@ -838,9 +839,9 @@ static void eth0_iface_init(struct net_if *iface)
link_configure(cfg->regs, link_status); link_configure(cfg->regs, link_status);
} }
static struct net_if_api eth0_api = { static const struct ethernet_api eth0_api = {
.init = eth0_iface_init, .iface_api.init = eth0_iface_init,
.send = eth_tx, .iface_api.send = eth_tx,
}; };
static struct device DEVICE_NAME_GET(eth0_sam_gmac); static struct device DEVICE_NAME_GET(eth0_sam_gmac);

View file

@ -15,6 +15,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <net/net_pkt.h> #include <net/net_pkt.h>
#include <net/net_if.h> #include <net/net_if.h>
#include <net/ethernet.h>
#include <soc.h> #include <soc.h>
#include <misc/printk.h> #include <misc/printk.h>
#include <clock_control.h> #include <clock_control.h>
@ -368,9 +369,9 @@ static void eth0_iface_init(struct net_if *iface)
NET_LINK_ETHERNET); NET_LINK_ETHERNET);
} }
static struct net_if_api eth0_api = { static const struct ethernet_api eth0_api = {
.init = eth0_iface_init, .iface_api.init = eth0_iface_init,
.send = eth_tx, .iface_api.send = eth_tx,
}; };
static struct device DEVICE_NAME_GET(eth0_stm32_hal); static struct device DEVICE_NAME_GET(eth0_stm32_hal);

View file

@ -27,6 +27,7 @@
#include <net/net_if.h> #include <net/net_if.h>
#include <net/net_core.h> #include <net/net_core.h>
#include <console/uart_pipe.h> #include <console/uart_pipe.h>
#include <net/ethernet.h>
#define SLIP_END 0300 #define SLIP_END 0300
#define SLIP_ESC 0333 #define SLIP_ESC 0333
@ -464,18 +465,24 @@ use_random_mac:
NET_LINK_ETHERNET); NET_LINK_ETHERNET);
} }
static struct net_if_api slip_if_api = {
.init = slip_iface_init,
.send = slip_send,
};
static struct slip_context slip_context_data; static struct slip_context slip_context_data;
#if defined(CONFIG_SLIP_TAP) && defined(CONFIG_NET_L2_ETHERNET) #if defined(CONFIG_SLIP_TAP) && defined(CONFIG_NET_L2_ETHERNET)
static const struct ethernet_api slip_if_api = {
.iface_api.init = slip_iface_init,
.iface_api.send = slip_send,
};
#define _SLIP_L2_LAYER ETHERNET_L2 #define _SLIP_L2_LAYER ETHERNET_L2
#define _SLIP_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2) #define _SLIP_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2)
#define _SLIP_MTU 1500 #define _SLIP_MTU 1500
#else #else
static const struct net_if_api slip_if_api = {
.init = slip_iface_init,
.send = slip_send,
};
#define _SLIP_L2_LAYER DUMMY_L2 #define _SLIP_L2_LAYER DUMMY_L2
#define _SLIP_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) #define _SLIP_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
#define _SLIP_MTU 576 #define _SLIP_MTU 576

View file

@ -39,6 +39,25 @@ extern "C" {
#define NET_ETH_MINIMAL_FRAME_SIZE 60 #define NET_ETH_MINIMAL_FRAME_SIZE 60
enum eth_hw_caps {
/** TX Checksum offloading supported */
ETH_HW_TX_CHKSUM_OFFLOAD = BIT(0),
/** RX Checksum offloading supported */
ETH_HW_RX_CHKSUM_OFFLOAD = BIT(1),
};
struct ethernet_api {
/**
* The net_if_api must be placed in first position in this
* struct so that we are compatible with network interface API.
*/
struct net_if_api iface_api;
/** Get the device capabilities */
enum eth_hw_caps (*get_capabilities)(struct device *dev);
} __packed;
struct net_eth_addr { struct net_eth_addr {
u8_t addr[6]; u8_t addr[6];
}; };
@ -94,6 +113,26 @@ const struct net_eth_addr *net_eth_broadcast_addr(void);
void net_eth_ipv6_mcast_to_mac_addr(const struct in6_addr *ipv6_addr, void net_eth_ipv6_mcast_to_mac_addr(const struct in6_addr *ipv6_addr,
struct net_eth_addr *mac_addr); struct net_eth_addr *mac_addr);
/**
* @brief Return ethernet device hardware capability information.
*
* @param iface Network interface
*
* @return Hardware capabilities
*/
static inline
enum eth_hw_caps net_eth_get_hw_capabilities(struct net_if *iface)
{
const struct ethernet_api *eth =
net_if_get_device(iface)->driver_api;
if (!eth->get_capabilities) {
return 0;
}
return eth->get_capabilities(net_if_get_device(iface));
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1261,6 +1261,29 @@ void net_if_unregister_link_cb(struct net_if_link_cb *link);
void net_if_call_link_cb(struct net_if *iface, struct net_linkaddr *lladdr, void net_if_call_link_cb(struct net_if *iface, struct net_linkaddr *lladdr,
int status); int status);
/**
* @brief Check if received network packet checksum calculation can be avoided
* or not. For example many ethernet devices support network packet offloading
* in which case the IP stack does not need to calculate the checksum.
*
* @param iface Network interface
*
* @return True if checksum needs to be calculated, false otherwise.
*/
bool net_if_need_calc_rx_checksum(struct net_if *iface);
/**
* @brief Check if network packet checksum calculation can be avoided or not
* when sending the packet. For example many ethernet devices support network
* packet offloading in which case the IP stack does not need to calculate the
* checksum.
*
* @param iface Network interface
*
* @return True if checksum needs to be calculated, false otherwise.
*/
bool net_if_need_calc_tx_checksum(struct net_if *iface);
/** /**
* @brief Get interface according to index * @brief Get interface according to index
* *

View file

@ -907,7 +907,8 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt)
* If the checksum calculation fails, then discard the message. * If the checksum calculation fails, then discard the message.
*/ */
if (IS_ENABLED(CONFIG_NET_UDP_CHECKSUM) && if (IS_ENABLED(CONFIG_NET_UDP_CHECKSUM) &&
proto == IPPROTO_UDP) { proto == IPPROTO_UDP &&
net_if_need_calc_rx_checksum(net_pkt_iface(pkt))) {
u16_t chksum_calc; u16_t chksum_calc;
net_udp_set_chksum(pkt, pkt->frags); net_udp_set_chksum(pkt, pkt->frags);
@ -922,7 +923,8 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt)
} }
} else if (IS_ENABLED(CONFIG_NET_TCP_CHECKSUM) && } else if (IS_ENABLED(CONFIG_NET_TCP_CHECKSUM) &&
proto == IPPROTO_TCP) { proto == IPPROTO_TCP &&
net_if_need_calc_rx_checksum(net_pkt_iface(pkt))) {
u16_t chksum_calc; u16_t chksum_calc;
net_tcp_set_chksum(pkt, pkt->frags); net_tcp_set_chksum(pkt, pkt->frags);

View file

@ -100,15 +100,18 @@ int net_ipv4_finalize_raw(struct net_pkt *pkt, u8_t next_header)
NET_IPV4_HDR(pkt)->len[1] = total_len & 0xff; NET_IPV4_HDR(pkt)->len[1] = total_len & 0xff;
NET_IPV4_HDR(pkt)->chksum = 0; NET_IPV4_HDR(pkt)->chksum = 0;
NET_IPV4_HDR(pkt)->chksum = ~net_calc_chksum_ipv4(pkt);
#if defined(CONFIG_NET_UDP) #if defined(CONFIG_NET_UDP)
if (next_header == IPPROTO_UDP) { if (next_header == IPPROTO_UDP &&
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
NET_IPV4_HDR(pkt)->chksum = ~net_calc_chksum_ipv4(pkt);
net_udp_set_chksum(pkt, pkt->frags); net_udp_set_chksum(pkt, pkt->frags);
} }
#endif #endif
#if defined(CONFIG_NET_TCP) #if defined(CONFIG_NET_TCP)
if (next_header == IPPROTO_TCP) { if (next_header == IPPROTO_TCP &&
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
NET_IPV4_HDR(pkt)->chksum = ~net_calc_chksum_ipv4(pkt);
net_tcp_set_chksum(pkt, pkt->frags); net_tcp_set_chksum(pkt, pkt->frags);
} }
#endif #endif

View file

@ -779,13 +779,15 @@ int net_ipv6_finalize_raw(struct net_pkt *pkt, u8_t next_header)
NET_IPV6_HDR(pkt)->len[1] = total_len & 0xff; NET_IPV6_HDR(pkt)->len[1] = total_len & 0xff;
#if defined(CONFIG_NET_UDP) #if defined(CONFIG_NET_UDP)
if (next_header == IPPROTO_UDP) { if (next_header == IPPROTO_UDP &&
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
net_udp_set_chksum(pkt, pkt->frags); net_udp_set_chksum(pkt, pkt->frags);
} else } else
#endif #endif
#if defined(CONFIG_NET_TCP) #if defined(CONFIG_NET_TCP)
if (next_header == IPPROTO_TCP) { if (next_header == IPPROTO_TCP &&
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
net_tcp_set_chksum(pkt, pkt->frags); net_tcp_set_chksum(pkt, pkt->frags);
} else } else
#endif #endif

View file

@ -18,6 +18,7 @@
#include <net/net_if.h> #include <net/net_if.h>
#include <net/arp.h> #include <net/arp.h>
#include <net/net_mgmt.h> #include <net/net_mgmt.h>
#include <net/ethernet.h>
#include "net_private.h" #include "net_private.h"
#include "ipv6.h" #include "ipv6.h"
@ -1807,6 +1808,29 @@ void net_if_call_link_cb(struct net_if *iface, struct net_linkaddr *lladdr,
} }
} }
static bool need_calc_checksum(struct net_if *iface, enum eth_hw_caps caps)
{
#if defined(CONFIG_NET_L2_ETHERNET)
if (iface->l2 != &NET_L2_GET_NAME(ETHERNET)) {
return true;
}
return !(net_eth_get_hw_capabilities(iface) & caps);
#else
return true;
#endif
}
bool net_if_need_calc_tx_checksum(struct net_if *iface)
{
return need_calc_checksum(iface, ETH_HW_TX_CHKSUM_OFFLOAD);
}
bool net_if_need_calc_rx_checksum(struct net_if *iface)
{
return need_calc_checksum(iface, ETH_HW_RX_CHKSUM_OFFLOAD);
}
struct net_if *net_if_get_by_index(u8_t index) struct net_if *net_if_get_by_index(u8_t index)
{ {
if (&__net_if_start[index] >= __net_if_end) { if (&__net_if_start[index] >= __net_if_end) {

View file

@ -283,15 +283,20 @@ static void setup_eth_header(struct net_if *iface, struct net_pkt *pkt,
struct net_arp_context net_arp_context_data; struct net_arp_context net_arp_context_data;
static struct net_if_api net_arp_if_api = { #if defined(CONFIG_NET_ARP) && defined(CONFIG_NET_L2_ETHERNET)
static const struct ethernet_api net_arp_if_api = {
.iface_api.init = net_arp_iface_init,
.iface_api.send = tester_send,
};
#define _ETH_L2_LAYER ETHERNET_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2)
#else
static const struct net_if_api net_arp_if_api = {
.init = net_arp_iface_init, .init = net_arp_iface_init,
.send = tester_send, .send = tester_send,
}; };
#if defined(CONFIG_NET_ARP) && defined(CONFIG_NET_L2_ETHERNET)
#define _ETH_L2_LAYER ETHERNET_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2)
#else
#define _ETH_L2_LAYER DUMMY_L2 #define _ETH_L2_LAYER DUMMY_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) #define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
#endif #endif