Support segmentation of non-encapsulated udp4/udp6 packets. Like for tcp, the segmentation engine expects the udp csum to be seeded with 0 length. The HW inverts UDP checksums computed as 0 during segmentation. Signed-off-by: Daniel Zahka --- v2: - fall back to SW GSO for skbs shorter than MPNIC_MIN_FRAME_LEN, since HW LSO would segment the zero padding as payload --- drivers/net/ethernet/meta/mpnic/mpnic_csr.h | 2 ++ drivers/net/ethernet/meta/mpnic/mpnic_init.c | 1 + drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 3 ++- drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 36 +++++++++++++++++++------- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_csr.h b/drivers/net/ethernet/meta/mpnic/mpnic_csr.h index 672ce0a98c22..2cd627d62bf5 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_csr.h +++ b/drivers/net/ethernet/meta/mpnic/mpnic_csr.h @@ -20,6 +20,7 @@ #define MPNIC_TWD_L4_TYPE DESC_GENMASK(33, 32) enum { MPNIC_TWD_L4_TYPE_TCP = 1, + MPNIC_TWD_L4_TYPE_UDP = 2, }; #define MPNIC_TWD_FLAG_REQ_LSO DESC_BIT(35) @@ -335,6 +336,7 @@ enum { #define MPNIC_TOF_MODE_IPV4_ZERO_CSUM CSR_BIT(1) #define MPNIC_TOF_MODE_TCP_ZERO_CSUM CSR_BIT(2) +#define MPNIC_TOF_MODE_UDP_ZERO_CSUM CSR_BIT(3) #define MPNIC_TOF_MODE_L4_ZERO_CSUM CSR_BIT(4) #define MPNIC_TOF_TCP_FLAG_CLR 0x7e3030 /* 0x1f8c0c0 */ #define MPNIC_TOF_TCP_FLAG_CLR_FIRST_SEGMENT CSR_GENMASK(8, 0) diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_init.c b/drivers/net/ethernet/meta/mpnic/mpnic_init.c index b9a41656f8f7..ab832ec5de0e 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_init.c +++ b/drivers/net/ethernet/meta/mpnic/mpnic_init.c @@ -304,6 +304,7 @@ static void mpnic_txglb_init(struct mpnic_dev *mpd) MPNIC_IPV4_ID_MODE_INCREMENT) | FIELD_PREP(MPNIC_TOF_MODE_IPV4_ZERO_CSUM, 1) | FIELD_PREP(MPNIC_TOF_MODE_TCP_ZERO_CSUM, 1) | + FIELD_PREP(MPNIC_TOF_MODE_UDP_ZERO_CSUM, 0) | FIELD_PREP(MPNIC_TOF_MODE_L4_ZERO_CSUM, 0)); mpnic_wr64(mpd, MPNIC_TOF_TCP_FLAG_CLR, FIELD_PREP(MPNIC_TOF_TCP_FLAG_CLR_FIRST_SEGMENT, diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c index bde327ad3154..bf3b459ba5be 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c +++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c @@ -328,7 +328,8 @@ struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd) NETIF_F_RXCSUM | NETIF_F_TSO | NETIF_F_TSO_ECN | - NETIF_F_TSO6; + NETIF_F_TSO6 | + NETIF_F_GSO_UDP_L4; netdev->hw_features |= netdev->features; netdev->vlan_features |= netdev->features; diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c index b78f8599bbe8..2a0560b7dd19 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c +++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c @@ -223,29 +223,44 @@ static void mpnic_tx_fill_hdrs(struct sk_buff *skb, struct mpnic_tx_hdrs *hdrs) return; } - hdrs->l4_type = MPNIC_TWD_L4_TYPE_TCP; + if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) + hdrs->l4_type = MPNIC_TWD_L4_TYPE_TCP; + else + hdrs->l4_type = MPNIC_TWD_L4_TYPE_UDP; + hdrs->i3len = skb_transport_offset(skb) - skb_network_offset(skb); hdrs->l3_type = ip_hdr(skb)->version == 4 ? MPNIC_TWD_L3_TYPE_IPV4 : MPNIC_TWD_L3_TYPE_IPV6; } static bool -mpnic_tx_lso(struct mpnic_ring *ring, struct sk_buff *skb, __le64 *meta) +mpnic_tx_lso(struct mpnic_ring *ring, struct sk_buff *skb, __le64 *meta, + const struct mpnic_tx_hdrs *hdrs) { struct skb_shared_info *shinfo; unsigned int l4len, hdrlen; - struct tcphdr *tcph; - __be16 payload_len; + unsigned char *l4hdr; if (skb_cow_head(skb, 0)) return true; shinfo = skb_shinfo(skb); + l4hdr = skb_checksum_start(skb); + + if (hdrs->l4_type == MPNIC_TWD_L4_TYPE_TCP) { + struct tcphdr *tcph = (struct tcphdr *)l4hdr; + __be16 payload_len; - tcph = (struct tcphdr *)skb_checksum_start(skb); - l4len = __tcp_hdrlen(tcph); - payload_len = cpu_to_be16(skb->len - skb_checksum_start_offset(skb)); - csum_replace_by_diff(&tcph->check, (__force __wsum)payload_len); + l4len = __tcp_hdrlen(tcph); + payload_len = cpu_to_be16(skb->len - + skb_checksum_start_offset(skb)); + csum_replace_by_diff(&tcph->check, (__force __wsum)payload_len); + } else { + struct udphdr *udph = (struct udphdr *)l4hdr; + + l4len = sizeof(*udph); + csum_replace_by_diff(&udph->check, (__force __wsum)udph->len); + } *meta |= cpu_to_le64(FIELD_PREP(MPNIC_TWD_L4_HLEN, l4len / 4) | FIELD_PREP(MPNIC_TWD_MSS, shinfo->gso_size) | @@ -276,7 +291,7 @@ mpnic_tx_offloads(struct mpnic_ring *ring, struct sk_buff *skb, __le64 *meta) skb->csum_offset / 2)); if (skb_is_gso(skb)) { - if (mpnic_tx_lso(ring, skb, meta)) + if (mpnic_tx_lso(ring, skb, meta, &hdrs)) return true; } else { *meta |= cpu_to_le64(MPNIC_TWD_FLAG_REQ_CSO); @@ -369,7 +384,8 @@ netdev_features_t mpnic_features_check(struct sk_buff *skb, return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); if (skb_is_gso(skb) && - !FIELD_FIT(MPNIC_TWD_MSS, skb_shinfo(skb)->gso_size)) + (skb->len < MPNIC_MIN_FRAME_LEN || + !FIELD_FIT(MPNIC_TWD_MSS, skb_shinfo(skb)->gso_size))) return features & ~NETIF_F_GSO_MASK; return features; -- 2.52.0