lkml.org 
[lkml]   [2008]   [Jun]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] [UPDATED] ixgbe: add LRO support
    Date
    From: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>

    Support for in-kernel LRO with the ability to enable/disable via ethtool
    based on comments from Ben Hutchings and Randy Dunlap.


    Signed-off-by: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>
    Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
    Signed-off-by: PJ Waskiewicz <peter.p.waskiewicz.jr@intel.com>
    ---

    drivers/net/Kconfig | 1
    drivers/net/ixgbe/ixgbe.h | 9 +++
    drivers/net/ixgbe/ixgbe_ethtool.c | 11 ++++
    drivers/net/ixgbe/ixgbe_main.c | 111 ++++++++++++++++++++++++++++++-------
    4 files changed, 111 insertions(+), 21 deletions(-)

    diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
    index af46341..f6a15ed 100644
    --- a/drivers/net/Kconfig
    +++ b/drivers/net/Kconfig
    @@ -2451,6 +2451,7 @@ config EHEA
    config IXGBE
    tristate "Intel(R) 10GbE PCI Express adapters support"
    depends on PCI
    + select INET_LRO
    ---help---
    This driver supports Intel(R) 10GbE PCI Express family of
    adapters. For more information on how to identify your adapter, go
    diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
    index d981134..956914a 100644
    --- a/drivers/net/ixgbe/ixgbe.h
    +++ b/drivers/net/ixgbe/ixgbe.h
    @@ -32,6 +32,7 @@
    #include <linux/types.h>
    #include <linux/pci.h>
    #include <linux/netdevice.h>
    +#include <linux/inet_lro.h>

    #include "ixgbe_type.h"
    #include "ixgbe_common.h"
    @@ -100,6 +101,9 @@
    #define IXGBE_TX_FLAGS_VLAN_MASK 0xffff0000
    #define IXGBE_TX_FLAGS_VLAN_SHIFT 16

    +#define IXGBE_MAX_LRO_DESCRIPTORS 8
    +#define IXGBE_MAX_LRO_AGGREGATE 32
    +
    /* wrapper around a pointer to a socket buffer,
    * so a DMA handle can be stored along with the buffer */
    struct ixgbe_tx_buffer {
    @@ -150,6 +154,8 @@ struct ixgbe_ring {
    /* cpu for tx queue */
    int cpu;
    #endif
    + struct net_lro_mgr lro_mgr;
    + bool lro_used;
    struct ixgbe_queue_stats stats;
    u8 v_idx; /* maps directly to the index for this ring in the hardware
    * vector array, can also be used for finding the bit in EICR
    @@ -287,6 +293,9 @@ struct ixgbe_adapter {

    unsigned long state;
    u64 tx_busy;
    + u64 lro_aggregated;
    + u64 lro_flushed;
    + u64 lro_no_desc;
    };

    enum ixbge_state_t {
    diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
    index 4e46377..12990b1 100644
    --- a/drivers/net/ixgbe/ixgbe_ethtool.c
    +++ b/drivers/net/ixgbe/ixgbe_ethtool.c
    @@ -90,6 +90,8 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = {
    {"rx_header_split", IXGBE_STAT(rx_hdr_split)},
    {"alloc_rx_page_failed", IXGBE_STAT(alloc_rx_page_failed)},
    {"alloc_rx_buff_failed", IXGBE_STAT(alloc_rx_buff_failed)},
    + {"lro_aggregated", IXGBE_STAT(lro_aggregated)},
    + {"lro_flushed", IXGBE_STAT(lro_flushed)},
    };

    #define IXGBE_QUEUE_STATS_LEN \
    @@ -787,6 +789,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
    int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64);
    int j, k;
    int i;
    + u64 aggregated = 0, flushed = 0, no_desc = 0;

    ixgbe_update_stats(adapter);
    for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
    @@ -801,11 +804,17 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
    i += k;
    }
    for (j = 0; j < adapter->num_rx_queues; j++) {
    + aggregated += adapter->rx_ring[j].lro_mgr.stats.aggregated;
    + flushed += adapter->rx_ring[j].lro_mgr.stats.flushed;
    + no_desc += adapter->rx_ring[j].lro_mgr.stats.no_desc;
    queue_stat = (u64 *)&adapter->rx_ring[j].stats;
    for (k = 0; k < stat_count; k++)
    data[i + k] = queue_stat[k];
    i += k;
    }
    + adapter->lro_aggregated = aggregated;
    + adapter->lro_flushed = flushed;
    + adapter->lro_no_desc = no_desc;
    }

    static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
    @@ -973,6 +982,8 @@ static struct ethtool_ops ixgbe_ethtool_ops = {
    .get_ethtool_stats = ixgbe_get_ethtool_stats,
    .get_coalesce = ixgbe_get_coalesce,
    .set_coalesce = ixgbe_set_coalesce,
    + .get_flags = ethtool_op_get_flags,
    + .set_flags = ethtool_op_set_flags,
    };

    void ixgbe_set_ethtool_ops(struct net_device *netdev)
    diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
    index 7b85922..04c6482 100644
    --- a/drivers/net/ixgbe/ixgbe_main.c
    +++ b/drivers/net/ixgbe/ixgbe_main.c
    @@ -389,24 +389,39 @@ static int __ixgbe_notify_dca(struct device *dev, void *data)
    * ixgbe_receive_skb - Send a completed packet up the stack
    * @adapter: board private structure
    * @skb: packet to send up
    - * @is_vlan: packet has a VLAN tag
    - * @tag: VLAN tag from descriptor
    + * @status: hardware indication of status of receive
    + * @rx_ring: rx descriptor ring (for a specific queue) to setup
    + * @rx_desc: rx descriptor
    **/
    static void ixgbe_receive_skb(struct ixgbe_adapter *adapter,
    - struct sk_buff *skb, bool is_vlan,
    - u16 tag)
    + struct sk_buff *skb, u8 status,
    + struct ixgbe_ring *ring,
    + union ixgbe_adv_rx_desc *rx_desc)
    {
    - if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
    - if (adapter->vlgrp && is_vlan)
    - vlan_hwaccel_receive_skb(skb, adapter->vlgrp, tag);
    - else
    - netif_receive_skb(skb);
    - } else {
    + bool is_vlan = (status & IXGBE_RXD_STAT_VP);
    + u16 tag = le16_to_cpu(rx_desc->wb.upper.vlan);

    + if (adapter->netdev->features & NETIF_F_LRO &&
    + skb->ip_summed == CHECKSUM_UNNECESSARY) {
    if (adapter->vlgrp && is_vlan)
    - vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
    + lro_vlan_hwaccel_receive_skb(&ring->lro_mgr, skb,
    + adapter->vlgrp, tag,
    + rx_desc);
    else
    - netif_rx(skb);
    + lro_receive_skb(&ring->lro_mgr, skb, rx_desc);
    + ring->lro_used = true;
    + } else {
    + if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
    + if (adapter->vlgrp && is_vlan)
    + vlan_nw_accel_receive_skb(skb, adapter->vlgrp, tag);
    + else
    + netif_receive_skb(skb);
    + } else {
    + if (adapter->vlgrp && is_vlan)
    + vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
    + else
    + netif_rx(skb);
    + }
    }
    }

    @@ -546,8 +561,8 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
    struct sk_buff *skb;
    unsigned int i;
    u32 upper_len, len, staterr;
    - u16 hdr_info, vlan_tag;
    - bool is_vlan, cleaned = false;
    + u16 hdr_info;
    + bool cleaned = false;
    int cleaned_count = 0;
    unsigned int total_rx_bytes = 0, total_rx_packets = 0;

    @@ -556,8 +571,6 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
    rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
    staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
    rx_buffer_info = &rx_ring->rx_buffer_info[i];
    - is_vlan = (staterr & IXGBE_RXD_STAT_VP);
    - vlan_tag = le16_to_cpu(rx_desc->wb.upper.vlan);

    while (staterr & IXGBE_RXD_STAT_DD) {
    if (*work_done >= work_to_do)
    @@ -635,7 +648,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
    total_rx_packets++;

    skb->protocol = eth_type_trans(skb, netdev);
    - ixgbe_receive_skb(adapter, skb, is_vlan, vlan_tag);
    + ixgbe_receive_skb(adapter, skb, staterr, rx_ring, rx_desc);
    netdev->last_rx = jiffies;

    next_desc:
    @@ -652,8 +665,11 @@ next_desc:
    rx_buffer_info = next_buffer;

    staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
    - is_vlan = (staterr & IXGBE_RXD_STAT_VP);
    - vlan_tag = le16_to_cpu(rx_desc->wb.upper.vlan);
    + }
    +
    + if (rx_ring->lro_used) {
    + lro_flush_all(&rx_ring->lro_mgr);
    + rx_ring->lro_used = false;
    }

    rx_ring->next_to_clean = i;
    @@ -1382,6 +1398,33 @@ static void ixgbe_configure_tx(struct ixgbe_adapter *adapter)

    #define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2
    /**
    + * ixgbe_get_skb_hdr - helper function for LRO header processing
    + * @skb: pointer to sk_buff to be added to LRO packet
    + * @iphdr: pointer to tcp header structure
    + * @tcph: pointer to tcp header structure
    + * @hdr_flags: pointer to header flags
    + * @priv: private data
    + **/
    +static int ixgbe_get_skb_hdr(struct sk_buff *skb, void **iphdr, void **tcph,
    + u64 *hdr_flags, void *priv)
    +{
    + union ixgbe_adv_rx_desc *rx_desc = priv;
    +
    + /* Verify that this is a valid IPv4 TCP packet */
    + if (!(rx_desc->wb.lower.lo_dword.pkt_info &
    + (IXGBE_RXDADV_PKTTYPE_IPV4 | IXGBE_RXDADV_PKTTYPE_TCP)))
    + return -1;
    +
    + /* Set network headers */
    + skb_reset_network_header(skb);
    + skb_set_transport_header(skb, ip_hdrlen(skb));
    + *iphdr = ip_hdr(skb);
    + *tcph = tcp_hdr(skb);
    + *hdr_flags = LRO_IPV4 | LRO_TCP;
    + return 0;
    +}
    +
    +/**
    * ixgbe_configure_rx - Configure 8254x Receive Unit after Reset
    * @adapter: board private structure
    *
    @@ -1470,6 +1513,17 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
    adapter->rx_ring[i].tail = IXGBE_RDT(i);
    }

    + /* Intitial LRO Settings */
    + adapter->rx_ring[i].lro_mgr.max_aggr = IXGBE_MAX_LRO_AGGREGATE;
    + adapter->rx_ring[i].lro_mgr.max_desc = IXGBE_MAX_LRO_DESCRIPTORS;
    + adapter->rx_ring[i].lro_mgr.get_skb_header = ixgbe_get_skb_hdr;
    + adapter->rx_ring[i].lro_mgr.features = LRO_F_EXTRACT_VLAN_ID;
    + if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
    + adapter->rx_ring[i].lro_mgr.features |= LRO_F_NAPI;
    + adapter->rx_ring[i].lro_mgr.dev = adapter->netdev;
    + adapter->rx_ring[i].lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
    + adapter->rx_ring[i].lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
    +
    if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) {
    /* Fill out redirection table */
    for (i = 0, j = 0; i < 128; i++, j++) {
    @@ -2489,12 +2543,18 @@ int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
    struct pci_dev *pdev = adapter->pdev;
    int size;

    + size = sizeof(struct net_lro_desc) * IXGBE_MAX_LRO_DESCRIPTORS;
    + rxdr->lro_mgr.lro_arr = vmalloc(size);
    + if (!rxdr->lro_mgr.lro_arr)
    + return -ENOMEM;
    + memset(rxdr->lro_mgr.lro_arr, 0, size);
    +
    size = sizeof(struct ixgbe_rx_buffer) * rxdr->count;
    rxdr->rx_buffer_info = vmalloc(size);
    if (!rxdr->rx_buffer_info) {
    DPRINTK(PROBE, ERR,
    "vmalloc allocation failed for the rx desc ring\n");
    - return -ENOMEM;
    + goto alloc_failed;
    }
    memset(rxdr->rx_buffer_info, 0, size);

    @@ -2508,13 +2568,18 @@ int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
    DPRINTK(PROBE, ERR,
    "Memory allocation failed for the rx desc ring\n");
    vfree(rxdr->rx_buffer_info);
    - return -ENOMEM;
    + goto alloc_failed;
    }

    rxdr->next_to_clean = 0;
    rxdr->next_to_use = 0;

    return 0;
    +
    +alloc_failed:
    + vfree(rxdr->lro_mgr.lro_arr);
    + rxdr->lro_mgr.lro_arr = NULL;
    + return -ENOMEM;
    }

    /**
    @@ -2565,6 +2630,9 @@ static void ixgbe_free_rx_resources(struct ixgbe_adapter *adapter,
    {
    struct pci_dev *pdev = adapter->pdev;

    + vfree(rx_ring->lro_mgr.lro_arr);
    + rx_ring->lro_mgr.lro_arr = NULL;
    +
    ixgbe_clean_rx_ring(adapter, rx_ring);

    vfree(rx_ring->rx_buffer_info);
    @@ -3517,6 +3585,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
    NETIF_F_HW_VLAN_RX |
    NETIF_F_HW_VLAN_FILTER;

    + netdev->features |= NETIF_F_LRO;
    netdev->features |= NETIF_F_TSO;

    netdev->features |= NETIF_F_TSO6;


    \
     
     \ /
      Last update: 2008-06-13 18:33    [W:7.446 / U:0.024 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site