Print this page
16884 viona TSO should better handle csum offloads

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/intel/io/viona/viona_tx.c
          +++ new/usr/src/uts/intel/io/viona/viona_tx.c
↓ open down ↓ 28 lines elided ↑ open up ↑
  29   29   * You may only use this file in accordance with the terms of version
  30   30   * 1.0 of the CDDL.
  31   31   *
  32   32   * A full copy of the text of the CDDL should have accompanied this
  33   33   * source.  A copy of the CDDL is also available via the Internet at
  34   34   * http://www.illumos.org/license/CDDL.
  35   35   *
  36   36   * Copyright 2015 Pluribus Networks Inc.
  37   37   * Copyright 2019 Joyent, Inc.
  38   38   * Copyright 2024 Oxide Computer Company
       39 + * Copyright 2024 MNX Cloud, Inc.
  39   40   */
  40   41  
  41   42  
  42   43  #include <sys/types.h>
  43   44  #include <sys/smt.h>
  44   45  #include <sys/strsubr.h>
  45   46  
  46   47  #include <sys/pattr.h>
  47   48  #include <sys/dlpi.h>
  48   49  #include <inet/ip.h>
↓ open down ↓ 328 lines elided ↑ open up ↑
 377  378                  ip6_t *ip6h = (ip6_t *)(mp->b_rptr + eth_len);
 378  379  
 379  380                  ipproto = ip6h->ip6_nxt;
 380  381          }
 381  382  
 382  383          /*
 383  384           * We ignore hdr_len because the spec says it can't be
 384  385           * trusted. Besides, our own stack will determine the header
 385  386           * boundary.
 386  387           */
 387      -        if ((link->l_cap_csum & HCKSUM_INET_PARTIAL) != 0 &&
 388      -            (hdr->vrh_gso_type & VIRTIO_NET_HDR_GSO_TCPV4) != 0 &&
      388 +        if ((hdr->vrh_gso_type & VIRTIO_NET_HDR_GSO_TCPV4) != 0 &&
 389  389              ftype == ETHERTYPE_IP) {
 390      -                uint16_t        *cksump;
 391      -                uint32_t        cksum;
 392      -                ipaddr_t        src = ipha->ipha_src;
 393      -                ipaddr_t        dst = ipha->ipha_dst;
      390 +                if ((link->l_cap_csum & HCKSUM_INET_PARTIAL) != 0) {
      391 +                        uint16_t        *cksump;
      392 +                        uint32_t        cksum;
      393 +                        ipaddr_t        src = ipha->ipha_src;
      394 +                        ipaddr_t        dst = ipha->ipha_dst;
 394  395  
 395      -                /*
 396      -                 * Our native IP stack doesn't set the L4 length field
 397      -                 * of the pseudo header when LSO is in play. Other IP
 398      -                 * stacks, e.g. Linux, do include the length field.
 399      -                 * This is a problem because the hardware expects that
 400      -                 * the length field is not set. When it is set it will
 401      -                 * cause an incorrect TCP checksum to be generated.
 402      -                 * The reason this works in Linux is because Linux
 403      -                 * corrects the pseudo-header checksum in the driver
 404      -                 * code. In order to get the correct HW checksum we
 405      -                 * need to assume the guest's IP stack gave us a bogus
 406      -                 * TCP partial checksum and calculate it ourselves.
 407      -                 */
 408      -                cksump = IPH_TCPH_CHECKSUMP(ipha, IPH_HDR_LENGTH(ipha));
 409      -                cksum = IP_TCP_CSUM_COMP;
 410      -                cksum += (dst >> 16) + (dst & 0xFFFF) +
 411      -                    (src >> 16) + (src & 0xFFFF);
 412      -                cksum = (cksum & 0xFFFF) + (cksum >> 16);
 413      -                *(cksump) = (cksum & 0xFFFF) + (cksum >> 16);
      396 +                        /*
      397 +                         * Our native IP stack doesn't set the L4 length field
      398 +                         * of the pseudo header when LSO is in play. Other IP
      399 +                         * stacks, e.g. Linux, do include the length field.
      400 +                         * This is a problem because the hardware expects that
      401 +                         * the length field is not set. When it is set it will
      402 +                         * cause an incorrect TCP checksum to be generated.
      403 +                         * The reason this works in Linux is because Linux
      404 +                         * corrects the pseudo-header checksum in the driver
      405 +                         * code. In order to get the correct HW checksum we
      406 +                         * need to assume the guest's IP stack gave us a bogus
      407 +                         * TCP partial checksum and calculate it ourselves.
      408 +                         */
      409 +                        cksump = IPH_TCPH_CHECKSUMP(ipha, IPH_HDR_LENGTH(ipha));
      410 +                        cksum = IP_TCP_CSUM_COMP;
      411 +                        cksum += (dst >> 16) + (dst & 0xFFFF) +
      412 +                            (src >> 16) + (src & 0xFFFF);
      413 +                        cksum = (cksum & 0xFFFF) + (cksum >> 16);
      414 +                        *(cksump) = (cksum & 0xFFFF) + (cksum >> 16);
      415 +                }
 414  416  
 415  417                  /*
 416  418                   * Since viona is a "legacy device", the data stored
 417  419                   * by the driver will be in the guest's native endian
 418  420                   * format (see sections 2.4.3 and 5.1.6.1 of the
 419  421                   * VIRTIO 1.0 spec for more info). At this time the
 420  422                   * only guests using viona are x86 and we can assume
 421  423                   * little-endian.
 422  424                   */
 423  425                  lso_info_set(mp, LE_16(hdr->vrh_gso_size), HW_LSO);
↓ open down ↓ 361 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX