Hi, Jakub Kicinski <kubak...@wp.pl> writes:
> On Fri, 2 Mar 2018 10:43:44 -0800, Vinicius Costa Gomes wrote: >> 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, ethtool can be >> used to read these filters, but modification and deletion can only be >> done via tc-flower. >> >> Signed-off-by: Vinicius Costa Gomes <vinicius.go...@intel.com> >> --- >> drivers/net/ethernet/intel/igb/igb.h | 2 + >> drivers/net/ethernet/intel/igb/igb_main.c | 176 >> +++++++++++++++++++++++++++++- >> 2 files changed, 176 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/net/ethernet/intel/igb/igb.h >> b/drivers/net/ethernet/intel/igb/igb.h >> index 43ce6d64f693..0edd3a74d043 100644 >> --- a/drivers/net/ethernet/intel/igb/igb.h >> +++ b/drivers/net/ethernet/intel/igb/igb.h >> @@ -463,6 +463,7 @@ struct igb_nfc_input { >> struct igb_nfc_filter { >> struct hlist_node nfc_node; >> struct igb_nfc_input filter; >> + unsigned long cookie; >> u16 etype_reg_index; >> u16 sw_idx; >> u16 action; >> @@ -601,6 +602,7 @@ struct igb_adapter { >> >> /* RX network flow classification support */ >> struct hlist_head nfc_filter_list; >> + struct hlist_head cls_flower_list; >> unsigned int nfc_filter_count; >> /* lock for RX network flow classification filter */ >> spinlock_t nfc_lock; >> diff --git a/drivers/net/ethernet/intel/igb/igb_main.c >> b/drivers/net/ethernet/intel/igb/igb_main.c >> index b5a6bd37bb16..cbd2048b9110 100644 >> --- a/drivers/net/ethernet/intel/igb/igb_main.c >> +++ b/drivers/net/ethernet/intel/igb/igb_main.c >> @@ -2397,6 +2397,12 @@ static int igb_set_features(struct net_device *netdev, >> hlist_del(&rule->nfc_node); >> kfree(rule); >> } >> + hlist_for_each_entry_safe(rule, node2, >> + &adapter->cls_flower_list, nfc_node) { >> + igb_erase_filter(adapter, rule); >> + hlist_del(&rule->nfc_node); >> + kfree(rule); >> + } > > Hm. I don't think you should flush cls filters when someone disables > NTUPLE. (a) ntuple is independent, (b) the in_hw flag will no longer > reflect the actual state of the system. Makes total sense. Will fix. > >> spin_unlock(&adapter->nfc_lock); >> adapter->nfc_filter_count = 0; >> } >> @@ -2497,16 +2503,179 @@ static int igb_offload_cbs(struct igb_adapter >> *adapter, >> return 0; >> } >> >> +#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) >> + >> +static int igb_parse_cls_flower(struct igb_adapter *adapter, >> + struct tc_cls_flower_offload *f, >> + int traffic_class, >> + struct igb_nfc_filter *input) >> +{ >> + 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))) { >> + dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n", >> + f->dissector->used_keys); > > This will probably trigger for opportunistic offload (non-skip_sw) and > confuse users. > I see your point. I will change to 'dev_warn()', it should not surprise users too much, right? >> + return -EOPNOTSUPP; >> + } >> + >> + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { >> + struct flow_dissector_key_eth_addrs *key = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_ETH_ADDRS, >> + f->key); >> + >> + struct flow_dissector_key_eth_addrs *mask = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_ETH_ADDRS, >> + f->mask); > > You can do the assignment on separate lines to avoid strange variable > declaration. > Fair enough. Will fix. >> + if (is_broadcast_ether_addr(mask->dst)) { >> + input->filter.match_flags |= >> + IGB_FILTER_FLAG_DST_MAC_ADDR; >> + ether_addr_copy(input->filter.dst_addr, key->dst); >> + } >> + >> + if (is_broadcast_ether_addr(mask->src)) { >> + input->filter.match_flags |= >> + IGB_FILTER_FLAG_SRC_MAC_ADDR; >> + ether_addr_copy(input->filter.src_addr, key->src); >> + } > > If the mask is not full you need to reject the filter, I don't think > you can just ignore the MAC if it's masked.. > > Same two comments for conditions below. Yeah, being silent in these cases can cause some confusion. Will fix them. > >> + } >> + >> + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { >> + struct flow_dissector_key_basic *key = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_BASIC, >> + f->key); >> + >> + struct flow_dissector_key_basic *mask = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_BASIC, >> + f->mask); >> + >> + if (mask->n_proto == ETHER_TYPE_FULL_MASK) { >> + 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 = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_VLAN, >> + f->key); >> + struct flow_dissector_key_vlan *mask = >> + skb_flow_dissector_target(f->dissector, >> + FLOW_DISSECTOR_KEY_VLAN, >> + f->mask); >> + >> + if (mask->vlan_priority) { >> + 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) >> { >> - return -EOPNOTSUPP; >> + struct igb_nfc_filter *filter, *f; >> + int err, tc; >> + >> + if (!(adapter->netdev->hw_features & NETIF_F_NTUPLE)) >> + return -EOPNOTSUPP; > > I'd rather there wasn't dependency on NTUPLEs for cls offloads. > Cool. Will remove this check. >> + tc = tc_classid_to_hwtc(adapter->netdev, cls_flower->classid); >> + if (tc < 0) { >> + dev_err(&adapter->pdev->dev, "Invalid traffic class\n"); >> + 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) { >> + dev_err(&adapter->pdev->dev, "Invalid cls_flower filter\n"); >> + 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; >> + dev_err(&adapter->pdev->dev, >> + "This filter is already set in ethtool\n"); > > Consider using extack too, maybe? Yeah, will do. > >> + 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; >> + dev_err(&adapter->pdev->dev, >> + "This filter is already set in cls_flower\n"); >> + goto err_locked; >> + } >> + } >> + >> + err = igb_add_filter(adapter, filter); >> + if (err < 0) >> + 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; >> } Cheers, -- Vinicius