Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / net / ixgbe / ixgbe_ethtool.c
index cb1555b..dc64955 100644 (file)
@@ -27,6 +27,7 @@
 
 /* ethtool support for ixgbe */
 
+#include <linux/interrupt.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -441,62 +442,6 @@ static int ixgbe_set_pauseparam(struct net_device *netdev,
        return 0;
 }
 
-static u32 ixgbe_get_rx_csum(struct net_device *netdev)
-{
-       struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       return adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED;
-}
-
-static int ixgbe_set_rx_csum(struct net_device *netdev, u32 data)
-{
-       struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       if (data)
-               adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
-       else
-               adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
-
-       return 0;
-}
-
-static u32 ixgbe_get_tx_csum(struct net_device *netdev)
-{
-       return (netdev->features & NETIF_F_IP_CSUM) != 0;
-}
-
-static int ixgbe_set_tx_csum(struct net_device *netdev, u32 data)
-{
-       struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       u32 feature_list;
-
-       feature_list = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
-       switch (adapter->hw.mac.type) {
-       case ixgbe_mac_82599EB:
-       case ixgbe_mac_X540:
-               feature_list |= NETIF_F_SCTP_CSUM;
-               break;
-       default:
-               break;
-       }
-       if (data)
-               netdev->features |= feature_list;
-       else
-               netdev->features &= ~feature_list;
-
-       return 0;
-}
-
-static int ixgbe_set_tso(struct net_device *netdev, u32 data)
-{
-       if (data) {
-               netdev->features |= NETIF_F_TSO;
-               netdev->features |= NETIF_F_TSO6;
-       } else {
-               netdev->features &= ~NETIF_F_TSO;
-               netdev->features &= ~NETIF_F_TSO6;
-       }
-       return 0;
-}
-
 static u32 ixgbe_get_msglevel(struct net_device *netdev)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
@@ -2055,7 +2000,7 @@ static int ixgbe_get_coalesce(struct net_device *netdev,
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
-       ec->tx_max_coalesced_frames_irq = adapter->tx_ring[0]->work_limit;
+       ec->tx_max_coalesced_frames_irq = adapter->tx_work_limit;
 
        /* only valid if in constant ITR mode */
        switch (adapter->rx_itr_setting) {
@@ -2074,7 +2019,7 @@ static int ixgbe_get_coalesce(struct net_device *netdev,
        }
 
        /* if in mixed tx/rx queues per vector mode, report only rx settings */
-       if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count)
+       if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count)
                return 0;
 
        /* only valid if in constant ITR mode */
@@ -2139,12 +2084,12 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
        bool need_reset = false;
 
        /* don't accept tx specific changes if we've got mixed RxTx vectors */
-       if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count
+       if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count
           && ec->tx_coalesce_usecs)
                return -EINVAL;
 
        if (ec->tx_max_coalesced_frames_irq)
-               adapter->tx_ring[0]->work_limit = ec->tx_max_coalesced_frames_irq;
+               adapter->tx_work_limit = ec->tx_max_coalesced_frames_irq;
 
        if (ec->rx_coalesce_usecs > 1) {
                /* check the limits */
@@ -2213,18 +2158,20 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
                int num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
                for (i = 0; i < num_vectors; i++) {
                        q_vector = adapter->q_vector[i];
-                       if (q_vector->txr_count && !q_vector->rxr_count)
+                       if (q_vector->tx.count && !q_vector->rx.count)
                                /* tx only */
                                q_vector->eitr = adapter->tx_eitr_param;
                        else
                                /* rx only or mixed */
                                q_vector->eitr = adapter->rx_eitr_param;
+                       q_vector->tx.work_limit = adapter->tx_work_limit;
                        ixgbe_write_eitr(q_vector);
                }
        /* Legacy Interrupt Mode */
        } else {
                q_vector = adapter->q_vector[0];
                q_vector->eitr = adapter->rx_eitr_param;
+               q_vector->tx.work_limit = adapter->tx_work_limit;
                ixgbe_write_eitr(q_vector);
        }
 
