sfc: Extend and abstract efx_filter_spec to cover Huntington/EF10
authorBen Hutchings <bhutchings@solarflare.com>
Fri, 26 Oct 2012 23:33:28 +0000 (00:33 +0100)
committerBen Hutchings <bhutchings@solarflare.com>
Thu, 22 Aug 2013 18:25:53 +0000 (19:25 +0100)
Replace type field with match_flags.  Add rss_context and match values
covering of most of what is now in the MCDI protocol.

Change some fields into bitfields so that the structure size doesn't grow
beyond 64 bytes.

Ditch the filter decoding functions as it is now easier to pick apart
the abstract structure.

Rewrite ethtool NFC rule functions to set/get filter match flags and
values directly.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/filter.c
drivers/net/ethernet/sfc/filter.h

index ec5cacd427ec12d7a504387d6f4643fac2c052b0..58ae28b8820dd5ba5d6fb107065ea9df0924b9de 100644 (file)
@@ -799,11 +799,12 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
        return efx_reset(efx, rc);
 }
 
-/* MAC address mask including only MC flag */
-static const u8 mac_addr_mc_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+/* MAC address mask including only I/G bit */
+static const u8 mac_addr_ig_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
 
 #define IP4_ADDR_FULL_MASK     ((__force __be32)~0)
 #define PORT_FULL_MASK         ((__force __be16)~0)
+#define ETHER_TYPE_FULL_MASK   ((__force __be16)~0)
 
 static int efx_ethtool_get_class_rule(struct efx_nic *efx,
                                      struct ethtool_rx_flow_spec *rule)
@@ -813,8 +814,6 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
        struct ethhdr *mac_entry = &rule->h_u.ether_spec;
        struct ethhdr *mac_mask = &rule->m_u.ether_spec;
        struct efx_filter_spec spec;
-       u16 vid;
-       u8 proto;
        int rc;
 
        rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL,
@@ -827,39 +826,67 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
        else
                rule->ring_cookie = spec.dmaq_id;
 
-       if (spec.type == EFX_FILTER_MC_DEF || spec.type == EFX_FILTER_UC_DEF) {
-               rule->flow_type = ETHER_FLOW;
-               memcpy(mac_mask->h_dest, mac_addr_mc_mask, ETH_ALEN);
-               if (spec.type == EFX_FILTER_MC_DEF)
-                       memcpy(mac_entry->h_dest, mac_addr_mc_mask, ETH_ALEN);
-               return 0;
-       }
-
-       rc = efx_filter_get_eth_local(&spec, &vid, mac_entry->h_dest);
-       if (rc == 0) {
+       if ((spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) &&
+           spec.ether_type == htons(ETH_P_IP) &&
+           (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) &&
+           (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) &&
+           !(spec.match_flags &
+             ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
+               EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT))) {
+               rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ?
+                                  TCP_V4_FLOW : UDP_V4_FLOW);
+               if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) {
+                       ip_entry->ip4dst = spec.loc_host[0];
+                       ip_mask->ip4dst = IP4_ADDR_FULL_MASK;
+               }
+               if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) {
+                       ip_entry->ip4src = spec.rem_host[0];
+                       ip_mask->ip4src = IP4_ADDR_FULL_MASK;
+               }
+               if (spec.match_flags & EFX_FILTER_MATCH_LOC_PORT) {
+                       ip_entry->pdst = spec.loc_port;
+                       ip_mask->pdst = PORT_FULL_MASK;
+               }
+               if (spec.match_flags & EFX_FILTER_MATCH_REM_PORT) {
+                       ip_entry->psrc = spec.rem_port;
+                       ip_mask->psrc = PORT_FULL_MASK;
+               }
+       } else if (!(spec.match_flags &
+                    ~(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG |
+                      EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_ETHER_TYPE |
+                      EFX_FILTER_MATCH_OUTER_VID))) {
                rule->flow_type = ETHER_FLOW;
-               memset(mac_mask->h_dest, ~0, ETH_ALEN);
-               if (vid != EFX_FILTER_VID_UNSPEC) {
-                       rule->flow_type |= FLOW_EXT;
-                       rule->h_ext.vlan_tci = htons(vid);
-                       rule->m_ext.vlan_tci = htons(0xfff);
+               if (spec.match_flags &
+                   (EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG)) {
+                       memcpy(mac_entry->h_dest, spec.loc_mac, ETH_ALEN);
+                       if (spec.match_flags & EFX_FILTER_MATCH_LOC_MAC)
+                               memset(mac_mask->h_dest, ~0, ETH_ALEN);
+                       else
+                               memcpy(mac_mask->h_dest, mac_addr_ig_mask,
+                                      ETH_ALEN);
                }
-               return 0;
+               if (spec.match_flags & EFX_FILTER_MATCH_REM_MAC) {
+                       memcpy(mac_entry->h_source, spec.rem_mac, ETH_ALEN);
+                       memset(mac_mask->h_source, ~0, ETH_ALEN);
+               }
+               if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) {
+                       mac_entry->h_proto = spec.ether_type;
+                       mac_mask->h_proto = ETHER_TYPE_FULL_MASK;
+               }
+       } else {
+               /* The above should handle all filters that we insert */
+               WARN_ON(1);
+               return -EINVAL;
        }
 
-       rc = efx_filter_get_ipv4_local(&spec, &proto,
-                                      &ip_entry->ip4dst, &ip_entry->pdst);
-       if (rc != 0) {
-               rc = efx_filter_get_ipv4_full(
-                       &spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst,
-                       &ip_entry->ip4src, &ip_entry->psrc);
-               EFX_WARN_ON_PARANOID(rc);
-               ip_mask->ip4src = IP4_ADDR_FULL_MASK;
-               ip_mask->psrc = PORT_FULL_MASK;
+       if (spec.match_flags & EFX_FILTER_MATCH_OUTER_VID) {
+               rule->flow_type |= FLOW_EXT;
+               rule->h_ext.vlan_tci = spec.outer_vid;
+               rule->m_ext.vlan_tci = htons(0xfff);
        }
-       rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW;
-       ip_mask->ip4dst = IP4_ADDR_FULL_MASK;
-       ip_mask->pdst = PORT_FULL_MASK;
+
        return rc;
 }
 
