Commit 039454a8 authored by Akeem G. Abodunrin's avatar Akeem G. Abodunrin Committed by David S. Miller

igb: Support for modifying UDP RSS flow hashing

This patch provides ability to enable or disable UDP RSS hashing. It gives
users option of generating RSS hash based on the UDP source and destination
ports numbers. Currently, UDP flow hash is always disabled in igb-driver.
Signed-off-by: default avatarAkeem G Abodunrin <akeem.g.abodunrin@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 867eb39e
...@@ -386,12 +386,14 @@ struct igb_adapter { ...@@ -386,12 +386,14 @@ struct igb_adapter {
char fw_version[32]; char fw_version[32];
}; };
#define IGB_FLAG_HAS_MSI (1 << 0) #define IGB_FLAG_HAS_MSI (1 << 0)
#define IGB_FLAG_DCA_ENABLED (1 << 1) #define IGB_FLAG_DCA_ENABLED (1 << 1)
#define IGB_FLAG_QUAD_PORT_A (1 << 2) #define IGB_FLAG_QUAD_PORT_A (1 << 2)
#define IGB_FLAG_QUEUE_PAIRS (1 << 3) #define IGB_FLAG_QUEUE_PAIRS (1 << 3)
#define IGB_FLAG_DMAC (1 << 4) #define IGB_FLAG_DMAC (1 << 4)
#define IGB_FLAG_PTP (1 << 5) #define IGB_FLAG_PTP (1 << 5)
#define IGB_FLAG_RSS_FIELD_IPV4_UDP (1 << 6)
#define IGB_FLAG_RSS_FIELD_IPV6_UDP (1 << 7)
/* DMA Coalescing defines */ /* DMA Coalescing defines */
#define IGB_MIN_TXPBSIZE 20408 #define IGB_MIN_TXPBSIZE 20408
......
...@@ -2350,6 +2350,185 @@ static int igb_get_ts_info(struct net_device *dev, ...@@ -2350,6 +2350,185 @@ static int igb_get_ts_info(struct net_device *dev,
} }
} }
static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd)
{
cmd->data = 0;
/* Report default options for RSS on igb */
switch (cmd->flow_type) {
case TCP_V4_FLOW:
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
case UDP_V4_FLOW:
if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV4_UDP)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
case SCTP_V4_FLOW:
case AH_ESP_V4_FLOW:
case AH_V4_FLOW:
case ESP_V4_FLOW:
case IPV4_FLOW:
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
break;
case TCP_V6_FLOW:
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
case UDP_V6_FLOW:
if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV6_UDP)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
case SCTP_V6_FLOW:
case AH_ESP_V6_FLOW:
case AH_V6_FLOW:
case ESP_V6_FLOW:
case IPV6_FLOW:
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
break;
default:
return -EINVAL;
}
return 0;
}
static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct igb_adapter *adapter = netdev_priv(dev);
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
cmd->data = adapter->num_rx_queues;
ret = 0;
break;
case ETHTOOL_GRXFH:
ret = igb_get_rss_hash_opts(adapter, cmd);
break;
default:
break;
}
return ret;
}
#define UDP_RSS_FLAGS (IGB_FLAG_RSS_FIELD_IPV4_UDP | \
IGB_FLAG_RSS_FIELD_IPV6_UDP)
static int igb_set_rss_hash_opt(struct igb_adapter *adapter,
struct ethtool_rxnfc *nfc)
{
u32 flags = adapter->flags;
/* RSS does not support anything other than hashing
* to queues on src and dst IPs and ports
*/
if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3))
return -EINVAL;
switch (nfc->flow_type) {
case TCP_V4_FLOW:
case TCP_V6_FLOW:
if (!(nfc->data & RXH_IP_SRC) ||
!(nfc->data & RXH_IP_DST) ||
!(nfc->data & RXH_L4_B_0_1) ||
!(nfc->data & RXH_L4_B_2_3))
return -EINVAL;
break;
case UDP_V4_FLOW:
if (!(nfc->data & RXH_IP_SRC) ||
!(nfc->data & RXH_IP_DST))
return -EINVAL;
switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
case 0:
flags &= ~IGB_FLAG_RSS_FIELD_IPV4_UDP;
break;
case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
flags |= IGB_FLAG_RSS_FIELD_IPV4_UDP;
break;
default:
return -EINVAL;
}
break;
case UDP_V6_FLOW:
if (!(nfc->data & RXH_IP_SRC) ||
!(nfc->data & RXH_IP_DST))
return -EINVAL;
switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
case 0:
flags &= ~IGB_FLAG_RSS_FIELD_IPV6_UDP;
break;
case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
flags |= IGB_FLAG_RSS_FIELD_IPV6_UDP;
break;
default:
return -EINVAL;
}
break;
case AH_ESP_V4_FLOW:
case AH_V4_FLOW:
case ESP_V4_FLOW:
case SCTP_V4_FLOW:
case AH_ESP_V6_FLOW:
case AH_V6_FLOW:
case ESP_V6_FLOW:
case SCTP_V6_FLOW:
if (!(nfc->data & RXH_IP_SRC) ||
!(nfc->data & RXH_IP_DST) ||
(nfc->data & RXH_L4_B_0_1) ||
(nfc->data & RXH_L4_B_2_3))
return -EINVAL;
break;
default:
return -EINVAL;
}
/* if we changed something we need to update flags */
if (flags != adapter->flags) {
struct e1000_hw *hw = &adapter->hw;
u32 mrqc = rd32(E1000_MRQC);
if ((flags & UDP_RSS_FLAGS) &&
!(adapter->flags & UDP_RSS_FLAGS))
dev_err(&adapter->pdev->dev,
"enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
adapter->flags = flags;
/* Perform hash on these packet types */
mrqc |= E1000_MRQC_RSS_FIELD_IPV4 |
E1000_MRQC_RSS_FIELD_IPV4_TCP |
E1000_MRQC_RSS_FIELD_IPV6 |
E1000_MRQC_RSS_FIELD_IPV6_TCP;
mrqc &= ~(E1000_MRQC_RSS_FIELD_IPV4_UDP |
E1000_MRQC_RSS_FIELD_IPV6_UDP);
if (flags & IGB_FLAG_RSS_FIELD_IPV4_UDP)
mrqc |= E1000_MRQC_RSS_FIELD_IPV4_UDP;
if (flags & IGB_FLAG_RSS_FIELD_IPV6_UDP)
mrqc |= E1000_MRQC_RSS_FIELD_IPV6_UDP;
wr32(E1000_MRQC, mrqc);
}
return 0;
}
static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
struct igb_adapter *adapter = netdev_priv(dev);
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
case ETHTOOL_SRXFH:
ret = igb_set_rss_hash_opt(adapter, cmd);
break;
default:
break;
}
return ret;
}
static int igb_ethtool_begin(struct net_device *netdev) static int igb_ethtool_begin(struct net_device *netdev)
{ {
struct igb_adapter *adapter = netdev_priv(netdev); struct igb_adapter *adapter = netdev_priv(netdev);
...@@ -2390,6 +2569,8 @@ static const struct ethtool_ops igb_ethtool_ops = { ...@@ -2390,6 +2569,8 @@ static const struct ethtool_ops igb_ethtool_ops = {
.get_coalesce = igb_get_coalesce, .get_coalesce = igb_get_coalesce,
.set_coalesce = igb_set_coalesce, .set_coalesce = igb_set_coalesce,
.get_ts_info = igb_get_ts_info, .get_ts_info = igb_get_ts_info,
.get_rxnfc = igb_get_rxnfc,
.set_rxnfc = igb_set_rxnfc,
.begin = igb_ethtool_begin, .begin = igb_ethtool_begin,
.complete = igb_ethtool_complete, .complete = igb_ethtool_complete,
}; };
......
...@@ -2874,18 +2874,21 @@ static void igb_setup_mrqc(struct igb_adapter *adapter) ...@@ -2874,18 +2874,21 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
/* Don't need to set TUOFL or IPOFL, they default to 1 */ /* Don't need to set TUOFL or IPOFL, they default to 1 */
wr32(E1000_RXCSUM, rxcsum); wr32(E1000_RXCSUM, rxcsum);
/*
* Generate RSS hash based on TCP port numbers and/or
* IPv4/v6 src and dst addresses since UDP cannot be
* hashed reliably due to IP fragmentation
*/
/* Generate RSS hash based on packet types, TCP/UDP
* port numbers and/or IPv4/v6 src and dst addresses
*/
mrqc = E1000_MRQC_RSS_FIELD_IPV4 | mrqc = E1000_MRQC_RSS_FIELD_IPV4 |
E1000_MRQC_RSS_FIELD_IPV4_TCP | E1000_MRQC_RSS_FIELD_IPV4_TCP |
E1000_MRQC_RSS_FIELD_IPV6 | E1000_MRQC_RSS_FIELD_IPV6 |
E1000_MRQC_RSS_FIELD_IPV6_TCP | E1000_MRQC_RSS_FIELD_IPV6_TCP |
E1000_MRQC_RSS_FIELD_IPV6_TCP_EX; E1000_MRQC_RSS_FIELD_IPV6_TCP_EX;
if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV4_UDP)
mrqc |= E1000_MRQC_RSS_FIELD_IPV4_UDP;
if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV6_UDP)
mrqc |= E1000_MRQC_RSS_FIELD_IPV6_UDP;
/* If VMDq is enabled then we set the appropriate mode for that, else /* If VMDq is enabled then we set the appropriate mode for that, else
* we default to RSS so that an RSS hash is calculated per packet even * we default to RSS so that an RSS hash is calculated per packet even
* if we are only using one queue */ * if we are only using one queue */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment