diff options
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/hisilicon/hns/hnae.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/hisilicon/hns/hns_enet.c | 76 |
2 files changed, 71 insertions, 7 deletions
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 09602f1187f5..8016854796fb 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -99,6 +99,8 @@ enum hnae_led_state { #define HNS_RX_FLAG_L3ID_IPV6 0x1 #define HNS_RX_FLAG_L4ID_UDP 0x0 #define HNS_RX_FLAG_L4ID_TCP 0x1 +#define HNS_RX_FLAG_L4ID_SCTP 0x3 + #define HNS_TXD_ASID_S 0 #define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 776d81e785d8..672b64606321 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -566,6 +566,71 @@ static void get_rx_desc_bnum(u32 bnum_flag, int *out_bnum) HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); } +static void hns_nic_rx_checksum(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb, u32 flag) +{ + struct net_device *netdev = ring_data->napi.dev; + u32 l3id; + u32 l4id; + + /* check if RX checksum offload is enabled */ + if (unlikely(!(netdev->features & NETIF_F_RXCSUM))) + return; + + /* In hardware, we only support checksum for the following protocols: + * 1) IPv4, + * 2) TCP(over IPv4 or IPv6), + * 3) UDP(over IPv4 or IPv6), + * 4) SCTP(over IPv4 or IPv6) + * but we support many L3(IPv4, IPv6, MPLS, PPPoE etc) and L4(TCP, + * UDP, GRE, SCTP, IGMP, ICMP etc.) protocols. + * + * Hardware limitation: + * Our present hardware RX Descriptor lacks L3/L4 checksum "Status & + * Error" bit (which usually can be used to indicate whether checksum + * was calculated by the hardware and if there was any error encountered + * during checksum calculation). + * + * Software workaround: + * We do get info within the RX descriptor about the kind of L3/L4 + * protocol coming in the packet and the error status. These errors + * might not just be checksum errors but could be related to version, + * length of IPv4, UDP, TCP etc. + * Because there is no-way of knowing if it is a L3/L4 error due to bad + * checksum or any other L3/L4 error, we will not (cannot) convey + * checksum status for such cases to upper stack and will not maintain + * the RX L3/L4 checksum counters as well. + */ + + l3id = hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S); + l4id = hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S); + + /* check L3 protocol for which checksum is supported */ + if ((l3id != HNS_RX_FLAG_L3ID_IPV4) && (l3id != HNS_RX_FLAG_L3ID_IPV6)) + return; + + /* check for any(not just checksum)flagged L3 protocol errors */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_L3E_B))) + return; + + /* we do not support checksum of fragmented packets */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_FRAG_B))) + return; + + /* check L4 protocol for which checksum is supported */ + if ((l4id != HNS_RX_FLAG_L4ID_TCP) && + (l4id != HNS_RX_FLAG_L4ID_UDP) && + (l4id != HNS_RX_FLAG_L4ID_SCTP)) + return; + + /* check for any(not just checksum)flagged L4 protocol errors */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_L4E_B))) + return; + + /* now, this has to be a packet with valid RX checksum */ + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, struct sk_buff **out_skb, int *out_bnum) { @@ -684,13 +749,10 @@ out_bnum_err: ring->stats.rx_pkts++; ring->stats.rx_bytes += skb->len; - if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) || - hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) { - ring->stats.l3l4_csum_err++; - return 0; - } - - skb->ip_summed = CHECKSUM_UNNECESSARY; + /* indicate to upper stack if our hardware has already calculated + * the RX checksum + */ + hns_nic_rx_checksum(ring_data, skb, bnum_flag); return 0; } |