@@ -2233,241 +2180,376 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
         * correctly w.r.t stopping tx, and changing TXDCTL.WTHRESH settings
         * also locks in RSC enable/disable which requires reset
         */
-       if (need_reset) {
-               if (netif_running(netdev))
-                       ixgbe_reinit_locked(adapter);
-               else
-                       ixgbe_reset(adapter);
-       }
+       if (need_reset)
+               ixgbe_do_reset(netdev);
 
        return 0;
 }
 
-static int ixgbe_set_flags(struct net_device *netdev, u32 data)
+static int ixgbe_get_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+                                       struct ethtool_rxnfc *cmd)
 {
-       struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       bool need_reset = false;
-       int rc;
+       union ixgbe_atr_input *mask = &adapter->fdir_mask;
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct hlist_node *node, *node2;
+       struct ixgbe_fdir_filter *rule = NULL;
+
+       /* report total rule count */
+       cmd->data = (1024 << adapter->fdir_pballoc) - 2;
+
+       hlist_for_each_entry_safe(rule, node, node2,
+                                 &adapter->fdir_filter_list, fdir_node) {
+               if (fsp->location <= rule->sw_idx)
+                       break;
+       }
 
-#ifdef CONFIG_IXGBE_DCB
-       if ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) &&
-           !(data & ETH_FLAG_RXVLAN))
+       if (!rule || fsp->location != rule->sw_idx)
                return -EINVAL;
-#endif
 
-       need_reset = (data & ETH_FLAG_RXVLAN) !=
-                    (netdev->features & NETIF_F_HW_VLAN_RX);
+       /* fill out the flow spec entry */
 
