Commit e086be9a authored by Vinicius Costa Gomes's avatar Vinicius Costa Gomes Committed by Jeff Kirsher

igb: Add support for adding offloaded clsflower filters

This allows filters added by tc-flower and specifying MAC addresses,
Ethernet types, and the VLAN priority field, to be offloaded to the
controller.

This reuses most of the infrastructure used by ethtool, but clsflower
filters are kept in a separated list, so they are invisible to
ethtool.

To setup clsflower offloading:

$ tc qdisc replace dev eth0 handle 100: parent root mqprio \
     	   	   num_tc 3 map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \
		   queues 1@0 1@1 2@2 hw 0
(clsflower offloading depends on the netword driver to be configured
with multiple traffic classes, we use mqprio's 'num_tc' parameter to
set it to 3)

$ tc qdisc add dev eth0 ingress

Examples of filters:

$ tc filter add dev eth0 parent ffff: flower \
     	    dst_mac aa:aa:aa:aa:aa:aa \
	    hw_tc 2 skip_sw
(just a simple filter filtering for the destination MAC address and
steering that traffic to queue 2)

$ tc filter add dev enp2s0 parent ffff: proto 0x22f0 flower \
     	    src_mac cc:cc:cc:cc:cc:cc \
	    hw_tc 1 skip_sw
(as the i210 doesn't support steering traffic based on the source
address alone, we need to use another steering traffic, in this case
we are using the ethernet type (0x22f0) to steer traffic to queue 1)
Signed-off-by: default avatarVinicius Costa Gomes <vinicius.gomes@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent f8f3d34e
...@@ -465,6 +465,7 @@ struct igb_nfc_input { ...@@ -465,6 +465,7 @@ struct igb_nfc_input {
struct igb_nfc_filter { struct igb_nfc_filter {
struct hlist_node nfc_node; struct hlist_node nfc_node;
struct igb_nfc_input filter; struct igb_nfc_input filter;
unsigned long cookie;
u16 etype_reg_index; u16 etype_reg_index;
u16 sw_idx; u16 sw_idx;
u16 action; u16 action;
...@@ -604,6 +605,7 @@ struct igb_adapter { ...@@ -604,6 +605,7 @@ struct igb_adapter {
/* RX network flow classification support */ /* RX network flow classification support */
struct hlist_head nfc_filter_list; struct hlist_head nfc_filter_list;
struct hlist_head cls_flower_list;
unsigned int nfc_filter_count; unsigned int nfc_filter_count;
/* lock for RX network flow classification filter */ /* lock for RX network flow classification filter */
spinlock_t nfc_lock; spinlock_t nfc_lock;
......
...@@ -2514,16 +2514,197 @@ static int igb_offload_cbs(struct igb_adapter *adapter, ...@@ -2514,16 +2514,197 @@ static int igb_offload_cbs(struct igb_adapter *adapter,
return 0; 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, static int igb_configure_clsflower(struct igb_adapter *adapter,
struct tc_cls_flower_offload *cls_flower) struct tc_cls_flower_offload *cls_flower)
{ {
return -EOPNOTSUPP; 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, static int igb_delete_clsflower(struct igb_adapter *adapter,
struct tc_cls_flower_offload *cls_flower) struct tc_cls_flower_offload *cls_flower)
{ {
return -EOPNOTSUPP; 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, static int igb_setup_tc_cls_flower(struct igb_adapter *adapter,
...@@ -9368,6 +9549,9 @@ static void igb_nfc_filter_exit(struct igb_adapter *adapter) ...@@ -9368,6 +9549,9 @@ static void igb_nfc_filter_exit(struct igb_adapter *adapter)
hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
igb_erase_filter(adapter, rule); 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); spin_unlock(&adapter->nfc_lock);
} }
......
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