igc: Check VLAN TCI mask
[platform/kernel/linux-rpi.git] / drivers / net / ethernet / intel / igc / igc_ethtool.c
index fa71bfb..a044e4a 100644 (file)
@@ -957,6 +957,7 @@ static int igc_ethtool_set_coalesce(struct net_device *netdev,
 }
 
 #define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
+#define VLAN_TCI_FULL_MASK ((__force __be16)~0)
 static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
                                    struct ethtool_rxnfc *cmd)
 {
@@ -988,7 +989,7 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
        if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
                fsp->flow_type |= FLOW_EXT;
                fsp->h_ext.vlan_tci = htons(rule->filter.vlan_tci);
-               fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK);
+               fsp->m_ext.vlan_tci = htons(rule->filter.vlan_tci_mask);
        }
 
        if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
@@ -1223,6 +1224,7 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
 
        if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
                rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
+               rule->filter.vlan_tci_mask = ntohs(fsp->m_ext.vlan_tci);
                rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
        }
 
@@ -1260,11 +1262,19 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
                memcpy(rule->filter.user_mask, fsp->m_ext.data, sizeof(fsp->m_ext.data));
        }
 
-       /* When multiple filter options or user data or vlan etype is set, use a
-        * flex filter.
+       /* The i225/i226 has various different filters. Flex filters provide a
+        * way to match up to the first 128 bytes of a packet. Use them for:
+        *   a) For specific user data
+        *   b) For VLAN EtherType
+        *   c) For full TCI match
+        *   d) Or in case multiple filter criteria are set
+        *
+        * Otherwise, use the simple MAC, VLAN PRIO or EtherType filters.
         */
        if ((rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) ||
            (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) ||
+           ((rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) &&
+            rule->filter.vlan_tci_mask == ntohs(VLAN_TCI_FULL_MASK)) ||
            (rule->filter.match_flags & (rule->filter.match_flags - 1)))
                rule->flex = true;
        else
@@ -1334,6 +1344,18 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
                return -EINVAL;
        }
 
+       /* There are two ways to match the VLAN TCI:
+        *  1. Match on PCP field and use vlan prio filter for it
+        *  2. Match on complete TCI field and use flex filter for it
+        */
+       if ((fsp->flow_type & FLOW_EXT) &&
+           fsp->m_ext.vlan_tci &&
+           fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK) &&
+           fsp->m_ext.vlan_tci != VLAN_TCI_FULL_MASK) {
+               netdev_dbg(netdev, "VLAN mask not supported\n");
+               return -EOPNOTSUPP;
+       }
+
        if (fsp->location >= IGC_MAX_RXNFC_RULES) {
                netdev_dbg(netdev, "Invalid location\n");
                return -EINVAL;