@@ -969,80 +996,78 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
                           (rule->ring_cookie == RX_CLS_FLOW_DISC) ?
                           EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie);
 
-       switch (rule->flow_type) {
+       switch (rule->flow_type & ~FLOW_EXT) {
        case TCP_V4_FLOW:
-       case UDP_V4_FLOW: {
-               u8 proto = (rule->flow_type == TCP_V4_FLOW ?
-                           IPPROTO_TCP : IPPROTO_UDP);
-
-               /* Must match all of destination, */
-               if (!(ip_mask->ip4dst == IP4_ADDR_FULL_MASK &&
-                     ip_mask->pdst == PORT_FULL_MASK))
-                       return -EINVAL;
-               /* all or none of source, */
-               if ((ip_mask->ip4src || ip_mask->psrc) &&
-                   !(ip_mask->ip4src == IP4_ADDR_FULL_MASK &&
-                     ip_mask->psrc == PORT_FULL_MASK))
-                       return -EINVAL;
-               /* and nothing else */
-               if (ip_mask->tos || rule->m_ext.vlan_tci)
+       case UDP_V4_FLOW:
+               spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE |
+                                   EFX_FILTER_MATCH_IP_PROTO);
+               spec.ether_type = htons(ETH_P_IP);
+               spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V4_FLOW ?
+                                IPPROTO_TCP : IPPROTO_UDP);
+               if (ip_mask->ip4dst) {
+                       if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK)
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST;
+                       spec.loc_host[0] = ip_entry->ip4dst;
+               }
+               if (ip_mask->ip4src) {
+                       if (ip_mask->ip4src != IP4_ADDR_FULL_MASK)
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_REM_HOST;
+                       spec.rem_host[0] = ip_entry->ip4src;
+               }
+               if (ip_mask->pdst) {
+                       if (ip_mask->pdst != PORT_FULL_MASK)
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_LOC_PORT;
+                       spec.loc_port = ip_entry->pdst;
+               }
+               if (ip_mask->psrc) {
+                       if (ip_mask->psrc != PORT_FULL_MASK)
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_REM_PORT;
+                       spec.rem_port = ip_entry->psrc;
+               }
+               if (ip_mask->tos)
                        return -EINVAL;
-
-               if (ip_mask->ip4src)
-                       rc = efx_filter_set_ipv4_full(&spec, proto,
-                                                     ip_entry->ip4dst,
-                                                     ip_entry->pdst,
-                                                     ip_entry->ip4src,
-                                                     ip_entry->psrc);
-               else
-                       rc = efx_filter_set_ipv4_local(&spec, proto,
-                                                      ip_entry->ip4dst,
-                                                      ip_entry->pdst);
-               if (rc)
-                       return rc;
                break;
-       }
-
-       case ETHER_FLOW | FLOW_EXT:
-       case ETHER_FLOW: {
-               u16 vlan_tag_mask = (rule->flow_type & FLOW_EXT ?
-                                    ntohs(rule->m_ext.vlan_tci) : 0);
 
-               /* Must not match on source address or Ethertype */
-               if (!is_zero_ether_addr(mac_mask->h_source) ||
-                   mac_mask->h_proto)
-                       return -EINVAL;
-
-               /* Is it a default UC or MC filter? */
-               if (ether_addr_equal(mac_mask->h_dest, mac_addr_mc_mask) &&
-                   vlan_tag_mask == 0) {
-                       if (is_multicast_ether_addr(mac_entry->h_dest))
-                               rc = efx_filter_set_mc_def(&spec);
+       case ETHER_FLOW:
+               if (!is_zero_ether_addr(mac_mask->h_dest)) {
+                       if (ether_addr_equal(mac_mask->h_dest,
+                                            mac_addr_ig_mask))
+                               spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
+                       else if (is_broadcast_ether_addr(mac_mask->h_dest))
+                               spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC;
                        else
-                               rc = efx_filter_set_uc_def(&spec);
+                               return -EINVAL;
+                       memcpy(spec.loc_mac, mac_entry->h_dest, ETH_ALEN);
                }
-               /* Otherwise, it must match all of destination and all
-                * or none of VID.
-                */
-               else if (is_broadcast_ether_addr(mac_mask->h_dest) &&
-                        (vlan_tag_mask == 0xfff || vlan_tag_mask == 0)) {
-                       rc = efx_filter_set_eth_local(
-                               &spec,
-                               vlan_tag_mask ?
-                               ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC,
-                               mac_entry->h_dest);
-               } else {
-                       rc = -EINVAL;
+               if (!is_zero_ether_addr(mac_mask->h_source)) {
+                       if (!is_broadcast_ether_addr(mac_mask->h_source))
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_REM_MAC;
+                       memcpy(spec.rem_mac, mac_entry->h_source, ETH_ALEN);
+               }
+               if (mac_mask->h_proto) {
+                       if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK)
+                               return -EINVAL;
+                       spec.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE;
+                       spec.ether_type = mac_entry->h_proto;
                }
-               if (rc)
-                       return rc;
                break;
-       }
 
        default:
                return -EINVAL;
        }
 
+       if ((rule->flow_type & FLOW_EXT) && rule->m_ext.vlan_tci) {
+               if (rule->m_ext.vlan_tci != htons(0xfff))
+                       return -EINVAL;
+               spec.match_flags |= EFX_FILTER_MATCH_OUTER_VID;
+               spec.outer_vid = rule->h_ext.vlan_tci;
+       }
+
        rc = efx_filter_insert_filter(efx, &spec, true);
        if (rc < 0)
                return rc;
index c547630cfee42aff064f744d2fe35e25702d8655..96f8f2e344038b387be4e1bf77f2a0a2cef54f46 100644 (file)
  * counter-productive. */
 #define EFX_FARCH_FILTER_CTL_SRCH_HINT_MAX 5
 
