/* Copyright (c) Meta Platforms, Inc. and affiliates. */
 
 #include <linux/ethtool.h>
+#include <linux/ethtool_netlink.h>
 #include <linux/netdevice.h>
 #include <linux/pci.h>
 #include <net/ipv6.h>
        swap(clone->num_rx_queues, orig->num_rx_queues);
        swap(clone->num_tx_queues, orig->num_tx_queues);
        swap(clone->num_napi, orig->num_napi);
+       swap(clone->hds_thresh, orig->hds_thresh);
 }
 
 static void fbnic_aggregate_vector_counters(struct fbnic_net *fbn,
        ring->rx_mini_pending = fbn->hpq_size;
        ring->rx_jumbo_pending = fbn->ppq_size;
        ring->tx_pending = fbn->txq_size;
+
+       kernel_ring->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED;
+       kernel_ring->hds_thresh_max = FBNIC_HDS_THRESH_MAX;
+       kernel_ring->hds_thresh = fbn->hds_thresh;
 }
 
 static void fbnic_set_rings(struct fbnic_net *fbn,
-                           struct ethtool_ringparam *ring)
+                           struct ethtool_ringparam *ring,
+                           struct kernel_ethtool_ringparam *kernel_ring)
 {
        fbn->rcq_size = ring->rx_pending;
        fbn->hpq_size = ring->rx_mini_pending;
        fbn->ppq_size = ring->rx_jumbo_pending;
        fbn->txq_size = ring->tx_pending;
+       fbn->hds_thresh = kernel_ring->hds_thresh;
 }
 
 static int
                return -EINVAL;
        }
 
+       if (kernel_ring->tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED) {
+               NL_SET_ERR_MSG_MOD(extack, "Cannot disable TCP data split");
+               return -EINVAL;
+       }
+
        if (!netif_running(netdev)) {
-               fbnic_set_rings(fbn, ring);
+               fbnic_set_rings(fbn, ring, kernel_ring);
                return 0;
        }
 
        if (!clone)
                return -ENOMEM;
 
-       fbnic_set_rings(clone, ring);
+       fbnic_set_rings(clone, ring, kernel_ring);
 
        err = fbnic_alloc_napi_vectors(clone);
        if (err)
 static const struct ethtool_ops fbnic_ethtool_ops = {
        .supported_coalesce_params      = ETHTOOL_COALESCE_USECS |
                                          ETHTOOL_COALESCE_RX_MAX_FRAMES,
+       .supported_ring_params          = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
+                                         ETHTOOL_RING_USE_HDS_THRS,
        .rxfh_max_num_contexts          = FBNIC_RPC_RSS_TBL_COUNT,
        .get_drvinfo                    = fbnic_get_drvinfo,
        .get_regs_len                   = fbnic_get_regs_len,
 
 {
        struct fbnic_net *fbn = netdev_priv(nv->napi.dev);
        u32 log_size = fls(rcq->size_mask);
-       u32 rcq_ctl;
+       u32 hds_thresh = fbn->hds_thresh;
+       u32 rcq_ctl = 0;
 
        fbnic_config_drop_mode_rcq(nv, rcq);
 
-       rcq_ctl = FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PADLEN_MASK, FBNIC_RX_PAD) |
-                  FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_MAX_HDR_MASK,
-                             FBNIC_RX_MAX_HDR) |
+       /* Force lower bound on MAX_HEADER_BYTES. Below this, all frames should
+        * be split at L4. It would also result in the frames being split at
+        * L2/L3 depending on the frame size.
+        */
+       if (fbn->hds_thresh < FBNIC_HDR_BYTES_MIN) {
+               rcq_ctl = FBNIC_QUEUE_RDE_CTL0_EN_HDR_SPLIT;
+               hds_thresh = FBNIC_HDR_BYTES_MIN;
+       }
+
+       rcq_ctl |= FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PADLEN_MASK, FBNIC_RX_PAD) |
+                  FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_MAX_HDR_MASK, hds_thresh) |
                   FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PAYLD_OFF_MASK,
                              FBNIC_RX_PAYLD_OFFSET) |
                   FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PAYLD_PG_CL_MASK,
 
 #define FBNIC_RX_HROOM \
        (ALIGN(FBNIC_RX_TROOM + NET_SKB_PAD, 128) - FBNIC_RX_TROOM)
 #define FBNIC_RX_PAD                   0
-#define FBNIC_RX_MAX_HDR               (1536 - FBNIC_RX_PAD)
 #define FBNIC_RX_PAYLD_OFFSET          0
 #define FBNIC_RX_PAYLD_PG_CL           0
 
 #define FBNIC_RING_F_CTX               BIT(1)
 #define FBNIC_RING_F_STATS             BIT(2)  /* Ring's stats may be used */
 
+#define FBNIC_HDS_THRESH_MAX \
+       (4096 - FBNIC_RX_HROOM - FBNIC_RX_TROOM - FBNIC_RX_PAD)
+#define FBNIC_HDS_THRESH_DEFAULT \
+       (1536 - FBNIC_RX_PAD)
+#define FBNIC_HDR_BYTES_MIN            128
+
 struct fbnic_pkt_buff {
        struct xdp_buff buff;
        ktime_t hwtstamp;