sfc: Implement ethtool RX NFC rules API instead of n-tuple API
authorBen Hutchings <bhutchings@solarflare.com>
Tue, 3 Jan 2012 12:05:47 +0000 (12:05 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Jan 2012 19:10:18 +0000 (14:10 -0500)
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/ethtool.c

index 1be51b2bfa42ae2f7eeb322b29bdb26128458dde..29b2ebfef19f21d85bb61fca9abfe086b88774b0 100644 (file)
@@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
        return efx_reset(efx, rc);
 }
 
+static int efx_ethtool_get_class_rule(struct efx_nic *efx,
+                                     struct ethtool_rx_flow_spec *rule)
+{
+       struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
+       struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
+       struct efx_filter_spec spec;
+       u16 vid;
+       u8 proto;
+       int rc;
+
+       rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL,
+                                       rule->location, &spec);
+       if (rc)
+               return rc;
+
+       if (spec.dmaq_id == 0xfff)
+               rule->ring_cookie = RX_CLS_FLOW_DISC;
+       else
+               rule->ring_cookie = spec.dmaq_id;
+
+       rc = efx_filter_get_eth_local(&spec, &vid,
+                                     rule->h_u.ether_spec.h_dest);
+       if (rc == 0) {
+               rule->flow_type = ETHER_FLOW;
+               memset(rule->m_u.ether_spec.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);
+               }
+               return 0;
+       }
+
+       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->ip4src, &ip_entry->psrc,
+                       &ip_entry->ip4dst, &ip_entry->pdst);
+               EFX_WARN_ON_PARANOID(rc);
+               ip_mask->ip4src = ~0;
+               ip_mask->psrc = ~0;
+       }
+       rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW;
+       ip_mask->ip4dst = ~0;
+       ip_mask->pdst = ~0;
+       return rc;
+}
+
 static int
 efx_ethtool_get_rxnfc(struct net_device *net_dev,
-                     struct ethtool_rxnfc *info, u32 *rules __always_unused)
+                     struct ethtool_rxnfc *info, u32 *rule_locs)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
@@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
                return 0;
        }
 
+       case ETHTOOL_GRXCLSRLCNT:
+               info->data = efx_filter_get_rx_id_limit(efx);
+               if (info->data == 0)
+                       return -EOPNOTSUPP;
+               info->data |= RX_CLS_LOC_SPECIAL;
+               info->rule_cnt =
+                       efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL);
+               return 0;
+
+       case ETHTOOL_GRXCLSRULE:
+               if (efx_filter_get_rx_id_limit(efx) == 0)
+                       return -EOPNOTSUPP;
+               return efx_ethtool_get_class_rule(efx, &info->fs);
+
+       case ETHTOOL_GRXCLSRLALL: {
+               s32 rc;
+               info->data = efx_filter_get_rx_id_limit(efx);
+               if (info->data == 0)
+                       return -EOPNOTSUPP;
+               rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL,
+                                          rule_locs, info->rule_cnt);
+               if (rc < 0)
+                       return rc;
+               info->rule_cnt = rc;
+               return 0;
+       }
+
        default:
                return -EOPNOTSUPP;
        }
 }
 
-static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
-                                    struct ethtool_rx_ntuple *ntuple)
+static int efx_ethtool_set_class_rule(struct efx_nic *efx,
+                                     struct ethtool_rx_flow_spec *rule)
 {
-       struct efx_nic *efx = netdev_priv(net_dev);
-       struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec;
-       struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec;
-       struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec;
-       struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec;
-       struct efx_filter_spec filter;
+       struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
+       struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
+       struct ethhdr *mac_entry = &rule->h_u.ether_spec;
+       struct ethhdr *mac_mask = &rule->m_u.ether_spec;
+       struct efx_filter_spec spec;
        int rc;
 
-       /* Range-check action */
-       if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR ||
-           ntuple->fs.action >= (s32)efx->n_rx_channels)
+       /* Check that user wants us to choose the location */
+       if (rule->location != RX_CLS_LOC_ANY &&
+           rule->location != RX_CLS_LOC_FIRST &&
+           rule->location != RX_CLS_LOC_LAST)
                return -EINVAL;
 
-       if (~ntuple->fs.data_mask)
+       /* Range-check ring_cookie */
+       if (rule->ring_cookie >= efx->n_rx_channels &&
+           rule->ring_cookie != RX_CLS_FLOW_DISC)
                return -EINVAL;
 
