e1000e: add Receive Packet Steering (RPS) support
authorBruce Allan <bruce.w.allan@intel.com>
Wed, 11 Jan 2012 01:26:50 +0000 (01:26 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 26 Jan 2012 07:11:55 +0000 (23:11 -0800)
Enable RPS by default.  Disallow jumbo frames when both receive checksum
and receive hashing are enabled because the hardware cannot do both IP
payload checksum (enabled when receive checksum is enabled when using
packet split which is used for jumbo frames) and provide RSS hash at the
same time.

v2: added ethtool command to query flow hashing behavior per Ben Hutchings
    and changed the type of rsskey to cleanup the setting of the register
    array and avoid unnecessary casts (as pointed out by Joe Perches).
    The long error messages are not changed since there is nothing in
    the kernel ./Documentation that suggests the preferred method for
    dealing with long messages other than to never break strings; leaving
    them as-is for now.

Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/e1000e/defines.h
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/netdev.c

index c516a7440becb4eefa97a806a53c651d7673ec3d..c73795f46531aff0ea47ab0b8c75c21b0aa3f765 100644 (file)
     E1000_RXDEXT_STATERR_CXE |            \
     E1000_RXDEXT_STATERR_RXE)
 
+#define E1000_MRQC_RSS_FIELD_MASK              0xFFFF0000
+#define E1000_MRQC_RSS_FIELD_IPV4_TCP          0x00010000
+#define E1000_MRQC_RSS_FIELD_IPV4              0x00020000
+#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX       0x00040000
+#define E1000_MRQC_RSS_FIELD_IPV6              0x00100000
+#define E1000_MRQC_RSS_FIELD_IPV6_TCP          0x00200000
+
 #define E1000_RXDPS_HDRSTAT_HDRSP              0x00008000
 
 /* Management Control */
 /* Receive Checksum Control */
 #define E1000_RXCSUM_TUOFL     0x00000200   /* TCP / UDP checksum offload */
 #define E1000_RXCSUM_IPPCSE    0x00001000   /* IP payload checksum enable */
+#define E1000_RXCSUM_PCSD      0x00002000   /* packet checksum disabled */
 
 /* Header split receive */
 #define E1000_RFCTL_NFSW_DIS            0x00000040
index fb2c28e799a2d6ad80ef5eeb88d2fb6e058ec866..0a3137a791e9c5a57427b139c566c8dbf5fdc82f 100644 (file)
@@ -1955,6 +1955,53 @@ static void e1000_get_strings(struct net_device *netdev, u32 stringset,
        }
 }
 
