|
@@ -2141,41 +2141,72 @@ static int i40e_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
|
|
|
**/
|
|
|
static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd)
|
|
|
{
|
|
|
+ struct i40e_hw *hw = &pf->hw;
|
|
|
+ u8 flow_pctype = 0;
|
|
|
+ u64 i_set = 0;
|
|
|
+
|
|
|
cmd->data = 0;
|
|
|
|
|
|
- if (pf->vsi[pf->lan_vsi]->rxnfc.data != 0) {
|
|
|
- cmd->data = pf->vsi[pf->lan_vsi]->rxnfc.data;
|
|
|
- cmd->flow_type = pf->vsi[pf->lan_vsi]->rxnfc.flow_type;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- /* Report default options for RSS on i40e */
|
|
|
switch (cmd->flow_type) {
|
|
|
case TCP_V4_FLOW:
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
|
|
|
+ break;
|
|
|
case UDP_V4_FLOW:
|
|
|
- cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
|
- /* fall through to add IP fields */
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
|
|
|
+ break;
|
|
|
+ case TCP_V6_FLOW:
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
|
|
|
+ break;
|
|
|
+ case UDP_V6_FLOW:
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
|
|
|
+ break;
|
|
|
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:
|
|
|
- case UDP_V6_FLOW:
|
|
|
- cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
|
- /* fall through to add IP fields */
|
|
|
case SCTP_V6_FLOW:
|
|
|
case AH_ESP_V6_FLOW:
|
|
|
case AH_V6_FLOW:
|
|
|
case ESP_V6_FLOW:
|
|
|
case IPV6_FLOW:
|
|
|
+ /* Default is src/dest for IP, no matter the L4 hashing */
|
|
|
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
|
|
|
break;
|
|
|
default:
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ /* Read flow based hash input set register */
|
|
|
+ if (flow_pctype) {
|
|
|
+ i_set = (u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0,
|
|
|
+ flow_pctype)) |
|
|
|
+ ((u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1,
|
|
|
+ flow_pctype)) << 32);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Process bits of hash input set */
|
|
|
+ if (i_set) {
|
|
|
+ if (i_set & I40E_L4_SRC_MASK)
|
|
|
+ cmd->data |= RXH_L4_B_0_1;
|
|
|
+ if (i_set & I40E_L4_DST_MASK)
|
|
|
+ cmd->data |= RXH_L4_B_2_3;
|
|
|
+
|
|
|
+ if (cmd->flow_type == TCP_V4_FLOW ||
|
|
|
+ cmd->flow_type == UDP_V4_FLOW) {
|
|
|
+ if (i_set & I40E_L3_SRC_MASK)
|
|
|
+ cmd->data |= RXH_IP_SRC;
|
|
|
+ if (i_set & I40E_L3_DST_MASK)
|
|
|
+ cmd->data |= RXH_IP_DST;
|
|
|
+ } else if (cmd->flow_type == TCP_V6_FLOW ||
|
|
|
+ cmd->flow_type == UDP_V6_FLOW) {
|
|
|
+ if (i_set & I40E_L3_V6_SRC_MASK)
|
|
|
+ cmd->data |= RXH_IP_SRC;
|
|
|
+ if (i_set & I40E_L3_V6_DST_MASK)
|
|
|
+ cmd->data |= RXH_IP_DST;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2317,6 +2348,51 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * i40e_get_rss_hash_bits - Read RSS Hash bits from register
|
|
|
+ * @nfc: pointer to user request
|
|
|
+ * @i_setc bits currently set
|
|
|
+ *
|
|
|
+ * Returns value of bits to be set per user request
|
|
|
+ **/
|
|
|
+static u64 i40e_get_rss_hash_bits(struct ethtool_rxnfc *nfc, u64 i_setc)
|
|
|
+{
|
|
|
+ u64 i_set = i_setc;
|
|
|
+ u64 src_l3 = 0, dst_l3 = 0;
|
|
|
+
|
|
|
+ if (nfc->data & RXH_L4_B_0_1)
|
|
|
+ i_set |= I40E_L4_SRC_MASK;
|
|
|
+ else
|
|
|
+ i_set &= ~I40E_L4_SRC_MASK;
|
|
|
+ if (nfc->data & RXH_L4_B_2_3)
|
|
|
+ i_set |= I40E_L4_DST_MASK;
|
|
|
+ else
|
|
|
+ i_set &= ~I40E_L4_DST_MASK;
|
|
|
+
|
|
|
+ if (nfc->flow_type == TCP_V6_FLOW || nfc->flow_type == UDP_V6_FLOW) {
|
|
|
+ src_l3 = I40E_L3_V6_SRC_MASK;
|
|
|
+ dst_l3 = I40E_L3_V6_DST_MASK;
|
|
|
+ } else if (nfc->flow_type == TCP_V4_FLOW ||
|
|
|
+ nfc->flow_type == UDP_V4_FLOW) {
|
|
|
+ src_l3 = I40E_L3_SRC_MASK;
|
|
|
+ dst_l3 = I40E_L3_DST_MASK;
|
|
|
+ } else {
|
|
|
+ /* Any other flow type are not supported here */
|
|
|
+ return i_set;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nfc->data & RXH_IP_SRC)
|
|
|
+ i_set |= src_l3;
|
|
|
+ else
|
|
|
+ i_set &= ~src_l3;
|
|
|
+ if (nfc->data & RXH_IP_DST)
|
|
|
+ i_set |= dst_l3;
|
|
|
+ else
|
|
|
+ i_set &= ~dst_l3;
|
|
|
+
|
|
|
+ return i_set;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i40e_set_rss_hash_opt - Enable/Disable flow types for RSS hash
|
|
|
* @pf: pointer to the physical function struct
|
|
@@ -2329,6 +2405,8 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
|
|
|
struct i40e_hw *hw = &pf->hw;
|
|
|
u64 hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) |
|
|
|
((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32);
|
|
|
+ u8 flow_pctype = 0;
|
|
|
+ u64 i_set, i_setc;
|
|
|
|
|
|
/* RSS does not support anything other than hashing
|
|
|
* to queues on src and dst IPs and ports
|
|
@@ -2337,75 +2415,39 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
|
|
|
RXH_L4_B_0_1 | RXH_L4_B_2_3))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* We need at least the IP SRC and DEST fields for hashing */
|
|
|
- if (!(nfc->data & RXH_IP_SRC) ||
|
|
|
- !(nfc->data & RXH_IP_DST))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
switch (nfc->flow_type) {
|
|
|
case TCP_V4_FLOW:
|
|
|
- switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
|
- case 0:
|
|
|
- return -EINVAL;
|
|
|
- case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
|
- if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
- hena |=
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK);
|
|
|
-
|
|
|
- hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
|
|
|
+ if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
+ hena |=
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK);
|
|
|
break;
|
|
|
case TCP_V6_FLOW:
|
|
|
- switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
|
- case 0:
|
|
|
- return -EINVAL;
|
|
|
- case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
|
- if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
- hena |=
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK);
|
|
|
-
|
|
|
- hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
|
|
|
+ if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
+ hena |=
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK);
|
|
|
+ if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
+ hena |=
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK);
|
|
|
break;
|
|
|
case UDP_V4_FLOW:
|
|
|
- switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
|
- case 0:
|
|
|
- return -EINVAL;
|
|
|
- case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
|
- if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
- hena |=
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP);
|
|
|
-
|
|
|
- hena |= (BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_FRAG_IPV4));
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
|
|
|
+ if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
+ hena |=
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP);
|
|
|
+
|
|
|
+ hena |= BIT_ULL(I40E_FILTER_PCTYPE_FRAG_IPV4);
|
|
|
break;
|
|
|
case UDP_V6_FLOW:
|
|
|
- switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
|
- case 0:
|
|
|
- return -EINVAL;
|
|
|
- case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
|
- if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
- hena |=
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP);
|
|
|
-
|
|
|
- hena |= (BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
|
|
|
- BIT_ULL(I40E_FILTER_PCTYPE_FRAG_IPV6));
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
|
|
|
+ if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE)
|
|
|
+ hena |=
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
|
|
|
+ BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP);
|
|
|
+
|
|
|
+ hena |= BIT_ULL(I40E_FILTER_PCTYPE_FRAG_IPV6);
|
|
|
break;
|
|
|
case AH_ESP_V4_FLOW:
|
|
|
case AH_V4_FLOW:
|
|
@@ -2437,13 +2479,23 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ if (flow_pctype) {
|
|
|
+ i_setc = (u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0,
|
|
|
+ flow_pctype)) |
|
|
|
+ ((u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1,
|
|
|
+ flow_pctype)) << 32);
|
|
|
+ i_set = i40e_get_rss_hash_bits(nfc, i_setc);
|
|
|
+ i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, flow_pctype),
|
|
|
+ (u32)i_set);
|
|
|
+ i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, flow_pctype),
|
|
|
+ (u32)(i_set >> 32));
|
|
|
+ hena |= BIT_ULL(flow_pctype);
|
|
|
+ }
|
|
|
+
|
|
|
i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena);
|
|
|
i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32));
|
|
|
i40e_flush(hw);
|
|
|
|
|
|
- /* Save setting for future output/update */
|
|
|
- pf->vsi[pf->lan_vsi]->rxnfc = *nfc;
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2744,11 +2796,15 @@ static void i40e_get_channels(struct net_device *dev,
|
|
|
static int i40e_set_channels(struct net_device *dev,
|
|
|
struct ethtool_channels *ch)
|
|
|
{
|
|
|
+ const u8 drop = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET;
|
|
|
struct i40e_netdev_priv *np = netdev_priv(dev);
|
|
|
unsigned int count = ch->combined_count;
|
|
|
struct i40e_vsi *vsi = np->vsi;
|
|
|
struct i40e_pf *pf = vsi->back;
|
|
|
+ struct i40e_fdir_filter *rule;
|
|
|
+ struct hlist_node *node2;
|
|
|
int new_count;
|
|
|
+ int err = 0;
|
|
|
|
|
|
/* We do not support setting channels for any other VSI at present */
|
|
|
if (vsi->type != I40E_VSI_MAIN)
|
|
@@ -2766,6 +2822,26 @@ static int i40e_set_channels(struct net_device *dev,
|
|
|
if (count > i40e_max_channels(vsi))
|
|
|
return -EINVAL;
|
|
|
|
|
|
+ /* verify that the number of channels does not invalidate any current
|
|
|
+ * flow director rules
|
|
|
+ */
|
|
|
+ hlist_for_each_entry_safe(rule, node2,
|
|
|
+ &pf->fdir_filter_list, fdir_node) {
|
|
|
+ if (rule->dest_ctl != drop && count <= rule->q_index) {
|
|
|
+ dev_warn(&pf->pdev->dev,
|
|
|
+ "Existing user defined filter %d assigns flow to queue %d\n",
|
|
|
+ rule->fd_id, rule->q_index);
|
|
|
+ err = -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (err) {
|
|
|
+ dev_err(&pf->pdev->dev,
|
|
|
+ "Existing filter rules must be deleted to reduce combined channel count to %d\n",
|
|
|
+ count);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
/* update feature limits from largest to smallest supported values */
|
|
|
/* TODO: Flow director limit, DCB etc */
|
|
|
|