-       efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0,
-                          (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ?
-                          0xfff : ntuple->fs.action);
+       /* Check for unsupported extensions */
+       if ((rule->flow_type & FLOW_EXT) &&
+           (rule->m_ext.vlan_etype | rule->m_ext.data[0] |
+            rule->m_ext.data[1]))
+               return -EINVAL;
+
+       efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL,
+                          (rule->location == RX_CLS_LOC_FIRST) ?
+                          EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0,
+                          (rule->ring_cookie == RX_CLS_FLOW_DISC) ?
+                          0xfff : rule->ring_cookie);
 
-       switch (ntuple->fs.flow_type) {
+       switch (rule->flow_type) {
        case TCP_V4_FLOW:
        case UDP_V4_FLOW: {
-               u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ?
+               u8 proto = (rule->flow_type == TCP_V4_FLOW ?
                            IPPROTO_TCP : IPPROTO_UDP);
 
                /* Must match all of destination, */
-               if (ip_mask->ip4dst | ip_mask->pdst)
+               if ((__force u32)~ip_mask->ip4dst |
+                   (__force u16)~ip_mask->pdst)
                        return -EINVAL;
                /* all or none of source, */
                if ((ip_mask->ip4src | ip_mask->psrc) &&
@@ -905,17 +992,17 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
                     (__force u16)~ip_mask->psrc))
                        return -EINVAL;
                /* and nothing else */
-               if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask)
+               if (ip_mask->tos | rule->m_ext.vlan_tci)
                        return -EINVAL;
 
-               if (!ip_mask->ip4src)
-                       rc = efx_filter_set_ipv4_full(&filter, proto,
+               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(&filter, proto,
+                       rc = efx_filter_set_ipv4_local(&spec, proto,
                                                       ip_entry->ip4dst,
                                                       ip_entry->pdst);
                if (rc)
@@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
                break;
        }
 
-       case ETHER_FLOW:
-               /* Must match all of destination, */
-               if (!is_zero_ether_addr(mac_mask->h_dest))
+       case ETHER_FLOW | FLOW_EXT:
+               /* Must match all or none of VID */
+               if (rule->m_ext.vlan_tci != htons(0xfff) &&
+                   rule->m_ext.vlan_tci != 0)
                        return -EINVAL;
-               /* all or none of VID, */
-               if (ntuple->fs.vlan_tag_mask != 0xf000 &&
-                   ntuple->fs.vlan_tag_mask != 0xffff)
+       case ETHER_FLOW:
+               /* Must match all of destination */
+               if (!is_broadcast_ether_addr(mac_mask->h_dest))
                        return -EINVAL;
                /* and nothing else */
-               if (!is_broadcast_ether_addr(mac_mask->h_source) ||
-                   mac_mask->h_proto != htons(0xffff))
+               if (!is_zero_ether_addr(mac_mask->h_source) ||
+                   mac_mask->h_proto)
                        return -EINVAL;
 
                rc = efx_filter_set_eth_local(
-                       &filter,
-                       (ntuple->fs.vlan_tag_mask == 0xf000) ?
-                       ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC,
+                       &spec,
+                       (rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ?
+                       ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC,
                        mac_entry->h_dest);
                if (rc)
                        return rc;
@@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
                return -EINVAL;
        }
 
-       if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR)
-               return efx_filter_remove_filter(efx, &filter);
+       rc = efx_filter_insert_filter(efx, &spec, true);
+       if (rc < 0)
+               return rc;
 
-       rc = efx_filter_insert_filter(efx, &filter, true);
-       return rc < 0 ? rc : 0;
+       rule->location = rc;
+       return 0;
+}
+
+static int efx_ethtool_set_rxnfc(struct net_device *net_dev,
+                                struct ethtool_rxnfc *info)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+
+       if (efx_filter_get_rx_id_limit(efx) == 0)
+               return -EOPNOTSUPP;
+
+       switch (info->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               return efx_ethtool_set_class_rule(efx, &info->fs);
+
+       case ETHTOOL_SRXCLSRLDEL:
+               return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL,
+                                                info->fs.location);
+
+       default:
+               return -EOPNOTSUPP;
+       }
 }
 
 static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
@@ -1007,7 +1117,7 @@ const struct ethtool_ops efx_ethtool_ops = {
        .set_wol                = efx_ethtool_set_wol,
        .reset                  = efx_ethtool_reset,
        .get_rxnfc              = efx_ethtool_get_rxnfc,
-       .set_rx_ntuple          = efx_ethtool_set_rx_ntuple,
+       .set_rxnfc              = efx_ethtool_set_rxnfc,
        .get_rxfh_indir_size    = efx_ethtool_get_rxfh_indir_size,
        .get_rxfh_indir         = efx_ethtool_get_rxfh_indir,
        .set_rxfh_indir         = efx_ethtool_set_rxfh_indir,