diff options
Diffstat (limited to 'drivers/net/ixgbe/ixgbe_ethtool.c')
-rw-r--r-- | drivers/net/ixgbe/ixgbe_ethtool.c | 589 |
1 files changed, 331 insertions, 258 deletions
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index cb1555bc854..dc649553a0a 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -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) |