+static int e1000_get_rxnfc(struct net_device *netdev,
+                          struct ethtool_rxnfc *info, u32 *rule_locs)
+{
+       info->data = 0;
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXFH: {
+               struct e1000_adapter *adapter = netdev_priv(netdev);
+               struct e1000_hw *hw = &adapter->hw;
+               u32 mrqc = er32(MRQC);
+
+               if (!(mrqc & E1000_MRQC_RSS_FIELD_MASK))
+                       return 0;
+
+               switch (info->flow_type) {
+               case TCP_V4_FLOW:
+                       if (mrqc & E1000_MRQC_RSS_FIELD_IPV4_TCP)
+                               info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       /* fall through */
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case AH_ESP_V4_FLOW:
+               case IPV4_FLOW:
+                       if (mrqc & E1000_MRQC_RSS_FIELD_IPV4)
+                               info->data |= RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case TCP_V6_FLOW:
+                       if (mrqc & E1000_MRQC_RSS_FIELD_IPV6_TCP)
+                               info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       /* fall through */
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case AH_ESP_V6_FLOW:
+               case IPV6_FLOW:
+                       if (mrqc & E1000_MRQC_RSS_FIELD_IPV6)
+                               info->data |= RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               default:
+                       break;
+               }
+               return 0;
+       }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static const struct ethtool_ops e1000_ethtool_ops = {
        .get_settings           = e1000_get_settings,
        .set_settings           = e1000_set_settings,
@@ -1981,6 +2028,7 @@ static const struct ethtool_ops e1000_ethtool_ops = {
        .get_sset_count         = e1000e_get_sset_count,
        .get_coalesce           = e1000_get_coalesce,
        .set_coalesce           = e1000_set_coalesce,
+       .get_rxnfc              = e1000_get_rxnfc,
 };
 
 void e1000e_set_ethtool_ops(struct net_device *netdev)
index 29670397079bfad1ec06ecb13b3de444f0bbc615..93c349ee56501fe7deb106e62f88308932b9e745 100644 (file)
@@ -204,6 +204,7 @@ enum e1e_registers {
        E1000_WUC      = 0x05800, /* Wakeup Control - RW */
        E1000_WUFC     = 0x05808, /* Wakeup Filter Control - RW */
        E1000_WUS      = 0x05810, /* Wakeup Status - RO */
+       E1000_MRQC     = 0x05818, /* Multiple Receive Control - RW */
        E1000_MANC     = 0x05820, /* Management Control - RW */
        E1000_FFLT     = 0x05F00, /* Flexible Filter Length Table - RW Array */
        E1000_HOST_IF  = 0x08800, /* Host Interface */
@@ -219,6 +220,10 @@ enum e1e_registers {
        E1000_SWSM      = 0x05B50, /* SW Semaphore */
        E1000_FWSM      = 0x05B54, /* FW Semaphore */
        E1000_SWSM2     = 0x05B58, /* Driver-only SW semaphore */
+       E1000_RETA_BASE = 0x05C00, /* Redirection Table - RW */
+#define E1000_RETA(_n) (E1000_RETA_BASE + ((_n) * 4))
+       E1000_RSSRK_BASE = 0x05C80, /* RSS Random Key - RW */
+#define E1000_RSSRK(_n)        (E1000_RSSRK_BASE + ((_n) * 4))
        E1000_FFLT_DBG  = 0x05F04, /* Debug Register */
        E1000_PCH_RAICC_BASE = 0x05F50, /* Receive Address Initial CRC */
 #define E1000_PCH_RAICC(_n)    (E1000_PCH_RAICC_BASE + ((_n) * 4))
index 263bf5f02eb1a770c68585750cada4880a91a760..b196d79e108dfce82aefc66f160b8552226cfc72 100644 (file)
@@ -845,6 +845,13 @@ check_page:
        }
 }
 
+static inline void e1000_rx_hash(struct net_device *netdev, __le32 rss,
+                                struct sk_buff *skb)
+{
+       if (netdev->features & NETIF_F_RXHASH)
+               skb->rxhash = le32_to_cpu(rss);
+}
+
 /**
  * e1000_clean_rx_irq - Send received data up the network stack; legacy
  * @adapter: board private structure
@@ -964,6 +971,8 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
                e1000_rx_checksum(adapter, staterr,
                                  rx_desc->wb.lower.hi_dword.csum_ip.csum, skb);
 
+               e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb);
+
                e1000_receive_skb(adapter, netdev, skb, staterr,
                                  rx_desc->wb.upper.vlan);
 
@@ -1325,6 +1334,8 @@ copydone:
                e1000_rx_checksum(adapter, staterr,
                                  rx_desc->wb.lower.hi_dword.csum_ip.csum, skb);
 
+               e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb);
+
                if (rx_desc->wb.upper.header_status &
                           cpu_to_le16(E1000_RXDPS_HDRSTAT_HDRSP))
                        adapter->rx_hdr_split++;
@@ -1497,6 +1508,8 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
                e1000_rx_checksum(adapter, staterr,
                                  rx_desc->wb.lower.hi_dword.csum_ip.csum, skb);
 
+               e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb);
+
                /* probably a little skewed due to removing CRC */
                total_rx_bytes += skb->len;
                total_rx_packets++;
@@ -3271,6 +3284,42 @@ static void e1000e_set_rx_mode(struct net_device *netdev)
                e1000e_vlan_strip_disable(adapter);
 }
 
+static void e1000e_setup_rss_hash(struct e1000_adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       u32 mrqc, rxcsum;
+       int i;
+       static const u32 rsskey[10] = {
+               0xda565a6d, 0xc20e5b25, 0x3d256741, 0xb08fa343, 0xcb2bcad0,
+               0xb4307bae, 0xa32dcb77, 0x0cf23080, 0x3bb7426a, 0xfa01acbe
+       };
+
+       /* Fill out hash function seed */
+       for (i = 0; i < 10; i++)
+               ew32(RSSRK(i), rsskey[i]);
+
+       /* Direct all traffic to queue 0 */
+       for (i = 0; i < 32; i++)
+               ew32(RETA(i), 0);
+
+       /*
+        * Disable raw packet checksumming so that RSS hash is placed in
+        * descriptor on writeback.
+        */
+       rxcsum = er32(RXCSUM);
+       rxcsum |= E1000_RXCSUM_PCSD;
+
+       ew32(RXCSUM, rxcsum);
+
+       mrqc = (E1000_MRQC_RSS_FIELD_IPV4 |
+               E1000_MRQC_RSS_FIELD_IPV4_TCP |
+               E1000_MRQC_RSS_FIELD_IPV6 |
+               E1000_MRQC_RSS_FIELD_IPV6_TCP |
+               E1000_MRQC_RSS_FIELD_IPV6_TCP_EX);
+
+       ew32(MRQC, mrqc);
+}
+
 /**
  * e1000_configure - configure the hardware for Rx and Tx
  * @adapter: private board structure
@@ -3283,6 +3332,9 @@ static void e1000_configure(struct e1000_adapter *adapter)
        e1000_init_manageability_pt(adapter);
 
        e1000_configure_tx(adapter);
+
+       if (adapter->netdev->features & NETIF_F_RXHASH)
+               e1000e_setup_rss_hash(adapter);
        e1000_setup_rctl(adapter);
        e1000_configure_rx(adapter);
        adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring),
@@ -5168,10 +5220,22 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
        int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
 
        /* Jumbo frame support */
-       if ((max_frame > ETH_FRAME_LEN + ETH_FCS_LEN) &&
-           !(adapter->flags & FLAG_HAS_JUMBO_FRAMES)) {
-               e_err("Jumbo Frames not supported.\n");
-               return -EINVAL;
+       if (max_frame > ETH_FRAME_LEN + ETH_FCS_LEN) {
+               if (!(adapter->flags & FLAG_HAS_JUMBO_FRAMES)) {
+                       e_err("Jumbo Frames not supported.\n");
+                       return -EINVAL;
+               }
+
+               /*
+                * IP payload checksum (enabled with jumbos/packet-split when
+                * Rx checksum is enabled) and generation of RSS hash is
+                * mutually exclusive in the hardware.
+                */
+               if ((netdev->features & NETIF_F_RXCSUM) &&
+                   (netdev->features & NETIF_F_RXHASH)) {
+                       e_err("Jumbo frames cannot be enabled when both receive checksum offload and receive hashing are enabled.  Disable one of the receive offload features before enabling jumbos.\n");
+                       return -EINVAL;
+               }
        }
 
        /* Supported frame sizes */
@@ -5934,7 +5998,7 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter)
 }
 
 static int e1000_set_features(struct net_device *netdev,
-       netdev_features_t features)
+                             netdev_features_t features)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;
@@ -5943,9 +6007,22 @@ static int e1000_set_features(struct net_device *netdev,
                adapter->flags |= FLAG_TSO_FORCE;
 
        if (!(changed & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX |
-                        NETIF_F_RXCSUM)))
+                        NETIF_F_RXCSUM | NETIF_F_RXHASH)))
                return 0;
 
+       /*
+        * IP payload checksum (enabled with jumbos/packet-split when Rx
+        * checksum is enabled) and generation of RSS hash is mutually
+        * exclusive in the hardware.
+        */
+       if (adapter->rx_ps_pages &&
+           (features & NETIF_F_RXCSUM) && (features & NETIF_F_RXHASH)) {
+               e_err("Enabling both receive checksum offload and receive hashing is not possible with jumbo frames.  Disable jumbos or enable only one of the receive offload features.\n");
+               return -EINVAL;
+       }
+
+       netdev->features = features;
+
        if (netif_running(netdev))
                e1000e_reinit_locked(adapter);
        else
@@ -6136,6 +6213,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
                            NETIF_F_HW_VLAN_TX |
                            NETIF_F_TSO |
                            NETIF_F_TSO6 |
+                           NETIF_F_RXHASH |
                            NETIF_F_RXCSUM |
                            NETIF_F_HW_CSUM);