-       if ((data & ETH_FLAG_RXHASH) &&
-           !(adapter->flags & IXGBE_FLAG_RSS_ENABLED))
-               return -EOPNOTSUPP;
-
-       rc = ethtool_op_set_flags(netdev, data, ETH_FLAG_LRO | ETH_FLAG_NTUPLE |
-                                 ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
-                                 ETH_FLAG_RXHASH);
-       if (rc)
-               return rc;
-
-       /* if state changes we need to update adapter->flags and reset */
-       if ((adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE) &&
-           (!!(data & ETH_FLAG_LRO) !=
-            !!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED))) {
-               if ((data & ETH_FLAG_LRO) &&
-                   (!adapter->rx_itr_setting ||
-                    (adapter->rx_itr_setting > IXGBE_MAX_RSC_INT_RATE))) {
-                       e_info(probe, "rx-usecs set too low, "
-                                     "not enabling RSC.\n");
-               } else {
-                       adapter->flags2 ^= IXGBE_FLAG2_RSC_ENABLED;
-                       switch (adapter->hw.mac.type) {
-                       case ixgbe_mac_82599EB:
-                               need_reset = true;
-                               break;
-                       case ixgbe_mac_X540: {
-                               int i;
-                               for (i = 0; i < adapter->num_rx_queues; i++) {
-                                       struct ixgbe_ring *ring =
-                                                         adapter->rx_ring[i];
-                                       if (adapter->flags2 &
-                                           IXGBE_FLAG2_RSC_ENABLED) {
-                                               ixgbe_configure_rscctl(adapter,
-                                                                      ring);
-                                       } else {
-                                               ixgbe_clear_rscctl(adapter,
-                                                                  ring);
-                                       }
-                               }
-                       }
-                               break;
-                       default:
-                               break;
-                       }
-               }
+       /* set flow type field */
+       switch (rule->filter.formatted.flow_type) {
+       case IXGBE_ATR_FLOW_TYPE_TCPV4:
+               fsp->flow_type = TCP_V4_FLOW;
+               break;
+       case IXGBE_ATR_FLOW_TYPE_UDPV4:
+               fsp->flow_type = UDP_V4_FLOW;
+               break;
+       case IXGBE_ATR_FLOW_TYPE_SCTPV4:
+               fsp->flow_type = SCTP_V4_FLOW;
+               break;
+       case IXGBE_ATR_FLOW_TYPE_IPV4:
+               fsp->flow_type = IP_USER_FLOW;
+               fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+               fsp->h_u.usr_ip4_spec.proto = 0;
+               fsp->m_u.usr_ip4_spec.proto = 0;
+               break;
+       default:
+               return -EINVAL;
        }
 
-       /*
-        * Check if Flow Director n-tuple support was enabled or disabled.  If
-        * the state changed, we need to reset.
-        */
-       if ((adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE) &&
-           (!(data & ETH_FLAG_NTUPLE))) {
-               /* turn off Flow Director perfect, set hash and reset */
-               adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
-               adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
-               need_reset = true;
-       } else if ((!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) &&
-                  (data & ETH_FLAG_NTUPLE)) {
-               /* turn off Flow Director hash, enable perfect and reset */
-               adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE;
-               adapter->flags |= IXGBE_FLAG_FDIR_PERFECT_CAPABLE;
-               need_reset = true;
-       } else {
-               /* no state change */
-       }
+       fsp->h_u.tcp_ip4_spec.psrc = rule->filter.formatted.src_port;
+       fsp->m_u.tcp_ip4_spec.psrc = mask->formatted.src_port;
+       fsp->h_u.tcp_ip4_spec.pdst = rule->filter.formatted.dst_port;
+       fsp->m_u.tcp_ip4_spec.pdst = mask->formatted.dst_port;
+       fsp->h_u.tcp_ip4_spec.ip4src = rule->filter.formatted.src_ip[0];
+       fsp->m_u.tcp_ip4_spec.ip4src = mask->formatted.src_ip[0];
+       fsp->h_u.tcp_ip4_spec.ip4dst = rule->filter.formatted.dst_ip[0];
+       fsp->m_u.tcp_ip4_spec.ip4dst = mask->formatted.dst_ip[0];
+       fsp->h_ext.vlan_tci = rule->filter.formatted.vlan_id;
+       fsp->m_ext.vlan_tci = mask->formatted.vlan_id;
+       fsp->h_ext.vlan_etype = rule->filter.formatted.flex_bytes;
+       fsp->m_ext.vlan_etype = mask->formatted.flex_bytes;
+       fsp->h_ext.data[1] = htonl(rule->filter.formatted.vm_pool);
+       fsp->m_ext.data[1] = htonl(mask->formatted.vm_pool);
+       fsp->flow_type |= FLOW_EXT;
+
+       /* record action */
+       if (rule->action == IXGBE_FDIR_DROP_QUEUE)
+               fsp->ring_cookie = RX_CLS_FLOW_DISC;
+       else
+               fsp->ring_cookie = rule->action;
 
-       if (need_reset) {
-               if (netif_running(netdev))
-                       ixgbe_reinit_locked(adapter);
-               else
-                       ixgbe_reset(adapter);
+       return 0;
+}
+
+static int ixgbe_get_ethtool_fdir_all(struct ixgbe_adapter *adapter,
+                                     struct ethtool_rxnfc *cmd,
+                                     u32 *rule_locs)
+{
+       struct hlist_node *node, *node2;
+       struct ixgbe_fdir_filter *rule;
+       int cnt = 0;
+
+       /* report total rule count */
+       cmd->data = (1024 << adapter->fdir_pballoc) - 2;
+
+       hlist_for_each_entry_safe(rule, node, node2,
+                                 &adapter->fdir_filter_list, fdir_node) {
+               if (cnt == cmd->rule_cnt)
+                       return -EMSGSIZE;
+               rule_locs[cnt] = rule->sw_idx;
+               cnt++;
        }
 
        return 0;
 }
 
-static int ixgbe_set_rx_ntuple(struct net_device *dev,
-                               struct ethtool_rx_ntuple *cmd)
+static int ixgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+                          void *rule_locs)
 {
        struct ixgbe_adapter *adapter = netdev_priv(dev);
-       struct ethtool_rx_ntuple_flow_spec *fs = &cmd->fs;
-       union ixgbe_atr_input input_struct;
-       struct ixgbe_atr_input_masks input_masks;
-       int target_queue;
-       int err;
+       int ret = -EOPNOTSUPP;
 
-       if (adapter->hw.mac.type == ixgbe_mac_82598EB)
-               return -EOPNOTSUPP;
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = adapter->num_rx_queues;
+               ret = 0;
+               break;
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = adapter->fdir_filter_count;
+               ret = 0;
+               break;
+       case ETHTOOL_GRXCLSRULE:
+               ret = ixgbe_get_ethtool_fdir_entry(adapter, cmd);
+               break;
+       case ETHTOOL_GRXCLSRLALL:
+               ret = ixgbe_get_ethtool_fdir_all(adapter, cmd,
+                                                (u32 *)rule_locs);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int ixgbe_update_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+                                          struct ixgbe_fdir_filter *input,
+                                          u16 sw_idx)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       struct hlist_node *node, *node2, *parent;
+       struct ixgbe_fdir_filter *rule;
+       int err = -EINVAL;
+
+       parent = NULL;
+       rule = NULL;
+
+       hlist_for_each_entry_safe(rule, node, node2,
+                                 &adapter->fdir_filter_list, fdir_node) {
+               /* hash found, or no matching entry */
+               if (rule->sw_idx >= sw_idx)
+                       break;
+               parent = node;
+       }
+
+       /* if there is an old rule occupying our place remove it */
+       if (rule && (rule->sw_idx == sw_idx)) {
+               if (!input || (rule->filter.formatted.bkt_hash !=
+                              input->filter.formatted.bkt_hash)) {
+                       err = ixgbe_fdir_erase_perfect_filter_82599(hw,
+                                                               &rule->filter,
+                                                               sw_idx);
+               }
+
+               hlist_del(&rule->fdir_node);
+               kfree(rule);
+               adapter->fdir_filter_count--;
+       }
 
        /*
-        * Don't allow programming if the action is a queue greater than
-        * the number of online Tx queues.
+        * If no input this was a delete, err should be 0 if a rule was
+        * successfully found and removed from the list else -EINVAL
         */
