<div dir="ltr">Hi Joe,<div><br></div><div>Thanks - subscribed and commenting now!</div><div><br></div><div>Regards,</div><div>Daniel</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Nov 18, 2017 at 12:23 AM, Joseph Salisbury <span dir="ltr"><<a href="mailto:joseph.salisbury@canonical.com" target="_blank">joseph.salisbury@canonical.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On 08/25/2017 12:42 PM, Daniel Axtens wrote:<br>
> Hi,<br>
><br>
> Sorry if I'm a little late to the party with this...<br>
><br>
> In support we've seen a case where ibmveth + openvswitch + bnx2x has<br>
> lead to some issues, which you should probably be aware of before<br>
> turning on large segments in more places.<br>
><br>
> Here's my summary from that support case, appropriately sanitised.<br>
><br>
> ==========<br>
><br>
> [Issue: we see a firmware assertion from an IBM branded bnx2x card.<br>
> Decoding the dump with the help of upstream shows that the assert is<br>
> caused by a packet with GSO on and gso_size > ~9700 bytes being passed<br>
> to the card. We traced the packets through the system, and came up<br>
> with this root cause. The system uses ibmveth to talk to AIX LPARs, a<br>
> bnx2x network card to talk to the world, and Open vSwitch to tie them<br>
> together. There is no VIOS involvement - the card is attached to the<br>
> Linux partition.]<br>
><br>
> The packets causing the issue come through the ibmveth interface -<br>
> from the AIX LPAR. The veth protocol is 'special' - communication<br>
> between LPARs on the same chassis can use very large (64k) frames to<br>
> reduce overhead. Normal networks cannot handle such large packets, so<br>
> traditionally, the VIOS partition would signal to the AIX partitions<br>
> that it was 'special', and AIX would send regular, ethernet-sized<br>
> packets to VIOS, which VIOS would then send out.<br>
><br>
> This signalling between VIOS and AIX is done in a way that is not<br>
> standards-compliant, and so was never made part of Linux. Instead, the<br>
> Linux driver has always understood large frames and passed them up the<br>
> network stack.<br>
><br>
> In some cases (e.g. with TCP), multiple TCP segments are coalesced<br>
> into one large packet. In Linux, this goes through the generic receive<br>
> offload code, using a similar mechanism to GSO. These segments can be<br>
> very large which presents as a very large MSS (maximum segment size)<br>
> or gso_size.<br>
><br>
> Normally, the large packet is simply passed to whatever network<br>
> application on Linux is going to consume it, and everything is OK.<br>
><br>
> However, in this case, the packets go through Open vSwitch, and are<br>
> then passed to the bnx2x driver. The bnx2x driver/hardware supports<br>
> TSO and GSO, but with a restriction: the maximum segment size is<br>
> limited to around 9700 bytes. Normally this is more than adequate as<br>
> jumbo frames are limited to 9000 bytes. However, if a large packet<br>
> with large (>9700 byte) TCP segments arrives through ibmveth, and is<br>
> passed to bnx2x, the hardware will panic.<br>
><br>
> Turning off TSO prevents the crash as the kernel resegments the data<br>
> and assembles the packets in software. This has a performance cost.<br>
><br>
> Clearly at the very least, bnx2x should not crash in this case, and I<br>
> am working towards a patch for that.<br>
><br>
> However, this still leaves us with some issues. The only thing the<br>
> bnx2x driver can sensibly do is drop the packet, which will prevent<br>
> the crash. However, there will still be issues with large packets:<br>
> when they are dropped, the other side will eventually realise that the<br>
> data is missing and ask for a retransmit, but the retransmit might<br>
> also be too big - there's no way of signalling back to the AIX LPAR<br>
> that it should reduce the MSS. Even if the data eventually gets<br>
> through there will be a latency/throughput/performance hit.<br>
><br>
> ==========<br>
><br>
> Seeing as IBM seems to be in active development in this area - indeed<br>
> this code explicitly deals with ibmveth + ovs, could we gently push<br>
> for this issue to be considered and fixed also?<br>
><br>
> I'm happy to engage on launchpad or whatever the most appropriate channel is.<br>
><br>
> Regards,<br>
> Daniel<br>
><br>
> On Sat, Aug 26, 2017 at 2:23 AM, Joseph Salisbury<br>
> <<a href="mailto:joseph.salisbury@canonical.com">joseph.salisbury@canonical.<wbr>com</a>> wrote:<br>
>> From: Sivakumar Krishnasamy <<a href="mailto:ksiva@linux.vnet.ibm.com">ksiva@linux.vnet.ibm.com</a>><br>
>><br>
>> BugLink: <a href="http://bugs.launchpad.net/bugs/1692538" rel="noreferrer" target="_blank">http://bugs.launchpad.net/<wbr>bugs/1692538</a><br>
>><br>
>> Current largesend and checksum offload feature in ibmveth driver,<br>
>>  - Source VM sends the TCP packets with ip_summed field set as<br>
>>    CHECKSUM_PARTIAL and TCP pseudo header checksum is placed in<br>
>>    checksum field<br>
>>  - CHECKSUM_PARTIAL flag in SKB will enable ibmveth driver to mark<br>
>>    "no checksum" and "checksum good" bits in transmit buffer descriptor<br>
>>    before the packet is delivered to pseries PowerVM Hypervisor<br>
>>  - If ibmveth has largesend capability enabled, transmit buffer descriptors<br>
>>    are market accordingly before packet is delivered to Hypervisor<br>
>>    (along with mss value for packets with length > MSS)<br>
>>  - Destination VM's ibmveth driver receives the packet with "checksum good"<br>
>>    bit set and so, SKB's ip_summed field is set with CHECKSUM_UNNECESSARY<br>
>>  - If "largesend" bit was on, mss value is copied from receive descriptor<br>
>>    into SKB's gso_size and other flags are appropriately set for<br>
>>    packets > MSS size<br>
>>  - The packet is now successfully delivered up the stack in destination VM<br>
>><br>
>> The offloads described above works fine for TCP communication among VMs in<br>
>> the same pseries server ( VM A <=> PowerVM Hypervisor <=> VM B )<br>
>><br>
>> We are now enabling support for OVS in pseries PowerVM environment. One of<br>
>> our requirements is to have ibmveth driver configured in "Trunk" mode, when<br>
>> they are used with OVS. This is because, PowerVM Hypervisor will no more<br>
>> bridge the packets between VMs, instead the packets are delivered to<br>
>> IO Server which hosts OVS to bridge them between VMs or to external<br>
>> networks (flow shown below),<br>
>>   VM A <=> PowerVM Hypervisor <=> IO Server(OVS) <=> PowerVM Hypervisor<br>
>>                                                                    <=> VM B<br>
>> In "IO server" the packet is received by inbound Trunk ibmveth and then<br>
>> delivered to OVS, which is then bridged to outbound Trunk ibmveth (shown<br>
>> below),<br>
>>         Inbound Trunk ibmveth <=> OVS <=> Outbound Trunk ibmveth<br>
>><br>
>> In this model, we hit the following issues which impacted the VM<br>
>> communication performance,<br>
>><br>
>>  - Issue 1: ibmveth doesn't support largesend and checksum offload features<br>
>>    when configured as "Trunk". Driver has explicit checks to prevent<br>
>>    enabling these offloads.<br>
>><br>
>>  - Issue 2: SYN packet drops seen at destination VM. When the packet<br>
>>    originates, it has CHECKSUM_PARTIAL flag set and as it gets delivered to<br>
>>    IO server's inbound Trunk ibmveth, on validating "checksum good" bits<br>
>>    in ibmveth receive routine, SKB's ip_summed field is set with<br>
>>    CHECKSUM_UNNECESSARY flag. This packet is then bridged by OVS (or Linux<br>
>>    Bridge) and delivered to outbound Trunk ibmveth. At this point the<br>
>>    outbound ibmveth transmit routine will not set "no checksum" and<br>
>>    "checksum good" bits in transmit buffer descriptor, as it does so only<br>
>>    when the ip_summed field is CHECKSUM_PARTIAL. When this packet gets<br>
>>    delivered to destination VM, TCP layer receives the packet with checksum<br>
>>    value of 0 and with no checksum related flags in ip_summed field. This<br>
>>    leads to packet drops. So, TCP connections never goes through fine.<br>
>><br>
>>  - Issue 3: First packet of a TCP connection will be dropped, if there is<br>
>>    no OVS flow cached in datapath. OVS while trying to identify the flow,<br>
>>    computes the checksum. The computed checksum will be invalid at the<br>
>>    receiving end, as ibmveth transmit routine zeroes out the pseudo<br>
>>    checksum value in the packet. This leads to packet drop.<br>
>><br>
>>  - Issue 4: ibmveth driver doesn't have support for SKB's with frag_list.<br>
>>    When Physical NIC has GRO enabled and when OVS bridges these packets,<br>
>>    OVS vport send code will end up calling dev_queue_xmit, which in turn<br>
>>    calls validate_xmit_skb.<br>
>>    In validate_xmit_skb routine, the larger packets will get segmented into<br>
>>    MSS sized segments, if SKB has a frag_list and if the driver to which<br>
>>    they are delivered to doesn't support NETIF_F_FRAGLIST feature.<br>
>><br>
>> This patch addresses the above four issues, thereby enabling end to end<br>
>> largesend and checksum offload support for better performance.<br>
>><br>
>>  - Fix for Issue 1 : Remove checks which prevent enabling TCP largesend and<br>
>>    checksum offloads.<br>
>>  - Fix for Issue 2 : When ibmveth receives a packet with "checksum good"<br>
>>    bit set and if its configured in Trunk mode, set appropriate SKB fields<br>
>>    using skb_partial_csum_set (ip_summed field is set with<br>
>>    CHECKSUM_PARTIAL)<br>
>>  - Fix for Issue 3: Recompute the pseudo header checksum before sending the<br>
>>    SKB up the stack.<br>
>>  - Fix for Issue 4: Linearize the SKBs with frag_list. Though we end up<br>
>>    allocating buffers and copying data, this fix gives<br>
>>    upto 4X throughput increase.<br>
>><br>
>> Note: All these fixes need to be dropped together as fixing just one of<br>
>> them will lead to other issues immediately (especially for Issues 1,2 & 3).<br>
>><br>
>> Signed-off-by: Sivakumar Krishnasamy <<a href="mailto:ksiva@linux.vnet.ibm.com">ksiva@linux.vnet.ibm.com</a>><br>
>> Signed-off-by: David S. Miller <<a href="mailto:davem@davemloft.net">davem@davemloft.net</a>><br>
>> (back ported from commit 66aa0678efc29abd2ab02a09b23f9a<wbr>8bc9f12a6c)<br>
>> Signed-off-by: Joseph Salisbury <<a href="mailto:joseph.salisbury@canonical.com">joseph.salisbury@canonical.<wbr>com</a>><br>
>> ---<br>
>>  drivers/net/ethernet/ibm/<wbr>ibmveth.c | 107 ++++++++++++++++++++++++++++++<wbr>-------<br>
>>  drivers/net/ethernet/ibm/<wbr>ibmveth.h |   1 +<br>
>>  2 files changed, 90 insertions(+), 18 deletions(-)<br>
>><br>
>> diff --git a/drivers/net/ethernet/ibm/<wbr>ibmveth.c b/drivers/net/ethernet/ibm/<wbr>ibmveth.c<br>
>> index 2f9b12c..0482491 100644<br>
>> --- a/drivers/net/ethernet/ibm/<wbr>ibmveth.c<br>
>> +++ b/drivers/net/ethernet/ibm/<wbr>ibmveth.c<br>
>> @@ -46,6 +46,8 @@<br>
>>  #include <asm/vio.h><br>
>>  #include <asm/iommu.h><br>
>>  #include <asm/firmware.h><br>
>> +#include <net/tcp.h><br>
>> +#include <net/ip6_checksum.h><br>
>><br>
>>  #include "ibmveth.h"<br>
>><br>
>> @@ -804,8 +806,7 @@ static int ibmveth_set_csum_offload(<wbr>struct net_device *dev, u32 data)<br>
>><br>
>>         ret = h_illan_attributes(adapter-><wbr>vdev->unit_address, 0, 0, &ret_attr);<br>
>><br>
>> -       if (ret == H_SUCCESS && !(ret_attr & IBMVETH_ILLAN_ACTIVE_TRUNK) &&<br>
>> -           !(ret_attr & IBMVETH_ILLAN_TRUNK_PRI_MASK) &&<br>
>> +       if (ret == H_SUCCESS &&<br>
>>             (ret_attr & IBMVETH_ILLAN_PADDED_PKT_CSUM)<wbr>) {<br>
>>                 ret4 = h_illan_attributes(adapter-><wbr>vdev->unit_address, clr_attr,<br>
>>                                          set_attr, &ret_attr);<br>
>> @@ -1035,6 +1036,15 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,<br>
>>         dma_addr_t dma_addr;<br>
>>         unsigned long mss = 0;<br>
>><br>
>> +       /* veth doesn't handle frag_list, so linearize the skb.<br>
>> +        * When GRO is enabled SKB's can have frag_list.<br>
>> +        */<br>
>> +       if (adapter->is_active_trunk &&<br>
>> +           skb_has_frag_list(skb) && __skb_linearize(skb)) {<br>
>> +               netdev->stats.tx_dropped++;<br>
>> +               goto out;<br>
>> +       }<br>
>> +<br>
>>         /*<br>
>>          * veth handles a maximum of 6 segments including the header, so<br>
>>          * we have to linearize the skb if there are more than this.<br>
>> @@ -1059,9 +1069,6 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,<br>
>><br>
>>         desc_flags = IBMVETH_BUF_VALID;<br>
>><br>
>> -       if (skb_is_gso(skb) && adapter->fw_large_send_<wbr>support)<br>
>> -               desc_flags |= IBMVETH_BUF_LRG_SND;<br>
>> -<br>
>>         if (skb->ip_summed == CHECKSUM_PARTIAL) {<br>
>>                 unsigned char *buf = skb_transport_header(skb) +<br>
>>                                                 skb->csum_offset;<br>
>> @@ -1071,6 +1078,9 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,<br>
>>                 /* Need to zero out the checksum */<br>
>>                 buf[0] = 0;<br>
>>                 buf[1] = 0;<br>
>> +<br>
>> +               if (skb_is_gso(skb) && adapter->fw_large_send_<wbr>support)<br>
>> +                       desc_flags |= IBMVETH_BUF_LRG_SND;<br>
>>         }<br>
>><br>
>>  retry_bounce:<br>
>> @@ -1123,7 +1133,7 @@ retry_bounce:<br>
>>                 descs[i+1].fields.address = dma_addr;<br>
>>         }<br>
>><br>
>> -       if (skb_is_gso(skb)) {<br>
>> +       if (skb->ip_summed == CHECKSUM_PARTIAL && skb_is_gso(skb)) {<br>
>>                 if (adapter->fw_large_send_<wbr>support) {<br>
>>                         mss = (unsigned long)skb_shinfo(skb)->gso_<wbr>size;<br>
>>                         adapter->tx_large_packets++;<br>
>> @@ -1224,6 +1234,71 @@ static void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt)<br>
>>         }<br>
>>  }<br>
>><br>
>> +static void ibmveth_rx_csum_helper(struct sk_buff *skb,<br>
>> +                                  struct ibmveth_adapter *adapter)<br>
>> +{<br>
>> +       struct iphdr *iph = NULL;<br>
>> +       struct ipv6hdr *iph6 = NULL;<br>
>> +       __be16 skb_proto = 0;<br>
>> +       u16 iphlen = 0;<br>
>> +       u16 iph_proto = 0;<br>
>> +       u16 tcphdrlen = 0;<br>
>> +<br>
>> +       skb_proto = be16_to_cpu(skb->protocol);<br>
>> +<br>
>> +       if (skb_proto == ETH_P_IP) {<br>
>> +               iph = (struct iphdr *)skb->data;<br>
>> +<br>
>> +               /* If the IP checksum is not offloaded and if the packet<br>
>> +                *  is large send, the checksum must be rebuilt.<br>
>> +                */<br>
>> +               if (iph->check == 0xffff) {<br>
>> +                       iph->check = 0;<br>
>> +                       iph->check = ip_fast_csum((unsigned char *)iph,<br>
>> +                                                 iph->ihl);<br>
>> +               }<br>
>> +<br>
>> +               iphlen = iph->ihl * 4;<br>
>> +               iph_proto = iph->protocol;<br>
>> +       } else if (skb_proto == ETH_P_IPV6) {<br>
>> +               iph6 = (struct ipv6hdr *)skb->data;<br>
>> +               iphlen = sizeof(struct ipv6hdr);<br>
>> +               iph_proto = iph6->nexthdr;<br>
>> +       }<br>
>> +<br>
>> +       /* In OVS environment, when a flow is not cached, specifically for a<br>
>> +        * new TCP connection, the first packet information is passed up<br>
>> +        * the user space for finding a flow. During this process, OVS computes<br>
>> +        * checksum on the first packet when CHECKSUM_PARTIAL flag is set.<br>
>> +        *<br>
>> +        * Given that we zeroed out TCP checksum field in transmit path<br>
>> +        * (refer ibmveth_start_xmit routine) as we set "no checksum bit",<br>
>> +        * OVS computed checksum will be incorrect w/o TCP pseudo checksum<br>
>> +        * in the packet. This leads to OVS dropping the packet and hence<br>
>> +        * TCP retransmissions are seen.<br>
>> +        *<br>
>> +        * So, re-compute TCP pseudo header checksum.<br>
>> +        */<br>
>> +       if (iph_proto == IPPROTO_TCP && adapter->is_active_trunk) {<br>
>> +               struct tcphdr *tcph = (struct tcphdr *)(skb->data + iphlen);<br>
>> +<br>
>> +               tcphdrlen = skb->len - iphlen;<br>
>> +<br>
>> +               /* Recompute TCP pseudo header checksum */<br>
>> +               if (skb_proto == ETH_P_IP)<br>
>> +                       tcph->check = ~csum_tcpudp_magic(iph->saddr,<br>
>> +                                       iph->daddr, tcphdrlen, iph_proto, 0);<br>
>> +               else if (skb_proto == ETH_P_IPV6)<br>
>> +                       tcph->check = ~csum_ipv6_magic(&iph6->saddr,<br>
>> +                                       &iph6->daddr, tcphdrlen, iph_proto, 0);<br>
>> +<br>
>> +               /* Setup SKB fields for checksum offload */<br>
>> +               skb_partial_csum_set(skb, iphlen,<br>
>> +                                    offsetof(struct tcphdr, check));<br>
>> +               skb_reset_network_header(skb);<br>
>> +       }<br>
>> +}<br>
>> +<br>
>>  static int ibmveth_poll(struct napi_struct *napi, int budget)<br>
>>  {<br>
>>         struct ibmveth_adapter *adapter =<br>
>> @@ -1231,7 +1306,6 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)<br>
>>         struct net_device *netdev = adapter->netdev;<br>
>>         int frames_processed = 0;<br>
>>         unsigned long lpar_rc;<br>
>> -       struct iphdr *iph;<br>
>>         u16 mss = 0;<br>
>><br>
>>  restart_poll:<br>
>> @@ -1289,17 +1363,7 @@ restart_poll:<br>
>><br>
>>                         if (csum_good) {<br>
>>                                 skb->ip_summed = CHECKSUM_UNNECESSARY;<br>
>> -                               if (be16_to_cpu(skb->protocol) == ETH_P_IP) {<br>
>> -                                       iph = (struct iphdr *)skb->data;<br>
>> -<br>
>> -                                       /* If the IP checksum is not offloaded and if the packet<br>
>> -                                        *  is large send, the checksum must be rebuilt.<br>
>> -                                        */<br>
>> -                                       if (iph->check == 0xffff) {<br>
>> -                                               iph->check = 0;<br>
>> -                                               iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);<br>
>> -                                       }<br>
>> -                               }<br>
>> +                               ibmveth_rx_csum_helper(skb, adapter);<br>
>>                         }<br>
>><br>
>>                         if (length > netdev->mtu + ETH_HLEN) {<br>
>> @@ -1621,6 +1685,13 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)<br>
>>                 netdev->hw_features |= NETIF_F_TSO;<br>
>>         }<br>
>><br>
>> +       adapter->is_active_trunk = false;<br>
>> +       if (ret == H_SUCCESS && (ret_attr & IBMVETH_ILLAN_ACTIVE_TRUNK)) {<br>
>> +               adapter->is_active_trunk = true;<br>
>> +               netdev->hw_features |= NETIF_F_FRAGLIST;<br>
>> +               netdev->features |= NETIF_F_FRAGLIST;<br>
>> +       }<br>
>> +<br>
>>         memcpy(netdev->dev_addr, mac_addr_p, ETH_ALEN);<br>
>><br>
>>         if (firmware_has_feature(FW_<wbr>FEATURE_CMO))<br>
>> diff --git a/drivers/net/ethernet/ibm/<wbr>ibmveth.h b/drivers/net/ethernet/ibm/<wbr>ibmveth.h<br>
>> index 7acda04..de6e381 100644<br>
>> --- a/drivers/net/ethernet/ibm/<wbr>ibmveth.h<br>
>> +++ b/drivers/net/ethernet/ibm/<wbr>ibmveth.h<br>
>> @@ -157,6 +157,7 @@ struct ibmveth_adapter {<br>
>>      int pool_config;<br>
>>      int rx_csum;<br>
>>      int large_send;<br>
>> +    bool is_active_trunk;<br>
>>      void *bounce_buffer;<br>
>>      dma_addr_t bounce_buffer_dma;<br>
>><br>
>> --<br>
>> 2.7.4<br>
>><br>
>><br>
>> --<br>
>> kernel-team mailing list<br>
>> <a href="mailto:kernel-team@lists.ubuntu.com">kernel-team@lists.ubuntu.com</a><br>
>> <a href="https://lists.ubuntu.com/mailman/listinfo/kernel-team" rel="noreferrer" target="_blank">https://lists.ubuntu.com/<wbr>mailman/listinfo/kernel-team</a><br>
</div></div>Hi Daniel,<br>
<br>
The SRU request for Xenial is on hold.  I forwarded the information you<br>
provided to IBM.  Thanks for the detailed write up!  I'll keep you up to<br>
date on any feedback they provide. <br>
<br>
You could also subscribe to the bug report, and feel free to comment as<br>
needed:<br>
<a href="http://pad.lv/1692538" rel="noreferrer" target="_blank">http://pad.lv/1692538</a><br>
<br>
Thanks,<br>
<br>
Joe<br>
<br>
<br>
<br>
<br>
<br>
</blockquote></div><br></div>