Commit 0e71def2 authored by Gangfeng Huang's avatar Gangfeng Huang Committed by Jeff Kirsher

igb: add support of RX network flow classification

This patch is meant to allow for RX network flow classification to insert
and remove Rx filter by ethtool. Ethtool interface has it's own rules
manager

Show all filters:
$ ethtool -n eth0
4 RX rings available
Total 2 rules
Signed-off-by: default avatarRuhao Gao <ruhao.gao@ni.com>
Signed-off-by: default avatarGangfeng Huang <gangfeng.huang@ni.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent e951f145
...@@ -355,6 +355,27 @@ struct hwmon_buff { ...@@ -355,6 +355,27 @@ struct hwmon_buff {
#define IGB_N_SDP 4 #define IGB_N_SDP 4
#define IGB_RETA_SIZE 128 #define IGB_RETA_SIZE 128
enum igb_filter_match_flags {
IGB_FILTER_FLAG_NONE = 0x0,
};
#define IGB_MAX_RXNFC_FILTERS 16
/* RX network flow classification data structure */
struct igb_nfc_input {
/* Byte layout in order, all values with MSB first:
* match_flags - 1 byte
*/
u8 match_flags;
};
struct igb_nfc_filter {
struct hlist_node nfc_node;
struct igb_nfc_input filter;
u16 sw_idx;
u16 action;
};
/* board specific private data structure */ /* board specific private data structure */
struct igb_adapter { struct igb_adapter {
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
...@@ -473,6 +494,12 @@ struct igb_adapter { ...@@ -473,6 +494,12 @@ struct igb_adapter {
int copper_tries; int copper_tries;
struct e1000_info ei; struct e1000_info ei;
u16 eee_advert; u16 eee_advert;
/* RX network flow classification support */
struct hlist_head nfc_filter_list;
unsigned int nfc_filter_count;
/* lock for RX network flow classification filter */
spinlock_t nfc_lock;
}; };
/* flags controlling PTP/1588 function */ /* flags controlling PTP/1588 function */
...@@ -599,4 +626,9 @@ static inline struct netdev_queue *txring_txq(const struct igb_ring *tx_ring) ...@@ -599,4 +626,9 @@ static inline struct netdev_queue *txring_txq(const struct igb_ring *tx_ring)
return netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index); return netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index);
} }
int igb_add_filter(struct igb_adapter *adapter,
struct igb_nfc_filter *input);
int igb_erase_filter(struct igb_adapter *adapter,
struct igb_nfc_filter *input);
#endif /* _IGB_H_ */ #endif /* _IGB_H_ */
...@@ -2431,6 +2431,48 @@ static int igb_get_ts_info(struct net_device *dev, ...@@ -2431,6 +2431,48 @@ static int igb_get_ts_info(struct net_device *dev,
} }
} }
static int igb_get_ethtool_nfc_entry(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp = &cmd->fs;
struct igb_nfc_filter *rule = NULL;
/* report total rule count */
cmd->data = IGB_MAX_RXNFC_FILTERS;
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
if (fsp->location <= rule->sw_idx)
break;
}
if (!rule || fsp->location != rule->sw_idx)
return -EINVAL;
return -EINVAL;
}
static int igb_get_ethtool_nfc_all(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct igb_nfc_filter *rule;
int cnt = 0;
/* report total rule count */
cmd->data = IGB_MAX_RXNFC_FILTERS;
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
if (cnt == cmd->rule_cnt)
return -EMSGSIZE;
rule_locs[cnt] = rule->sw_idx;
cnt++;
}
cmd->rule_cnt = cnt;
return 0;
}
static int igb_get_rss_hash_opts(struct igb_adapter *adapter, static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd) struct ethtool_rxnfc *cmd)
{ {
...@@ -2484,6 +2526,16 @@ static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, ...@@ -2484,6 +2526,16 @@ static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
cmd->data = adapter->num_rx_queues; cmd->data = adapter->num_rx_queues;
ret = 0; ret = 0;
break; break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = adapter->nfc_filter_count;
ret = 0;
break;
case ETHTOOL_GRXCLSRULE:
ret = igb_get_ethtool_nfc_entry(adapter, cmd);
break;
case ETHTOOL_GRXCLSRLALL:
ret = igb_get_ethtool_nfc_all(adapter, cmd, rule_locs);
break;
case ETHTOOL_GRXFH: case ETHTOOL_GRXFH:
ret = igb_get_rss_hash_opts(adapter, cmd); ret = igb_get_rss_hash_opts(adapter, cmd);
break; break;
...@@ -2598,6 +2650,142 @@ static int igb_set_rss_hash_opt(struct igb_adapter *adapter, ...@@ -2598,6 +2650,142 @@ static int igb_set_rss_hash_opt(struct igb_adapter *adapter,
return 0; return 0;
} }
int igb_add_filter(struct igb_adapter *adapter, struct igb_nfc_filter *input)
{
return -EINVAL;
}
int igb_erase_filter(struct igb_adapter *adapter, struct igb_nfc_filter *input)
{
return 0;
}
static int igb_update_ethtool_nfc_entry(struct igb_adapter *adapter,
struct igb_nfc_filter *input,
u16 sw_idx)
{
struct igb_nfc_filter *rule, *parent;
int err = -EINVAL;
parent = NULL;
rule = NULL;
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
/* hash found, or no matching entry */
if (rule->sw_idx >= sw_idx)
break;
parent = rule;
}
/* if there is an old rule occupying our place remove it */
if (rule && (rule->sw_idx == sw_idx)) {
if (!input)
err = igb_erase_filter(adapter, rule);
hlist_del(&rule->nfc_node);
kfree(rule);
adapter->nfc_filter_count--;
}
/* If no input this was a delete, err should be 0 if a rule was
* successfully found and removed from the list else -EINVAL
*/
if (!input)
return err;
/* initialize node */
INIT_HLIST_NODE(&input->nfc_node);
/* add filter to the list */
if (parent)
hlist_add_behind(&parent->nfc_node, &input->nfc_node);
else
hlist_add_head(&input->nfc_node, &adapter->nfc_filter_list);
/* update counts */
adapter->nfc_filter_count++;
return 0;
}
static int igb_add_ethtool_nfc_entry(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd)
{
struct net_device *netdev = adapter->netdev;
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
struct igb_nfc_filter *input, *rule;
int err = 0;
if (!(netdev->hw_features & NETIF_F_NTUPLE))
return -ENOTSUPP;
/* Don't allow programming if the action is a queue greater than
* the number of online Rx queues.
*/
if ((fsp->ring_cookie == RX_CLS_FLOW_DISC) ||
(fsp->ring_cookie >= adapter->num_rx_queues)) {
dev_err(&adapter->pdev->dev, "ethtool -N: The specified action is invalid\n");
return -EINVAL;
}
/* Don't allow indexes to exist outside of available space */
if (fsp->location >= IGB_MAX_RXNFC_FILTERS) {
dev_err(&adapter->pdev->dev, "Location out of range\n");
return -EINVAL;
}
if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW)
return -EINVAL;
input = kzalloc(sizeof(*input), GFP_KERNEL);
if (!input)
return -ENOMEM;
input->action = fsp->ring_cookie;
input->sw_idx = fsp->location;
spin_lock(&adapter->nfc_lock);
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
if (!memcmp(&input->filter, &rule->filter,
sizeof(input->filter))) {
err = -EEXIST;
dev_err(&adapter->pdev->dev,
"ethtool: this filter is already set\n");
goto err_out_w_lock;
}
}
err = igb_add_filter(adapter, input);
if (err)
goto err_out_w_lock;
igb_update_ethtool_nfc_entry(adapter, input, input->sw_idx);
spin_unlock(&adapter->nfc_lock);
return 0;
err_out_w_lock:
spin_unlock(&adapter->nfc_lock);
kfree(input);
return err;
}
static int igb_del_ethtool_nfc_entry(struct igb_adapter *adapter,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
int err;
spin_lock(&adapter->nfc_lock);
err = igb_update_ethtool_nfc_entry(adapter, NULL, fsp->location);
spin_unlock(&adapter->nfc_lock);
return err;
}
static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{ {
struct igb_adapter *adapter = netdev_priv(dev); struct igb_adapter *adapter = netdev_priv(dev);
...@@ -2607,6 +2795,11 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) ...@@ -2607,6 +2795,11 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
case ETHTOOL_SRXFH: case ETHTOOL_SRXFH:
ret = igb_set_rss_hash_opt(adapter, cmd); ret = igb_set_rss_hash_opt(adapter, cmd);
break; break;
case ETHTOOL_SRXCLSRLINS:
ret = igb_add_ethtool_nfc_entry(adapter, cmd);
break;
case ETHTOOL_SRXCLSRLDEL:
ret = igb_del_ethtool_nfc_entry(adapter, cmd);
default: default:
break; break;
} }
......
...@@ -176,6 +176,8 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, ...@@ -176,6 +176,8 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
static int igb_ndo_get_vf_config(struct net_device *netdev, int vf, static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
struct ifla_vf_info *ivi); struct ifla_vf_info *ivi);
static void igb_check_vf_rate_limit(struct igb_adapter *); static void igb_check_vf_rate_limit(struct igb_adapter *);
static void igb_nfc_filter_exit(struct igb_adapter *adapter);
static void igb_nfc_filter_restore(struct igb_adapter *adapter);
#ifdef CONFIG_PCI_IOV #ifdef CONFIG_PCI_IOV
static int igb_vf_configure(struct igb_adapter *adapter, int vf); static int igb_vf_configure(struct igb_adapter *adapter, int vf);
...@@ -1611,6 +1613,7 @@ static void igb_configure(struct igb_adapter *adapter) ...@@ -1611,6 +1613,7 @@ static void igb_configure(struct igb_adapter *adapter)
igb_setup_mrqc(adapter); igb_setup_mrqc(adapter);
igb_setup_rctl(adapter); igb_setup_rctl(adapter);
igb_nfc_filter_restore(adapter);
igb_configure_tx(adapter); igb_configure_tx(adapter);
igb_configure_rx(adapter); igb_configure_rx(adapter);
...@@ -2059,6 +2062,21 @@ static int igb_set_features(struct net_device *netdev, ...@@ -2059,6 +2062,21 @@ static int igb_set_features(struct net_device *netdev,
if (!(changed & (NETIF_F_RXALL | NETIF_F_NTUPLE))) if (!(changed & (NETIF_F_RXALL | NETIF_F_NTUPLE)))
return 0; return 0;
if (!(features & NETIF_F_NTUPLE)) {
struct hlist_node *node2;
struct igb_nfc_filter *rule;
spin_lock(&adapter->nfc_lock);
hlist_for_each_entry_safe(rule, node2,
&adapter->nfc_filter_list, nfc_node) {
igb_erase_filter(adapter, rule);
hlist_del(&rule->nfc_node);
kfree(rule);
}
spin_unlock(&adapter->nfc_lock);
adapter->nfc_filter_count = 0;
}
netdev->features = features; netdev->features = features;
if (netif_running(netdev)) if (netif_running(netdev))
...@@ -3053,6 +3071,7 @@ static int igb_sw_init(struct igb_adapter *adapter) ...@@ -3053,6 +3071,7 @@ static int igb_sw_init(struct igb_adapter *adapter)
VLAN_HLEN; VLAN_HLEN;
adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
spin_lock_init(&adapter->nfc_lock);
spin_lock_init(&adapter->stats64_lock); spin_lock_init(&adapter->stats64_lock);
#ifdef CONFIG_PCI_IOV #ifdef CONFIG_PCI_IOV
switch (hw->mac.type) { switch (hw->mac.type) {
...@@ -3240,6 +3259,8 @@ static int __igb_close(struct net_device *netdev, bool suspending) ...@@ -3240,6 +3259,8 @@ static int __igb_close(struct net_device *netdev, bool suspending)
igb_down(adapter); igb_down(adapter);
igb_free_irq(adapter); igb_free_irq(adapter);
igb_nfc_filter_exit(adapter);
igb_free_all_tx_resources(adapter); igb_free_all_tx_resources(adapter);
igb_free_all_rx_resources(adapter); igb_free_all_rx_resources(adapter);
...@@ -8306,4 +8327,28 @@ int igb_reinit_queues(struct igb_adapter *adapter) ...@@ -8306,4 +8327,28 @@ int igb_reinit_queues(struct igb_adapter *adapter)
return err; return err;
} }
static void igb_nfc_filter_exit(struct igb_adapter *adapter)
{
struct igb_nfc_filter *rule;
spin_lock(&adapter->nfc_lock);
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
igb_erase_filter(adapter, rule);
spin_unlock(&adapter->nfc_lock);
}
static void igb_nfc_filter_restore(struct igb_adapter *adapter)
{
struct igb_nfc_filter *rule;
spin_lock(&adapter->nfc_lock);
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
igb_add_filter(adapter, rule);
spin_unlock(&adapter->nfc_lock);
}
/* igb_main.c */ /* igb_main.c */
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