-       if ((fs->action >= adapter->num_tx_queues) ||
-           (fs->action < ETHTOOL_RXNTUPLE_ACTION_DROP))
-               return -EINVAL;
+       if (!input)
+               return err;
 
-       memset(&input_struct, 0, sizeof(union ixgbe_atr_input));
-       memset(&input_masks, 0, sizeof(struct ixgbe_atr_input_masks));
+       /* initialize node and set software index */
+       INIT_HLIST_NODE(&input->fdir_node);
 
-       /* record flow type */
-       switch (fs->flow_type) {
-       case IPV4_FLOW:
-               input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV4;
-               break;
+       /* add filter to the list */
+       if (parent)
+               hlist_add_after(parent, &input->fdir_node);
+       else
+               hlist_add_head(&input->fdir_node,
+                              &adapter->fdir_filter_list);
+
+       /* update counts */
+       adapter->fdir_filter_count++;
+
+       return 0;
+}
+
+static int ixgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,
+                                      u8 *flow_type)
+{
+       switch (fsp->flow_type & ~FLOW_EXT) {
        case TCP_V4_FLOW:
-               input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
+               *flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
                break;
        case UDP_V4_FLOW:
-               input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
+               *flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
                break;
        case SCTP_V4_FLOW:
-               input_struct.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
+               *flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
                break;
-       default:
-               return -1;
-       }
-
-       /* copy vlan tag minus the CFI bit */
-       if ((fs->vlan_tag & 0xEFFF) || (~fs->vlan_tag_mask & 0xEFFF)) {
-               input_struct.formatted.vlan_id = htons(fs->vlan_tag & 0xEFFF);
-               if (!fs->vlan_tag_mask) {
-                       input_masks.vlan_id_mask = htons(0xEFFF);
-               } else {
-                       switch (~fs->vlan_tag_mask & 0xEFFF) {
-                       /* all of these are valid vlan-mask values */
-                       case 0xEFFF:
-                       case 0xE000:
-                       case 0x0FFF:
-                       case 0x0000:
-                               input_masks.vlan_id_mask =
-                                       htons(~fs->vlan_tag_mask);
+       case IP_USER_FLOW:
+               switch (fsp->h_u.usr_ip4_spec.proto) {
+               case IPPROTO_TCP:
+                       *flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
+                       break;
+               case IPPROTO_UDP:
+                       *flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
+                       break;
+               case IPPROTO_SCTP:
+                       *flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
+                       break;
+               case 0:
+                       if (!fsp->m_u.usr_ip4_spec.proto) {
+                               *flow_type = IXGBE_ATR_FLOW_TYPE_IPV4;
                                break;
-                       /* exit with error if vlan-mask is invalid */
-                       default:
-                               e_err(drv, "Partial VLAN ID or "
-                                     "priority mask in vlan-mask is not "
-                                     "supported by hardware\n");
-                               return -1;
                        }
+               default:
+                       return 0;
                }
+               break;
+       default:
+               return 0;
        }
 
-       /* make sure we only use the first 2 bytes of user data */
-       if ((fs->data & 0xFFFF) || (~fs->data_mask & 0xFFFF)) {
-               input_struct.formatted.flex_bytes = htons(fs->data & 0xFFFF);
-               if (!(fs->data_mask & 0xFFFF)) {
-                       input_masks.flex_mask = 0xFFFF;
-               } else if (~fs->data_mask & 0xFFFF) {
-                       e_err(drv, "Partial user-def-mask is not "
-                             "supported by hardware\n");
-                       return -1;
-               }
-       }
+       return 1;
+}
+
+static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+                                       struct ethtool_rxnfc *cmd)
+{
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct ixgbe_hw *hw = &adapter->hw;
+       struct ixgbe_fdir_filter *input;
+       union ixgbe_atr_input mask;
+       int err;
+
+       if (!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))
+               return -EOPNOTSUPP;
 
        /*
-        * Copy input into formatted structures
-        *
-        * These assignments are based on the following logic
-        * If neither input or mask are set assume value is masked out.
-        * If input is set, but mask is not mask should default to accept all.
-        * If input is not set, but mask is set then mask likely results in 0.
-        * If input is set and mask is set then assign both.
+        * Don't allow programming if the action is a queue greater than
+        * the number of online Rx queues.
         */
-       if (fs->h_u.tcp_ip4_spec.ip4src || ~fs->m_u.tcp_ip4_spec.ip4src) {
-               input_struct.formatted.src_ip[0] = fs->h_u.tcp_ip4_spec.ip4src;
-               if (!fs->m_u.tcp_ip4_spec.ip4src)
-                       input_masks.src_ip_mask[0] = 0xFFFFFFFF;
-               else
-                       input_masks.src_ip_mask[0] =
-                               ~fs->m_u.tcp_ip4_spec.ip4src;
-       }
-       if (fs->h_u.tcp_ip4_spec.ip4dst || ~fs->m_u.tcp_ip4_spec.ip4dst) {
-               input_struct.formatted.dst_ip[0] = fs->h_u.tcp_ip4_spec.ip4dst;
-               if (!fs->m_u.tcp_ip4_spec.ip4dst)
-                       input_masks.dst_ip_mask[0] = 0xFFFFFFFF;
-               else
-                       input_masks.dst_ip_mask[0] =
-                               ~fs->m_u.tcp_ip4_spec.ip4dst;
+       if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
+           (fsp->ring_cookie >= adapter->num_rx_queues))
+               return -EINVAL;
+
+       /* Don't allow indexes to exist outside of available space */
+       if (fsp->location >= ((1024 << adapter->fdir_pballoc) - 2)) {
+               e_err(drv, "Location out of range\n");
+               return -EINVAL;
        }
-       if (fs->h_u.tcp_ip4_spec.psrc || ~fs->m_u.tcp_ip4_spec.psrc) {
-               input_struct.formatted.src_port = fs->h_u.tcp_ip4_spec.psrc;
-               if (!fs->m_u.tcp_ip4_spec.psrc)
-                       input_masks.src_port_mask = 0xFFFF;
-               else
-                       input_masks.src_port_mask = ~fs->m_u.tcp_ip4_spec.psrc;
+
+       input = kzalloc(sizeof(*input), GFP_ATOMIC);
+       if (!input)
+               return -ENOMEM;
+
+       memset(&mask, 0, sizeof(union ixgbe_atr_input));
+
+       /* set SW index */
+       input->sw_idx = fsp->location;
+
+       /* record flow type */
+       if (!ixgbe_flowspec_to_flow_type(fsp,
+                                        &input->filter.formatted.flow_type)) {
+               e_err(drv, "Unrecognized flow type\n");
+               goto err_out;
        }
-       if (fs->h_u.tcp_ip4_spec.pdst || ~fs->m_u.tcp_ip4_spec.pdst) {
-               input_struct.formatted.dst_port = fs->h_u.tcp_ip4_spec.pdst;
-               if (!fs->m_u.tcp_ip4_spec.pdst)
-                       input_masks.dst_port_mask = 0xFFFF;
-               else
-                       input_masks.dst_port_mask = ~fs->m_u.tcp_ip4_spec.pdst;
+
+       mask.formatted.flow_type = IXGBE_ATR_L4TYPE_IPV6_MASK |
+                                  IXGBE_ATR_L4TYPE_MASK;
+
+       if (input->filter.formatted.flow_type == IXGBE_ATR_FLOW_TYPE_IPV4)
+               mask.formatted.flow_type &= IXGBE_ATR_L4TYPE_IPV6_MASK;
+
+       /* Copy input into formatted structures */
+       input->filter.formatted.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+       mask.formatted.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
+       input->filter.formatted.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+       mask.formatted.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
+       input->filter.formatted.src_port = fsp->h_u.tcp_ip4_spec.psrc;
+       mask.formatted.src_port = fsp->m_u.tcp_ip4_spec.psrc;
+       input->filter.formatted.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
+       mask.formatted.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
+
+       if (fsp->flow_type & FLOW_EXT) {
+               input->filter.formatted.vm_pool =
+                               (unsigned char)ntohl(fsp->h_ext.data[1]);
+               mask.formatted.vm_pool =
+                               (unsigned char)ntohl(fsp->m_ext.data[1]);
+               input->filter.formatted.vlan_id = fsp->h_ext.vlan_tci;
+               mask.formatted.vlan_id = fsp->m_ext.vlan_tci;
+               input->filter.formatted.flex_bytes =
+                                               fsp->h_ext.vlan_etype;
+               mask.formatted.flex_bytes = fsp->m_ext.vlan_etype;
        }
 
        /* determine if we need to drop or route the packet */
-       if (fs->action == ETHTOOL_RXNTUPLE_ACTION_DROP)
-               target_queue = MAX_RX_QUEUES - 1;
+       if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
+               input->action = IXGBE_FDIR_DROP_QUEUE;
        else
-               target_queue = fs->action;
+               input->action = fsp->ring_cookie;
+
+       spin_lock(&adapter->fdir_perfect_lock);
+
+       if (hlist_empty(&adapter->fdir_filter_list)) {
+               /* save mask and program input mask into HW */
+               memcpy(&adapter->fdir_mask, &mask, sizeof(mask));
+               err = ixgbe_fdir_set_input_mask_82599(hw, &mask);
+               if (err) {
+                       e_err(drv, "Error writing mask\n");
+                       goto err_out_w_lock;
+               }
+       } else if (memcmp(&adapter->fdir_mask, &mask, sizeof(mask))) {
+               e_err(drv, "Only one mask supported per port\n");
+               goto err_out_w_lock;
+       }
+
+       /* apply mask and compute/store hash */
+       ixgbe_atr_compute_perfect_hash_82599(&input->filter, &mask);
+
+       /* program filters to filter memory */
+       err = ixgbe_fdir_write_perfect_filter_82599(hw,
+                               &input->filter, input->sw_idx,
+                               (input->action == IXGBE_FDIR_DROP_QUEUE) ?
+                               IXGBE_FDIR_DROP_QUEUE :
+                               adapter->rx_ring[input->action]->reg_idx);
+       if (err)
+               goto err_out_w_lock;
+
+       ixgbe_update_ethtool_fdir_entry(adapter, input, input->sw_idx);
+
+       spin_unlock(&adapter->fdir_perfect_lock);
+
+       return err;
+err_out_w_lock:
+       spin_unlock(&adapter->fdir_perfect_lock);
+err_out:
+       kfree(input);
+       return -EINVAL;
+}
+
+static int ixgbe_del_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+                                       struct ethtool_rxnfc *cmd)
+{
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       int err;
 
        spin_lock(&adapter->fdir_perfect_lock);
-       err = ixgbe_fdir_add_perfect_filter_82599(&adapter->hw,
-                                                 &input_struct,
-                                                 &input_masks, 0,
-                                                 target_queue);
+       err = ixgbe_update_ethtool_fdir_entry(adapter, NULL, fsp->location);
        spin_unlock(&adapter->fdir_perfect_lock);
 
-       return err ? -1 : 0;
+       return err;
+}
+
+static int ixgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(dev);
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               ret = ixgbe_add_ethtool_fdir_entry(adapter, cmd);
+               break;
+       case ETHTOOL_SRXCLSRLDEL:
+               ret = ixgbe_del_ethtool_fdir_entry(adapter, cmd);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
 }
 
 static const struct ethtool_ops ixgbe_ethtool_ops = {
@@ -2486,16 +2568,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
        .set_ringparam          = ixgbe_set_ringparam,
        .get_pauseparam         = ixgbe_get_pauseparam,
        .set_pauseparam         = ixgbe_set_pauseparam,
-       .get_rx_csum            = ixgbe_get_rx_csum,
-       .set_rx_csum            = ixgbe_set_rx_csum,
-       .get_tx_csum            = ixgbe_get_tx_csum,
-       .set_tx_csum            = ixgbe_set_tx_csum,
-       .get_sg                 = ethtool_op_get_sg,
-       .set_sg                 = ethtool_op_set_sg,
        .get_msglevel           = ixgbe_get_msglevel,
        .set_msglevel           = ixgbe_set_msglevel,
-       .get_tso                = ethtool_op_get_tso,
-       .set_tso                = ixgbe_set_tso,
        .self_test              = ixgbe_diag_test,
        .get_strings            = ixgbe_get_strings,
        .set_phys_id            = ixgbe_set_phys_id,
@@ -2503,9 +2577,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
        .get_ethtool_stats      = ixgbe_get_ethtool_stats,
        .get_coalesce           = ixgbe_get_coalesce,
        .set_coalesce           = ixgbe_set_coalesce,
-       .get_flags              = ethtool_op_get_flags,
-       .set_flags              = ixgbe_set_flags,
-       .set_rx_ntuple          = ixgbe_set_rx_ntuple,
+       .get_rxnfc              = ixgbe_get_rxnfc,
+       .set_rxnfc              = ixgbe_set_rxnfc,
 };
 
 void ixgbe_set_ethtool_ops(struct net_device *netdev)