Tunnels that are not natively supported for segmentation can be supported with GSO_PARTIAL. For GSO_PARTIAL, the driver uses L3 type OTHER, which tells the device to suppress any updates to the l3 headers of the packet. As a consequence of this, MANGLEID is required when the inner l3 is ipv4, and for tunnels that have an outer l3 of ipv4, we need to check if DF is set in mpnic_features_check_encap_gso(), because the device will not edit the IDs of the wire packets. Signed-off-by: Daniel Zahka --- drivers/net/ethernet/meta/mpnic/mpnic_csr.h | 1 + drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 9 +++++++++ drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_csr.h b/drivers/net/ethernet/meta/mpnic/mpnic_csr.h index 63282916eb13..03304e22d6ce 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_csr.h +++ b/drivers/net/ethernet/meta/mpnic/mpnic_csr.h @@ -29,6 +29,7 @@ enum { #define MPNIC_TWD_FLAG_REQ_COMPLETION DESC_BIT(37) #define MPNIC_TWD_L3_TYPE DESC_GENMASK(42, 40) enum { + MPNIC_TWD_L3_TYPE_OTHER = 0, MPNIC_TWD_L3_TYPE_IPV4 = 1, MPNIC_TWD_L3_TYPE_IPV6 = 2, MPNIC_TWD_L3_TYPE_V6V6 = 3, diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c index e391ca6483e0..fc19793036df 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c +++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c @@ -325,7 +325,15 @@ struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd) mpn->num_rx_queues = queues; mpn->num_napi = queues; + netdev->gso_partial_features = + NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | + NETIF_F_GSO_IPXIP4 | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + netdev->features |= + netdev->gso_partial_features | NETIF_F_GSO_IPXIP6 | NETIF_F_SG | NETIF_F_HW_CSUM | @@ -333,6 +341,7 @@ struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd) NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | + NETIF_F_GSO_PARTIAL | NETIF_F_GSO_UDP_L4; netdev->hw_features |= netdev->features; diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c index c349e54a3547..8b17acffe7e5 100644 --- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c +++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c @@ -236,6 +236,9 @@ static void mpnic_tx_fill_hdrs(struct sk_buff *skb, struct mpnic_tx_hdrs *hdrs) hdrs->i3len = skb_transport_offset(skb) - net_off; hdrs->l3_type = ip_hdr(skb)->version == 4 ? MPNIC_TWD_L3_TYPE_IPV4 : MPNIC_TWD_L3_TYPE_IPV6; + } else if (skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) { + hdrs->i3len = skb_inner_transport_offset(skb) - net_off; + hdrs->l3_type = MPNIC_TWD_L3_TYPE_OTHER; } else { inner_off = skb_inner_network_offset(skb); hdrs->o3len = inner_off - net_off; @@ -395,11 +398,27 @@ static bool mpnic_ipxip6_gso_ok(struct sk_buff *skb) static netdev_features_t mpnic_features_check_encap_gso(struct sk_buff *skb, netdev_features_t features) { + struct iphdr *iph; + if (skb_shinfo(skb)->gso_type & SKB_GSO_IPXIP6 && mpnic_ipxip6_gso_ok(skb)) return features; - return features & ~NETIF_F_GSO_IPXIP6; + features &= ~NETIF_F_GSO_IPXIP6; + + iph = ip_hdr(skb); + if (iph->version == 4 && !(iph->frag_off & htons(IP_DF))) + return features & ~NETIF_F_GSO_MASK; + + /* HW can segment inner udp4 with fixed IDs, but there is no UDP + * analog for MANGLEID, so defer to SW GSO. + */ + if (inner_ip_hdr(skb)->version == 4) + features &= ~NETIF_F_GSO_UDP_L4; + if (!(features & NETIF_F_TSO_MANGLEID)) + features &= ~NETIF_F_TSO; + + return features; } netdev_features_t mpnic_features_check(struct sk_buff *skb, -- 2.52.0