+enum efx_farch_filter_type {
+       EFX_FARCH_FILTER_TCP_FULL = 0,
+       EFX_FARCH_FILTER_TCP_WILD,
+       EFX_FARCH_FILTER_UDP_FULL,
+       EFX_FARCH_FILTER_UDP_WILD,
+       EFX_FARCH_FILTER_MAC_FULL = 4,
+       EFX_FARCH_FILTER_MAC_WILD,
+       EFX_FARCH_FILTER_UC_DEF = 8,
+       EFX_FARCH_FILTER_MC_DEF,
+       EFX_FARCH_FILTER_TYPE_COUNT,            /* number of specific types */
+};
+
 enum efx_farch_filter_table_id {
        EFX_FARCH_FILTER_TABLE_RX_IP = 0,
        EFX_FARCH_FILTER_TABLE_RX_MAC,
@@ -62,7 +74,7 @@ struct efx_farch_filter_table {
        unsigned        used;           /* number currently used */
        unsigned long   *used_bitmap;
        struct efx_farch_filter_spec *spec;
-       unsigned        search_depth[EFX_FILTER_TYPE_COUNT];
+       unsigned        search_depth[EFX_FARCH_FILTER_TYPE_COUNT];
 };
 
 struct efx_filter_state {
@@ -106,33 +118,22 @@ static enum efx_farch_filter_table_id
 efx_farch_filter_spec_table_id(const struct efx_farch_filter_spec *spec)
 {
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
-                    (EFX_FILTER_TCP_FULL >> 2));
+                    (EFX_FARCH_FILTER_TCP_FULL >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
-                    (EFX_FILTER_TCP_WILD >> 2));
+                    (EFX_FARCH_FILTER_TCP_WILD >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
-                    (EFX_FILTER_UDP_FULL >> 2));
+                    (EFX_FARCH_FILTER_UDP_FULL >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
-                    (EFX_FILTER_UDP_WILD >> 2));
+                    (EFX_FARCH_FILTER_UDP_WILD >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC !=
-                    (EFX_FILTER_MAC_FULL >> 2));
+                    (EFX_FARCH_FILTER_MAC_FULL >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC !=
-                    (EFX_FILTER_MAC_WILD >> 2));
+                    (EFX_FARCH_FILTER_MAC_WILD >> 2));
        BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_TX_MAC !=
                     EFX_FARCH_FILTER_TABLE_RX_MAC + 2);
-       EFX_BUG_ON_PARANOID(spec->type == EFX_FILTER_UNSPEC);
        return (spec->type >> 2) + ((spec->flags & EFX_FILTER_FLAG_TX) ? 2 : 0);
 }
 
-static struct efx_farch_filter_table *
-efx_farch_filter_spec_table(struct efx_filter_state *state,
-                           const struct efx_farch_filter_spec *spec)
-{
-       if (spec->type == EFX_FILTER_UNSPEC)
-               return NULL;
-       else
-               return &state->table[efx_farch_filter_spec_table_id(spec)];
-}
-
 static void
 efx_farch_filter_table_reset_search_depth(struct efx_farch_filter_table *table)
 {
@@ -149,27 +150,27 @@ static void efx_farch_filter_push_rx_config(struct efx_nic *efx)
 
        table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP];
        EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT,
-                           table->search_depth[EFX_FILTER_TCP_FULL] +
+                           table->search_depth[EFX_FARCH_FILTER_TCP_FULL] +
                            EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
        EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT,
-                           table->search_depth[EFX_FILTER_TCP_WILD] +
+                           table->search_depth[EFX_FARCH_FILTER_TCP_WILD] +
                            EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
        EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT,
-                           table->search_depth[EFX_FILTER_UDP_FULL] +
+                           table->search_depth[EFX_FARCH_FILTER_UDP_FULL] +
                            EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
        EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT,
-                           table->search_depth[EFX_FILTER_UDP_WILD] +
+                           table->search_depth[EFX_FARCH_FILTER_UDP_WILD] +
                            EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
 
        table = &state->table[EFX_FARCH_FILTER_TABLE_RX_MAC];
        if (table->size) {
                EFX_SET_OWORD_FIELD(
                        filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
-                       table->search_depth[EFX_FILTER_MAC_FULL] +
+                       table->search_depth[EFX_FARCH_FILTER_MAC_FULL] +
                        EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
                EFX_SET_OWORD_FIELD(
                        filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
-                       table->search_depth[EFX_FILTER_MAC_WILD] +
+                       table->search_depth[EFX_FARCH_FILTER_MAC_WILD] +
                        EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
        }
 
@@ -225,223 +226,202 @@ static void efx_farch_filter_push_tx_limits(struct efx_nic *efx)
        if (table->size) {
                EFX_SET_OWORD_FIELD(
                        tx_cfg, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
-                       table->search_depth[EFX_FILTER_MAC_FULL] +
+                       table->search_depth[EFX_FARCH_FILTER_MAC_FULL] +
                        EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
                EFX_SET_OWORD_FIELD(
                        tx_cfg, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
-                       table->search_depth[EFX_FILTER_MAC_WILD] +
+                       table->search_depth[EFX_FARCH_FILTER_MAC_WILD] +
                        EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
        }
 
        efx_writeo(efx, &tx_cfg, FR_AZ_TX_CFG);
 }
 
-static inline void __efx_filter_set_ipv4(struct efx_filter_spec *spec,
-                                        __be32 host1, __be16 port1,
-                                        __be32 host2, __be16 port2)
-{
-       spec->data[0] = ntohl(host1) << 16 | ntohs(port1);
-       spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16;
-       spec->data[2] = ntohl(host2);
-}
-
-static inline void __efx_filter_get_ipv4(const struct efx_filter_spec *spec,
-                                        __be32 *host1, __be16 *port1,
-                                        __be32 *host2, __be16 *port2)
-{
-       *host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16);
-       *port1 = htons(spec->data[0]);
-       *host2 = htonl(spec->data[2]);
-       *port2 = htons(spec->data[1] >> 16);
-}
-
-/**
- * efx_filter_set_ipv4_local - specify IPv4 host, transport protocol and port
- * @spec: Specification to initialise
- * @proto: Transport layer protocol number
- * @host: Local host address (network byte order)
- * @port: Local port (network byte order)
- */
-int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
-                             __be32 host, __be16 port)
+static int
+efx_farch_filter_from_gen_spec(struct efx_farch_filter_spec *spec,
+                              const struct efx_filter_spec *gen_spec)
 {
-       __be32 host1;
-       __be16 port1;
-
-       EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX));
-
-       /* This cannot currently be combined with other filtering */
-       if (spec->type != EFX_FILTER_UNSPEC)
-               return -EPROTONOSUPPORT;
+       bool is_full = false;
 
