|
@@ -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,
|
|
|
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;
|
|
|
ret = 0;
|
|
|
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:
|
|
|
ret = igb_get_rss_hash_opts(adapter, cmd);
|
|
|
break;
|
|
@@ -2598,6 +2650,142 @@ static int igb_set_rss_hash_opt(struct igb_adapter *adapter,
|
|
|
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)
|
|
|
{
|
|
|
struct igb_adapter *adapter = netdev_priv(dev);
|
|
@@ -2607,6 +2795,11 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
|
|
case ETHTOOL_SRXFH:
|
|
|
ret = igb_set_rss_hash_opt(adapter, cmd);
|
|
|
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:
|
|
|
break;
|
|
|
}
|