|
@@ -36,6 +36,7 @@
|
|
|
#include <net/checksum.h>
|
|
|
#include <net/ip6_checksum.h>
|
|
|
#include <net/pkt_sched.h>
|
|
|
+#include <net/pkt_cls.h>
|
|
|
#include <linux/net_tstamp.h>
|
|
|
#include <linux/mii.h>
|
|
|
#include <linux/ethtool.h>
|
|
@@ -2513,6 +2514,250 @@ static int igb_offload_cbs(struct igb_adapter *adapter,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
|
|
|
+#define VLAN_PRIO_FULL_MASK (0x07)
|
|
|
+
|
|
|
+static int igb_parse_cls_flower(struct igb_adapter *adapter,
|
|
|
+ struct tc_cls_flower_offload *f,
|
|
|
+ int traffic_class,
|
|
|
+ struct igb_nfc_filter *input)
|
|
|
+{
|
|
|
+ struct netlink_ext_ack *extack = f->common.extack;
|
|
|
+
|
|
|
+ if (f->dissector->used_keys &
|
|
|
+ ~(BIT(FLOW_DISSECTOR_KEY_BASIC) |
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_CONTROL) |
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_VLAN))) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack,
|
|
|
+ "Unsupported key used, only BASIC, CONTROL, ETH_ADDRS and VLAN are supported");
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
|
|
+ struct flow_dissector_key_eth_addrs *key, *mask;
|
|
|
+
|
|
|
+ key = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
|
|
|
+ f->key);
|
|
|
+ mask = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
|
|
|
+ f->mask);
|
|
|
+
|
|
|
+ if (!is_zero_ether_addr(mask->dst)) {
|
|
|
+ if (!is_broadcast_ether_addr(mask->dst)) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for destination MAC address");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ input->filter.match_flags |=
|
|
|
+ IGB_FILTER_FLAG_DST_MAC_ADDR;
|
|
|
+ ether_addr_copy(input->filter.dst_addr, key->dst);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!is_zero_ether_addr(mask->src)) {
|
|
|
+ if (!is_broadcast_ether_addr(mask->src)) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for source MAC address");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ input->filter.match_flags |=
|
|
|
+ IGB_FILTER_FLAG_SRC_MAC_ADDR;
|
|
|
+ ether_addr_copy(input->filter.src_addr, key->src);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
|
|
|
+ struct flow_dissector_key_basic *key, *mask;
|
|
|
+
|
|
|
+ key = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ f->key);
|
|
|
+ mask = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
+ f->mask);
|
|
|
+
|
|
|
+ if (mask->n_proto) {
|
|
|
+ if (mask->n_proto != ETHER_TYPE_FULL_MASK) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for EtherType filter");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ input->filter.match_flags |= IGB_FILTER_FLAG_ETHER_TYPE;
|
|
|
+ input->filter.etype = key->n_proto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
|
|
|
+ struct flow_dissector_key_vlan *key, *mask;
|
|
|
+
|
|
|
+ key = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_VLAN,
|
|
|
+ f->key);
|
|
|
+ mask = skb_flow_dissector_target(f->dissector,
|
|
|
+ FLOW_DISSECTOR_KEY_VLAN,
|
|
|
+ f->mask);
|
|
|
+
|
|
|
+ if (mask->vlan_priority) {
|
|
|
+ if (mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ input->filter.match_flags |= IGB_FILTER_FLAG_VLAN_TCI;
|
|
|
+ input->filter.vlan_tci = key->vlan_priority;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ input->action = traffic_class;
|
|
|
+ input->cookie = f->cookie;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int igb_configure_clsflower(struct igb_adapter *adapter,
|
|
|
+ struct tc_cls_flower_offload *cls_flower)
|
|
|
+{
|
|
|
+ struct netlink_ext_ack *extack = cls_flower->common.extack;
|
|
|
+ struct igb_nfc_filter *filter, *f;
|
|
|
+ int err, tc;
|
|
|
+
|
|
|
+ tc = tc_classid_to_hwtc(adapter->netdev, cls_flower->classid);
|
|
|
+ if (tc < 0) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Invalid traffic class");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
|
|
+ if (!filter)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ err = igb_parse_cls_flower(adapter, cls_flower, tc, filter);
|
|
|
+ if (err < 0)
|
|
|
+ goto err_parse;
|
|
|
+
|
|
|
+ spin_lock(&adapter->nfc_lock);
|
|
|
+
|
|
|
+ hlist_for_each_entry(f, &adapter->nfc_filter_list, nfc_node) {
|
|
|
+ if (!memcmp(&f->filter, &filter->filter, sizeof(f->filter))) {
|
|
|
+ err = -EEXIST;
|
|
|
+ NL_SET_ERR_MSG_MOD(extack,
|
|
|
+ "This filter is already set in ethtool");
|
|
|
+ goto err_locked;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_for_each_entry(f, &adapter->cls_flower_list, nfc_node) {
|
|
|
+ if (!memcmp(&f->filter, &filter->filter, sizeof(f->filter))) {
|
|
|
+ err = -EEXIST;
|
|
|
+ NL_SET_ERR_MSG_MOD(extack,
|
|
|
+ "This filter is already set in cls_flower");
|
|
|
+ goto err_locked;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ err = igb_add_filter(adapter, filter);
|
|
|
+ if (err < 0) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Could not add filter to the adapter");
|
|
|
+ goto err_locked;
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_add_head(&filter->nfc_node, &adapter->cls_flower_list);
|
|
|
+
|
|
|
+ spin_unlock(&adapter->nfc_lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_locked:
|
|
|
+ spin_unlock(&adapter->nfc_lock);
|
|
|
+
|
|
|
+err_parse:
|
|
|
+ kfree(filter);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int igb_delete_clsflower(struct igb_adapter *adapter,
|
|
|
+ struct tc_cls_flower_offload *cls_flower)
|
|
|
+{
|
|
|
+ struct igb_nfc_filter *filter;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ spin_lock(&adapter->nfc_lock);
|
|
|
+
|
|
|
+ hlist_for_each_entry(filter, &adapter->cls_flower_list, nfc_node)
|
|
|
+ if (filter->cookie == cls_flower->cookie)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (!filter) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = igb_erase_filter(adapter, filter);
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ hlist_del(&filter->nfc_node);
|
|
|
+ kfree(filter);
|
|
|
+
|
|
|
+out:
|
|
|
+ spin_unlock(&adapter->nfc_lock);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int igb_setup_tc_cls_flower(struct igb_adapter *adapter,
|
|
|
+ struct tc_cls_flower_offload *cls_flower)
|
|
|
+{
|
|
|
+ switch (cls_flower->command) {
|
|
|
+ case TC_CLSFLOWER_REPLACE:
|
|
|
+ return igb_configure_clsflower(adapter, cls_flower);
|
|
|
+ case TC_CLSFLOWER_DESTROY:
|
|
|
+ return igb_delete_clsflower(adapter, cls_flower);
|
|
|
+ case TC_CLSFLOWER_STATS:
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int igb_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
|
|
|
+ void *cb_priv)
|
|
|
+{
|
|
|
+ struct igb_adapter *adapter = cb_priv;
|
|
|
+
|
|
|
+ if (!tc_cls_can_offload_and_chain0(adapter->netdev, type_data))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case TC_SETUP_CLSFLOWER:
|
|
|
+ return igb_setup_tc_cls_flower(adapter, type_data);
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int igb_setup_tc_block(struct igb_adapter *adapter,
|
|
|
+ struct tc_block_offload *f)
|
|
|
+{
|
|
|
+ if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ switch (f->command) {
|
|
|
+ case TC_BLOCK_BIND:
|
|
|
+ return tcf_block_cb_register(f->block, igb_setup_tc_block_cb,
|
|
|
+ adapter, adapter);
|
|
|
+ case TC_BLOCK_UNBIND:
|
|
|
+ tcf_block_cb_unregister(f->block, igb_setup_tc_block_cb,
|
|
|
+ adapter);
|
|
|
+ return 0;
|
|
|
+ default:
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int igb_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
|
|
void *type_data)
|
|
|
{
|
|
@@ -2521,6 +2766,8 @@ static int igb_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
|
|
switch (type) {
|
|
|
case TC_SETUP_QDISC_CBS:
|
|
|
return igb_offload_cbs(adapter, type_data);
|
|
|
+ case TC_SETUP_BLOCK:
|
|
|
+ return igb_setup_tc_block(adapter, type_data);
|
|
|
|
|
|
default:
|
|
|
return -EOPNOTSUPP;
|
|
@@ -2822,6 +3069,9 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
|
if (hw->mac.type >= e1000_82576)
|
|
|
netdev->features |= NETIF_F_SCTP_CRC;
|
|
|
|
|
|
+ if (hw->mac.type >= e1000_i350)
|
|
|
+ netdev->features |= NETIF_F_HW_TC;
|
|
|
+
|
|
|
#define IGB_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
|
|
|
NETIF_F_GSO_GRE_CSUM | \
|
|
|
NETIF_F_GSO_IPXIP4 | \
|
|
@@ -6856,8 +7106,35 @@ static void igb_set_default_mac_filter(struct igb_adapter *adapter)
|
|
|
igb_rar_set_index(adapter, 0);
|
|
|
}
|
|
|
|
|
|
-static int igb_add_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
- const u8 queue)
|
|
|
+/* If the filter to be added and an already existing filter express
|
|
|
+ * the same address and address type, it should be possible to only
|
|
|
+ * override the other configurations, for example the queue to steer
|
|
|
+ * traffic.
|
|
|
+ */
|
|
|
+static bool igb_mac_entry_can_be_used(const struct igb_mac_addr *entry,
|
|
|
+ const u8 *addr, const u8 flags)
|
|
|
+{
|
|
|
+ if (!(entry->state & IGB_MAC_STATE_IN_USE))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if ((entry->state & IGB_MAC_STATE_SRC_ADDR) !=
|
|
|
+ (flags & IGB_MAC_STATE_SRC_ADDR))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (!ether_addr_equal(addr, entry->addr))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/* Add a MAC filter for 'addr' directing matching traffic to 'queue',
|
|
|
+ * 'flags' is used to indicate what kind of match is made, match is by
|
|
|
+ * default for the destination address, if matching by source address
|
|
|
+ * is desired the flag IGB_MAC_STATE_SRC_ADDR can be used.
|
|
|
+ */
|
|
|
+static int igb_add_mac_filter_flags(struct igb_adapter *adapter,
|
|
|
+ const u8 *addr, const u8 queue,
|
|
|
+ const u8 flags)
|
|
|
{
|
|
|
struct e1000_hw *hw = &adapter->hw;
|
|
|
int rar_entries = hw->mac.rar_entry_count -
|
|
@@ -6872,12 +7149,13 @@ static int igb_add_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
* addresses.
|
|
|
*/
|
|
|
for (i = 0; i < rar_entries; i++) {
|
|
|
- if (adapter->mac_table[i].state & IGB_MAC_STATE_IN_USE)
|
|
|
+ if (!igb_mac_entry_can_be_used(&adapter->mac_table[i],
|
|
|
+ addr, flags))
|
|
|
continue;
|
|
|
|
|
|
ether_addr_copy(adapter->mac_table[i].addr, addr);
|
|
|
adapter->mac_table[i].queue = queue;
|
|
|
- adapter->mac_table[i].state |= IGB_MAC_STATE_IN_USE;
|
|
|
+ adapter->mac_table[i].state |= IGB_MAC_STATE_IN_USE | flags;
|
|
|
|
|
|
igb_rar_set_index(adapter, i);
|
|
|
return i;
|
|
@@ -6886,8 +7164,21 @@ static int igb_add_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
return -ENOSPC;
|
|
|
}
|
|
|
|
|
|
-static int igb_del_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
+static int igb_add_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
const u8 queue)
|
|
|
+{
|
|
|
+ return igb_add_mac_filter_flags(adapter, addr, queue, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/* Remove a MAC filter for 'addr' directing matching traffic to
|
|
|
+ * 'queue', 'flags' is used to indicate what kind of match need to be
|
|
|
+ * removed, match is by default for the destination address, if
|
|
|
+ * matching by source address is to be removed the flag
|
|
|
+ * IGB_MAC_STATE_SRC_ADDR can be used.
|
|
|
+ */
|
|
|
+static int igb_del_mac_filter_flags(struct igb_adapter *adapter,
|
|
|
+ const u8 *addr, const u8 queue,
|
|
|
+ const u8 flags)
|
|
|
{
|
|
|
struct e1000_hw *hw = &adapter->hw;
|
|
|
int rar_entries = hw->mac.rar_entry_count -
|
|
@@ -6904,14 +7195,26 @@ static int igb_del_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
for (i = 0; i < rar_entries; i++) {
|
|
|
if (!(adapter->mac_table[i].state & IGB_MAC_STATE_IN_USE))
|
|
|
continue;
|
|
|
+ if ((adapter->mac_table[i].state & flags) != flags)
|
|
|
+ continue;
|
|
|
if (adapter->mac_table[i].queue != queue)
|
|
|
continue;
|
|
|
if (!ether_addr_equal(adapter->mac_table[i].addr, addr))
|
|
|
continue;
|
|
|
|
|
|
- adapter->mac_table[i].state &= ~IGB_MAC_STATE_IN_USE;
|
|
|
- memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
|
|
|
- adapter->mac_table[i].queue = 0;
|
|
|
+ /* When a filter for the default address is "deleted",
|
|
|
+ * we return it to its initial configuration
|
|
|
+ */
|
|
|
+ if (adapter->mac_table[i].state & IGB_MAC_STATE_DEFAULT) {
|
|
|
+ adapter->mac_table[i].state =
|
|
|
+ IGB_MAC_STATE_DEFAULT | IGB_MAC_STATE_IN_USE;
|
|
|
+ adapter->mac_table[i].queue =
|
|
|
+ adapter->vfs_allocated_count;
|
|
|
+ } else {
|
|
|
+ adapter->mac_table[i].state = 0;
|
|
|
+ adapter->mac_table[i].queue = 0;
|
|
|
+ memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
|
|
|
+ }
|
|
|
|
|
|
igb_rar_set_index(adapter, i);
|
|
|
return 0;
|
|
@@ -6920,6 +7223,34 @@ static int igb_del_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
|
|
|
+static int igb_del_mac_filter(struct igb_adapter *adapter, const u8 *addr,
|
|
|
+ const u8 queue)
|
|
|
+{
|
|
|
+ return igb_del_mac_filter_flags(adapter, addr, queue, 0);
|
|
|
+}
|
|
|
+
|
|
|
+int igb_add_mac_steering_filter(struct igb_adapter *adapter,
|
|
|
+ const u8 *addr, u8 queue, u8 flags)
|
|
|
+{
|
|
|
+ struct e1000_hw *hw = &adapter->hw;
|
|
|
+
|
|
|
+ /* In theory, this should be supported on 82575 as well, but
|
|
|
+ * that part wasn't easily accessible during development.
|
|
|
+ */
|
|
|
+ if (hw->mac.type != e1000_i210)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ return igb_add_mac_filter_flags(adapter, addr, queue,
|
|
|
+ IGB_MAC_STATE_QUEUE_STEERING | flags);
|
|
|
+}
|
|
|
+
|
|
|
+int igb_del_mac_steering_filter(struct igb_adapter *adapter,
|
|
|
+ const u8 *addr, u8 queue, u8 flags)
|
|
|
+{
|
|
|
+ return igb_del_mac_filter_flags(adapter, addr, queue,
|
|
|
+ IGB_MAC_STATE_QUEUE_STEERING | flags);
|
|
|
+}
|
|
|
+
|
|
|
static int igb_uc_sync(struct net_device *netdev, const unsigned char *addr)
|
|
|
{
|
|
|
struct igb_adapter *adapter = netdev_priv(netdev);
|
|
@@ -8763,12 +9094,24 @@ static void igb_rar_set_index(struct igb_adapter *adapter, u32 index)
|
|
|
if (is_valid_ether_addr(addr))
|
|
|
rar_high |= E1000_RAH_AV;
|
|
|
|
|
|
- if (hw->mac.type == e1000_82575)
|
|
|
+ if (adapter->mac_table[index].state & IGB_MAC_STATE_SRC_ADDR)
|
|
|
+ rar_high |= E1000_RAH_ASEL_SRC_ADDR;
|
|
|
+
|
|
|
+ switch (hw->mac.type) {
|
|
|
+ case e1000_82575:
|
|
|
+ case e1000_i210:
|
|
|
+ if (adapter->mac_table[index].state &
|
|
|
+ IGB_MAC_STATE_QUEUE_STEERING)
|
|
|
+ rar_high |= E1000_RAH_QSEL_ENABLE;
|
|
|
+
|
|
|
rar_high |= E1000_RAH_POOL_1 *
|
|
|
adapter->mac_table[index].queue;
|
|
|
- else
|
|
|
+ break;
|
|
|
+ default:
|
|
|
rar_high |= E1000_RAH_POOL_1 <<
|
|
|
adapter->mac_table[index].queue;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
wr32(E1000_RAL(index), rar_low);
|
|
@@ -9206,6 +9549,9 @@ static void igb_nfc_filter_exit(struct igb_adapter *adapter)
|
|
|
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
|
|
|
igb_erase_filter(adapter, rule);
|
|
|
|
|
|
+ hlist_for_each_entry(rule, &adapter->cls_flower_list, nfc_node)
|
|
|
+ igb_erase_filter(adapter, rule);
|
|
|
+
|
|
|
spin_unlock(&adapter->nfc_lock);
|
|
|
}
|
|
|
|