-       if (port == 0)
+       if ((gen_spec->flags & EFX_FILTER_FLAG_RX_RSS) &&
+           gen_spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT)
                return -EINVAL;
 
-       switch (proto) {
-       case IPPROTO_TCP:
-               spec->type = EFX_FILTER_TCP_WILD;
-               break;
-       case IPPROTO_UDP:
-               spec->type = EFX_FILTER_UDP_WILD;
-               break;
-       default:
-               return -EPROTONOSUPPORT;
-       }
-
-       /* Filter is constructed in terms of source and destination,
-        * with the odd wrinkle that the ports are swapped in a UDP
-        * wildcard filter.  We need to convert from local and remote
-        * (= zero for wildcard) addresses.
-        */
-       host1 = 0;
-       if (proto != IPPROTO_UDP) {
-               port1 = 0;
-       } else {
-               port1 = port;
-               port = 0;
-       }
-
-       __efx_filter_set_ipv4(spec, host1, port1, host, port);
-       return 0;
-}
+       spec->priority = gen_spec->priority;
+       spec->flags = gen_spec->flags;
+       spec->dmaq_id = gen_spec->dmaq_id;
 
-int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec,
-                             u8 *proto, __be32 *host, __be16 *port)
-{
-       __be32 host1;
-       __be16 port1;
+       switch (gen_spec->match_flags) {
+       case (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+             EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+             EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT):
+               is_full = true;
+               /* fall through */
+       case (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+             EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT): {
+               __be32 rhost, host1, host2;
+               __be16 rport, port1, port2;
 
-       switch (spec->type) {
-       case EFX_FILTER_TCP_WILD:
-               *proto = IPPROTO_TCP;
-               __efx_filter_get_ipv4(spec, &host1, &port1, host, port);
-               return 0;
-       case EFX_FILTER_UDP_WILD:
-               *proto = IPPROTO_UDP;
-               __efx_filter_get_ipv4(spec, &host1, port, host, &port1);
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-/**
- * efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
- * @spec: Specification to initialise
- * @proto: Transport layer protocol number
- * @host: Local host address (network byte order)
- * @port: Local port (network byte order)
- * @rhost: Remote host address (network byte order)
- * @rport: Remote port (network byte order)
- */
-int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
-                            __be32 host, __be16 port,
-                            __be32 rhost, __be16 rport)
-{
-       EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX));
+               EFX_BUG_ON_PARANOID(!(gen_spec->flags & EFX_FILTER_FLAG_RX));
 
-       /* This cannot currently be combined with other filtering */
-       if (spec->type != EFX_FILTER_UNSPEC)
-               return -EPROTONOSUPPORT;
+               if (gen_spec->ether_type != htons(ETH_P_IP))
+                       return -EPROTONOSUPPORT;
+               if (gen_spec->loc_port == 0 ||
+                   (is_full && gen_spec->rem_port == 0))
+                       return -EADDRNOTAVAIL;
+               switch (gen_spec->ip_proto) {
+               case IPPROTO_TCP:
+                       spec->type = (is_full ? EFX_FARCH_FILTER_TCP_FULL :
+                                     EFX_FARCH_FILTER_TCP_WILD);
+                       break;
+               case IPPROTO_UDP:
+                       spec->type = (is_full ? EFX_FARCH_FILTER_UDP_FULL :
+                                     EFX_FARCH_FILTER_UDP_WILD);
+                       break;
+               default:
+                       return -EPROTONOSUPPORT;
+               }
 
-       if (port == 0 || rport == 0)
-               return -EINVAL;
+               /* Filter is constructed in terms of source and destination,
+                * with the odd wrinkle that the ports are swapped in a UDP
+                * wildcard filter.  We need to convert from local and remote
+                * (= zero for wildcard) addresses.
+                */
+               rhost = is_full ? gen_spec->rem_host[0] : 0;
+               rport = is_full ? gen_spec->rem_port : 0;
+               host1 = rhost;
+               host2 = gen_spec->loc_host[0];
+               if (!is_full && gen_spec->ip_proto == IPPROTO_UDP) {
+                       port1 = gen_spec->loc_port;
+                       port2 = rport;
+               } else {
+                       port1 = rport;
+                       port2 = gen_spec->loc_port;
+               }
+               spec->data[0] = ntohl(host1) << 16 | ntohs(port1);
+               spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16;
+               spec->data[2] = ntohl(host2);
 
-       switch (proto) {
-       case IPPROTO_TCP:
-               spec->type = EFX_FILTER_TCP_FULL;
                break;
-       case IPPROTO_UDP:
-               spec->type = EFX_FILTER_UDP_FULL;
-               break;
-       default:
-               return -EPROTONOSUPPORT;
        }
 
-       __efx_filter_set_ipv4(spec, rhost, rport, host, port);
-       return 0;
-}
-
-int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec,
-                            u8 *proto, __be32 *host, __be16 *port,
-                            __be32 *rhost, __be16 *rport)
-{
-       switch (spec->type) {
-       case EFX_FILTER_TCP_FULL:
-               *proto = IPPROTO_TCP;
+       case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
+               is_full = true;
+               /* fall through */
+       case EFX_FILTER_MATCH_LOC_MAC:
+               spec->type = (is_full ? EFX_FARCH_FILTER_MAC_FULL :
+                             EFX_FARCH_FILTER_MAC_WILD);
+               spec->data[0] = is_full ? ntohs(gen_spec->outer_vid) : 0;
+               spec->data[1] = (gen_spec->loc_mac[2] << 24 |
+                                gen_spec->loc_mac[3] << 16 |
+                                gen_spec->loc_mac[4] << 8 |
+                                gen_spec->loc_mac[5]);
+               spec->data[2] = (gen_spec->loc_mac[0] << 8 |
+                                gen_spec->loc_mac[1]);
                break;
-       case EFX_FILTER_UDP_FULL:
-               *proto = IPPROTO_UDP;
+
+       case EFX_FILTER_MATCH_LOC_MAC_IG:
+               spec->type = (is_multicast_ether_addr(gen_spec->loc_mac) ?
+                             EFX_FARCH_FILTER_MC_DEF :
+                             EFX_FARCH_FILTER_UC_DEF);
+               memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
                break;
+
        default:
-               return -EINVAL;
+               return -EPROTONOSUPPORT;
        }
 
-       __efx_filter_get_ipv4(spec, rhost, rport, host, port);
        return 0;
 }
 
-/**
- * efx_filter_set_eth_local - specify local Ethernet address and optional VID
- * @spec: Specification to initialise
- * @vid: VLAN ID to match, or %EFX_FILTER_VID_UNSPEC
- * @addr: Local Ethernet MAC address
- */
-int efx_filter_set_eth_local(struct efx_filter_spec *spec,
-                            u16 vid, const u8 *addr)
+static void
+efx_farch_filter_to_gen_spec(struct efx_filter_spec *gen_spec,
+                            const struct efx_farch_filter_spec *spec)
 {
-       EFX_BUG_ON_PARANOID(!(spec->flags &
-                             (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
-
-       /* This cannot currently be combined with other filtering */
-       if (spec->type != EFX_FILTER_UNSPEC)
-               return -EPROTONOSUPPORT;
-
-       if (vid == EFX_FILTER_VID_UNSPEC) {
-               spec->type = EFX_FILTER_MAC_WILD;
-               spec->data[0] = 0;
-       } else {
-               spec->type = EFX_FILTER_MAC_FULL;
-               spec->data[0] = vid;
-       }
+       bool is_full = false;
 
-       spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5];
-       spec->data[2] = addr[0] << 8 | addr[1];
-       return 0;
-}
+       /* *gen_spec should be completely initialised, to be consistent
+        * with efx_filter_init_{rx,tx}() and in case we want to copy
+        * it back to userland.
+        */
+       memset(gen_spec, 0, sizeof(*gen_spec));
 
-/**
- * efx_filter_set_uc_def - specify matching otherwise-unmatched unicast
- * @spec: Specification to initialise
- */
-int efx_filter_set_uc_def(struct efx_filter_spec *spec)
-{
-       EFX_BUG_ON_PARANOID(!(spec->flags &
-                             (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
+       gen_spec->priority = spec->priority;
+       gen_spec->flags = spec->flags;
+       gen_spec->dmaq_id = spec->dmaq_id;
 
-       if (spec->type != EFX_FILTER_UNSPEC)
-               return -EINVAL;
+       switch (spec->type) {
+       case EFX_FARCH_FILTER_TCP_FULL:
+       case EFX_FARCH_FILTER_UDP_FULL:
+               is_full = true;
+               /* fall through */
+       case EFX_FARCH_FILTER_TCP_WILD:
+       case EFX_FARCH_FILTER_UDP_WILD: {
+               __be32 host1, host2;
+               __be16 port1, port2;
+
+               gen_spec->match_flags =
+                       EFX_FILTER_MATCH_ETHER_TYPE |
+                       EFX_FILTER_MATCH_IP_PROTO |
+                       EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
+               if (is_full)
+                       gen_spec->match_flags |= (EFX_FILTER_MATCH_REM_HOST |
+                                                 EFX_FILTER_MATCH_REM_PORT);
+               gen_spec->ether_type = htons(ETH_P_IP);
+               gen_spec->ip_proto =
+                       (spec->type == EFX_FARCH_FILTER_TCP_FULL ||
+                        spec->type == EFX_FARCH_FILTER_TCP_WILD) ?
+                       IPPROTO_TCP : IPPROTO_UDP;
+
+               host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16);
+               port1 = htons(spec->data[0]);
+               host2 = htonl(spec->data[2]);
+               port2 = htons(spec->data[1] >> 16);
+               if (spec->flags & EFX_FILTER_FLAG_TX) {
+                       gen_spec->loc_host[0] = host1;
+                       gen_spec->rem_host[0] = host2;
+               } else {
+                       gen_spec->loc_host[0] = host2;
+                       gen_spec->rem_host[0] = host1;
+               }
+               if (!!(gen_spec->flags & EFX_FILTER_FLAG_TX) ^
+                   (!is_full && gen_spec->ip_proto == IPPROTO_UDP)) {
+                       gen_spec->loc_port = port1;
+                       gen_spec->rem_port = port2;
+               } else {
+                       gen_spec->loc_port = port2;
+                       gen_spec->rem_port = port1;
+               }
 
-       spec->type = EFX_FILTER_UC_DEF;
-       memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
-       return 0;
-}
+               break;
+       }
 
-/**
- * efx_filter_set_mc_def - specify matching otherwise-unmatched multicast
- * @spec: Specification to initialise
- */
-int efx_filter_set_mc_def(struct efx_filter_spec *spec)
-{
-       EFX_BUG_ON_PARANOID(!(spec->flags &
-                             (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
+       case EFX_FARCH_FILTER_MAC_FULL:
+               is_full = true;
+               /* fall through */
+       case EFX_FARCH_FILTER_MAC_WILD:
+               gen_spec->match_flags = EFX_FILTER_MATCH_LOC_MAC;
+               if (is_full)
+                       gen_spec->match_flags |= EFX_FILTER_MATCH_OUTER_VID;
+               gen_spec->loc_mac[0] = spec->data[2] >> 8;
+               gen_spec->loc_mac[1] = spec->data[2];
+               gen_spec->loc_mac[2] = spec->data[1] >> 24;
+               gen_spec->loc_mac[3] = spec->data[1] >> 16;
+               gen_spec->loc_mac[4] = spec->data[1] >> 8;
+               gen_spec->loc_mac[5] = spec->data[1];
+               gen_spec->outer_vid = htons(spec->data[0]);
+               break;
 
-       if (spec->type != EFX_FILTER_UNSPEC)
-               return -EINVAL;
+       case EFX_FARCH_FILTER_UC_DEF:
+       case EFX_FARCH_FILTER_MC_DEF:
+               gen_spec->match_flags = EFX_FILTER_MATCH_LOC_MAC_IG;
+               gen_spec->loc_mac[0] = spec->type == EFX_FARCH_FILTER_MC_DEF;
+               break;
 
-       spec->type = EFX_FILTER_MC_DEF;
-       memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
-       return 0;
+       default:
+               WARN_ON(1);
+               break;
+       }
 }
 
 static void
@@ -455,7 +435,7 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
        /* If there's only one channel then disable RSS for non VF
         * traffic, thereby allowing VFs to use RSS when the PF can't.
         */
-       spec->type = EFX_FILTER_UC_DEF + filter_idx;
+       spec->type = EFX_FARCH_FILTER_UC_DEF + filter_idx;
        spec->priority = EFX_FILTER_PRI_MANUAL;
        spec->flags = (EFX_FILTER_FLAG_RX |
                       (efx->n_rx_channels > 1 ? EFX_FILTER_FLAG_RX_RSS : 0) |
@@ -464,29 +444,6 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
        table->used_bitmap[0] |= 1 << filter_idx;
 }
 
-int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
-                            u16 *vid, u8 *addr)
-{
-       switch (spec->type) {
-       case EFX_FILTER_MAC_WILD:
-               *vid = EFX_FILTER_VID_UNSPEC;
-               break;
-       case EFX_FILTER_MAC_FULL:
-               *vid = spec->data[0];
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       addr[0] = spec->data[2] >> 8;
-       addr[1] = spec->data[2];
-       addr[2] = spec->data[1] >> 24;
-       addr[3] = spec->data[1] >> 16;
-       addr[4] = spec->data[1] >> 8;
-       addr[5] = spec->data[1];
-       return 0;
-}
-
 /* Build a filter entry and return its n-tuple key. */
 static u32 efx_farch_filter_build(efx_oword_t *filter,
                                  struct efx_farch_filter_spec *spec)
@@ -495,8 +452,8 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
 
        switch (efx_farch_filter_spec_table_id(spec)) {
        case EFX_FARCH_FILTER_TABLE_RX_IP: {
-               bool is_udp = (spec->type == EFX_FILTER_UDP_FULL ||
-                              spec->type == EFX_FILTER_UDP_WILD);
+               bool is_udp = (spec->type == EFX_FARCH_FILTER_UDP_FULL ||
+                              spec->type == EFX_FARCH_FILTER_UDP_WILD);
                EFX_POPULATE_OWORD_7(
                        *filter,
                        FRF_BZ_RSS_EN,
@@ -513,7 +470,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
        }
 
        case EFX_FARCH_FILTER_TABLE_RX_MAC: {
-               bool is_wild = spec->type == EFX_FILTER_MAC_WILD;
+               bool is_wild = spec->type == EFX_FARCH_FILTER_MAC_WILD;
                EFX_POPULATE_OWORD_7(
                        *filter,
                        FRF_CZ_RMFT_RSS_EN,
@@ -530,7 +487,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
        }
 
        case EFX_FARCH_FILTER_TABLE_TX_MAC: {
-               bool is_wild = spec->type == EFX_FILTER_MAC_WILD;
+               bool is_wild = spec->type == EFX_FARCH_FILTER_MAC_WILD;
                EFX_POPULATE_OWORD_5(*filter,
                                     FRF_CZ_TMFT_TXQ_ID, spec->dmaq_id,
                                     FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
@@ -573,15 +530,15 @@ static bool efx_farch_filter_equal(const struct efx_farch_filter_spec *left,
 
 #define EFX_FARCH_FILTER_MATCH_PRI_COUNT       5
 
-static const u8 efx_farch_filter_type_match_pri[EFX_FILTER_TYPE_COUNT] = {
-       [EFX_FILTER_TCP_FULL]   = 0,
-       [EFX_FILTER_UDP_FULL]   = 0,
-       [EFX_FILTER_TCP_WILD]   = 1,
-       [EFX_FILTER_UDP_WILD]   = 1,
-       [EFX_FILTER_MAC_FULL]   = 2,
-       [EFX_FILTER_MAC_WILD]   = 3,
-       [EFX_FILTER_UC_DEF]     = 4,
-       [EFX_FILTER_MC_DEF]     = 4,
+static const u8 efx_farch_filter_type_match_pri[EFX_FARCH_FILTER_TYPE_COUNT] = {
+       [EFX_FARCH_FILTER_TCP_FULL]     = 0,
+       [EFX_FARCH_FILTER_UDP_FULL]     = 0,
+       [EFX_FARCH_FILTER_TCP_WILD]     = 1,
+       [EFX_FARCH_FILTER_UDP_WILD]     = 1,
+       [EFX_FARCH_FILTER_MAC_FULL]     = 2,
+       [EFX_FARCH_FILTER_MAC_WILD]     = 3,
+       [EFX_FARCH_FILTER_UC_DEF]       = 4,
+       [EFX_FARCH_FILTER_MC_DEF]       = 4,
 };
 
 static const enum efx_farch_filter_table_id efx_farch_filter_range_table[] = {
@@ -672,11 +629,12 @@ s32 efx_filter_insert_filter(struct efx_nic *efx,
        unsigned int depth = 0;
        int rc;
 
-       /* XXX efx_farch_filter_spec and efx_filter_spec will diverge in future */
-       memcpy(&spec, gen_spec, sizeof(*gen_spec));
+       rc = efx_farch_filter_from_gen_spec(&spec, gen_spec);
+       if (rc)
+               return rc;
 
-       table = efx_farch_filter_spec_table(state, &spec);
-       if (!table || table->size == 0)
+       table = &state->table[efx_farch_filter_spec_table_id(&spec)];
+       if (table->size == 0)
                return -EINVAL;
 
        netif_vdbg(efx, hw, efx->net_dev,
@@ -687,8 +645,8 @@ s32 efx_filter_insert_filter(struct efx_nic *efx,
                /* One filter spec per type */
                BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_UC_DEF != 0);
                BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_MC_DEF !=
-                            EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
-               rep_index = spec.type - EFX_FILTER_UC_DEF;
+                            EFX_FARCH_FILTER_MC_DEF - EFX_FARCH_FILTER_UC_DEF);
+               rep_index = spec.type - EFX_FARCH_FILTER_UC_DEF;
                ins_index = rep_index;
 
                spin_lock_bh(&state->lock);
@@ -911,8 +869,7 @@ int efx_filter_get_filter_safe(struct efx_nic *efx,
 
        if (test_bit(filter_idx, table->used_bitmap) &&
            spec->priority == priority) {
-               /* XXX efx_farch_filter_spec and efx_filter_spec will diverge */
-               memcpy(spec_buf, spec, sizeof(*spec));
+               efx_farch_filter_to_gen_spec(spec_buf, spec);
                rc = 0;
        } else {
                rc = -ENOENT;
index b1170d44171e257388141f1b1c9f62e0d6cf288c..1b410defccecc9a35e445b52ca4fad53e6efef2a 100644 (file)
 #define EFX_FILTER_H
 
 #include <linux/types.h>
+#include <linux/if_ether.h>
+#include <asm/byteorder.h>
 
 /**
- * enum efx_filter_type - type of hardware filter
- * @EFX_FILTER_TCP_FULL: Matching TCP/IPv4 4-tuple
- * @EFX_FILTER_TCP_WILD: Matching TCP/IPv4 destination (host, port)
- * @EFX_FILTER_UDP_FULL: Matching UDP/IPv4 4-tuple
- * @EFX_FILTER_UDP_WILD: Matching UDP/IPv4 destination (host, port)
- * @EFX_FILTER_MAC_FULL: Matching Ethernet destination MAC address, VID
- * @EFX_FILTER_MAC_WILD: Matching Ethernet destination MAC address
- * @EFX_FILTER_UC_DEF: Matching all otherwise unmatched unicast
- * @EFX_FILTER_MC_DEF: Matching all otherwise unmatched multicast
- * @EFX_FILTER_UNSPEC: Match type is unspecified
+ * enum efx_filter_match_flags - Flags for hardware filter match type
+ * @EFX_FILTER_MATCH_REM_HOST: Match by remote IP host address
+ * @EFX_FILTER_MATCH_LOC_HOST: Match by local IP host address
+ * @EFX_FILTER_MATCH_REM_MAC: Match by remote MAC address
+ * @EFX_FILTER_MATCH_REM_PORT: Match by remote TCP/UDP port
+ * @EFX_FILTER_MATCH_LOC_MAC: Match by local MAC address
+ * @EFX_FILTER_MATCH_LOC_PORT: Match by local TCP/UDP port
+ * @EFX_FILTER_MATCH_ETHER_TYPE: Match by Ether-type
+ * @EFX_FILTER_MATCH_INNER_VID: Match by inner VLAN ID
+ * @EFX_FILTER_MATCH_OUTER_VID: Match by outer VLAN ID
+ * @EFX_FILTER_MATCH_IP_PROTO: Match by IP transport protocol
+ * @EFX_FILTER_MATCH_LOC_MAC_IG: Match by local MAC address I/G bit.
+ *     Used for RX default unicast and multicast/broadcast filters.
  *
- * Falcon NICs only support the TCP/IPv4 and UDP/IPv4 filter types.
+ * Only some combinations are supported, depending on NIC type:
+ *
+ * - Falcon supports RX filters matching by {TCP,UDP}/IPv4 4-tuple or
+ *   local 2-tuple (only implemented for Falcon B0)
+ *
+ * - Siena supports RX and TX filters matching by {TCP,UDP}/IPv4 4-tuple
+ *   or local 2-tuple, or local MAC with or without outer VID, and RX
+ *   default filters
+ *
+ * - Huntington supports filter matching controlled by firmware, potentially
+ *   using {TCP,UDP}/IPv{4,6} 4-tuple or local 2-tuple, local MAC or I/G bit,
+ *   with or without outer and inner VID
  */
-enum efx_filter_type {
-       EFX_FILTER_TCP_FULL = 0,
-       EFX_FILTER_TCP_WILD,
-       EFX_FILTER_UDP_FULL,
-       EFX_FILTER_UDP_WILD,
-       EFX_FILTER_MAC_FULL = 4,
-       EFX_FILTER_MAC_WILD,
-       EFX_FILTER_UC_DEF = 8,
-       EFX_FILTER_MC_DEF,
-       EFX_FILTER_TYPE_COUNT,          /* number of specific types */
-       EFX_FILTER_UNSPEC = 0xf,
+enum efx_filter_match_flags {
+       EFX_FILTER_MATCH_REM_HOST =     0x0001,
+       EFX_FILTER_MATCH_LOC_HOST =     0x0002,
+       EFX_FILTER_MATCH_REM_MAC =      0x0004,
+       EFX_FILTER_MATCH_REM_PORT =     0x0008,
+       EFX_FILTER_MATCH_LOC_MAC =      0x0010,
+       EFX_FILTER_MATCH_LOC_PORT =     0x0020,
+       EFX_FILTER_MATCH_ETHER_TYPE =   0x0040,
+       EFX_FILTER_MATCH_INNER_VID =    0x0080,
+       EFX_FILTER_MATCH_OUTER_VID =    0x0100,
+       EFX_FILTER_MATCH_IP_PROTO =     0x0200,
+       EFX_FILTER_MATCH_LOC_MAC_IG =   0x0400,
 };
 
 /**
@@ -73,29 +90,55 @@ enum efx_filter_flags {
 
 /**
  * struct efx_filter_spec - specification for a hardware filter
- * @type: Type of match to be performed, from &enum efx_filter_type
+ * @match_flags: Match type flags, from &enum efx_filter_match_flags
  * @priority: Priority of the filter, from &enum efx_filter_priority
  * @flags: Miscellaneous flags, from &enum efx_filter_flags
+ * @rss_context: RSS context to use, if %EFX_FILTER_FLAG_RX_RSS is set
  * @dmaq_id: Source/target queue index, or %EFX_FILTER_RX_DMAQ_ID_DROP for
  *     an RX drop filter
- * @data: Match data (type-dependent)
+ * @outer_vid: Outer VLAN ID to match, if %EFX_FILTER_MATCH_OUTER_VID is set
+ * @inner_vid: Inner VLAN ID to match, if %EFX_FILTER_MATCH_INNER_VID is set
+ * @loc_mac: Local MAC address to match, if %EFX_FILTER_MATCH_LOC_MAC or
+ *     %EFX_FILTER_MATCH_LOC_MAC_IG is set
+ * @rem_mac: Remote MAC address to match, if %EFX_FILTER_MATCH_REM_MAC is set
+ * @ether_type: Ether-type to match, if %EFX_FILTER_MATCH_ETHER_TYPE is set
+ * @ip_proto: IP transport protocol to match, if %EFX_FILTER_MATCH_IP_PROTO
+ *     is set
+ * @loc_host: Local IP host to match, if %EFX_FILTER_MATCH_LOC_HOST is set
+ * @rem_host: Remote IP host to match, if %EFX_FILTER_MATCH_REM_HOST is set
+ * @loc_port: Local TCP/UDP port to match, if %EFX_FILTER_MATCH_LOC_PORT is set
+ * @rem_port: Remote TCP/UDP port to match, if %EFX_FILTER_MATCH_REM_PORT is set
  *
- * Use the efx_filter_set_*() functions to initialise the @type and
- * @data fields.
+ * The efx_filter_init_rx() or efx_filter_init_tx() function *must* be
+ * used to initialise the structure.  The efx_filter_set_*() functions
+ * may then be used to set @rss_context, @match_flags and related
+ * fields.
  *
  * The @priority field is used by software to determine whether a new
  * filter may replace an old one.  The hardware priority of a filter
- * depends on the filter type.
+ * depends on which fields are matched.
  */
 struct efx_filter_spec {
-       u8      type:4;
-       u8      priority:4;
-       u8      flags;
-       u16     dmaq_id;
-       u32     data[3];
+       u32     match_flags:12;
+       u32     priority:2;
+       u32     flags:6;
+       u32     dmaq_id:12;
+       u32     rss_context;
+       __be16  outer_vid __aligned(4); /* allow jhash2() of match values */
+       __be16  inner_vid;
+       u8      loc_mac[ETH_ALEN];
+       u8      rem_mac[ETH_ALEN];
+       __be16  ether_type;
+       u8      ip_proto;
+       __be32  loc_host[4];
+       __be32  rem_host[4];
+       __be16  loc_port;
+       __be16  rem_port;
+       /* total 64 bytes */
 };
 
 enum {
+       EFX_FILTER_RSS_CONTEXT_DEFAULT = 0xffffffff,
        EFX_FILTER_RX_DMAQ_ID_DROP = 0xfff
 };
 
@@ -104,39 +147,116 @@ static inline void efx_filter_init_rx(struct efx_filter_spec *spec,
                                      enum efx_filter_flags flags,
                                      unsigned rxq_id)
 {
-       spec->type = EFX_FILTER_UNSPEC;
+       memset(spec, 0, sizeof(*spec));
        spec->priority = priority;
        spec->flags = EFX_FILTER_FLAG_RX | flags;
+       spec->rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT;
        spec->dmaq_id = rxq_id;
 }
 
 static inline void efx_filter_init_tx(struct efx_filter_spec *spec,
                                      unsigned txq_id)
 {
-       spec->type = EFX_FILTER_UNSPEC;
+       memset(spec, 0, sizeof(*spec));
        spec->priority = EFX_FILTER_PRI_REQUIRED;
        spec->flags = EFX_FILTER_FLAG_TX;
        spec->dmaq_id = txq_id;
 }
 
-extern int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
-                                    __be32 host, __be16 port);
-extern int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec,
-                                    u8 *proto, __be32 *host, __be16 *port);
-extern int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
-                                   __be32 host, __be16 port,
-                                   __be32 rhost, __be16 rport);
-extern int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec,
-                                   u8 *proto, __be32 *host, __be16 *port,
-                                   __be32 *rhost, __be16 *rport);
-extern int efx_filter_set_eth_local(struct efx_filter_spec *spec,
-                                   u16 vid, const u8 *addr);
-extern int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
-                                   u16 *vid, u8 *addr);
-extern int efx_filter_set_uc_def(struct efx_filter_spec *spec);
-extern int efx_filter_set_mc_def(struct efx_filter_spec *spec);
+/**
+ * efx_filter_set_ipv4_local - specify IPv4 host, transport protocol and port
+ * @spec: Specification to initialise
+ * @proto: Transport layer protocol number
+ * @host: Local host address (network byte order)
+ * @port: Local port (network byte order)
+ */
+static inline int
+efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
+                         __be32 host, __be16 port)
+{
+       spec->match_flags |=
+               EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
+       spec->ether_type = htons(ETH_P_IP);
+       spec->ip_proto = proto;
+       spec->loc_host[0] = host;
+       spec->loc_port = port;
+       return 0;
+}
+
+/**
+ * efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
+ * @spec: Specification to initialise
+ * @proto: Transport layer protocol number
+ * @lhost: Local host address (network byte order)
+ * @lport: Local port (network byte order)
+ * @rhost: Remote host address (network byte order)
+ * @rport: Remote port (network byte order)
+ */
+static inline int
+efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
+                        __be32 lhost, __be16 lport,
+                        __be32 rhost, __be16 rport)
+{
+       spec->match_flags |=
+               EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+               EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
+       spec->ether_type = htons(ETH_P_IP);
+       spec->ip_proto = proto;
+       spec->loc_host[0] = lhost;
+       spec->loc_port = lport;
+       spec->rem_host[0] = rhost;
+       spec->rem_port = rport;
+       return 0;
+}
+
 enum {
        EFX_FILTER_VID_UNSPEC = 0xffff,
 };
 
+/**
+ * efx_filter_set_eth_local - specify local Ethernet address and/or VID
+ * @spec: Specification to initialise
+ * @vid: Outer VLAN ID to match, or %EFX_FILTER_VID_UNSPEC
+ * @addr: Local Ethernet MAC address, or %NULL
+ */
+static inline int efx_filter_set_eth_local(struct efx_filter_spec *spec,
+                                          u16 vid, const u8 *addr)
+{
+       if (vid == EFX_FILTER_VID_UNSPEC && addr == NULL)
+               return -EINVAL;
+
+       if (vid != EFX_FILTER_VID_UNSPEC) {
+               spec->match_flags |= EFX_FILTER_MATCH_OUTER_VID;
+               spec->outer_vid = htons(vid);
+       }
+       if (addr != NULL) {
+               spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC;
+               memcpy(spec->loc_mac, addr, ETH_ALEN);
+       }
+       return 0;
+}
+
+/**
+ * efx_filter_set_uc_def - specify matching otherwise-unmatched unicast
+ * @spec: Specification to initialise
+ */
+static inline int efx_filter_set_uc_def(struct efx_filter_spec *spec)
+{
+       spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
+       return 0;
+}
+
+/**
+ * efx_filter_set_mc_def - specify matching otherwise-unmatched multicast
+ * @spec: Specification to initialise
+ */
+static inline int efx_filter_set_mc_def(struct efx_filter_spec *spec)
+{
+       spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
+       spec->loc_mac[0] = 1;
+       return 0;
+}
+
 #endif /* EFX_FILTER_H */