diff options
Diffstat (limited to 'drivers/net/ethernet/cisco/enic/enic_ethtool.c')
-rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_ethtool.c | 125 |
1 files changed, 109 insertions, 16 deletions
diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 2e50b5489d20..523c9ceb04c0 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -22,6 +22,7 @@ #include "enic_res.h" #include "enic.h" #include "enic_dev.h" +#include "enic_clsf.h" struct enic_stat { char name[ETH_GSTRING_LEN]; @@ -231,7 +232,7 @@ static int enic_set_coalesce(struct net_device *netdev, if (ecmd->use_adaptive_rx_coalesce || ecmd->rx_coalesce_usecs_low || ecmd->rx_coalesce_usecs_high) - return -EOPNOTSUPP; + return -EINVAL; intr = enic_legacy_io_intr(); vnic_intr_coalescing_timer_set(&enic->intr[intr], @@ -243,34 +244,29 @@ static int enic_set_coalesce(struct net_device *netdev, if (ecmd->use_adaptive_rx_coalesce || ecmd->rx_coalesce_usecs_low || ecmd->rx_coalesce_usecs_high) - return -EOPNOTSUPP; + return -EINVAL; vnic_intr_coalescing_timer_set(&enic->intr[0], tx_coalesce_usecs); break; case VNIC_DEV_INTR_MODE_MSIX: + if (ecmd->rx_coalesce_usecs_high && + (rx_coalesce_usecs_high < + rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) + return -EINVAL; + for (i = 0; i < enic->wq_count; i++) { intr = enic_msix_wq_intr(enic, i); vnic_intr_coalescing_timer_set(&enic->intr[intr], tx_coalesce_usecs); } - if (rxcoal->use_adaptive_rx_coalesce) { - if (!ecmd->use_adaptive_rx_coalesce) { - rxcoal->use_adaptive_rx_coalesce = 0; - enic_intr_coal_set_rx(enic, rx_coalesce_usecs); - } - } else { - if (ecmd->use_adaptive_rx_coalesce) - rxcoal->use_adaptive_rx_coalesce = 1; - else - enic_intr_coal_set_rx(enic, rx_coalesce_usecs); - } + rxcoal->use_adaptive_rx_coalesce = + !!ecmd->use_adaptive_rx_coalesce; + if (!rxcoal->use_adaptive_rx_coalesce) + enic_intr_coal_set_rx(enic, rx_coalesce_usecs); if (ecmd->rx_coalesce_usecs_high) { - if (rx_coalesce_usecs_high < - (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) - return -EINVAL; rxcoal->range_end = rx_coalesce_usecs_high; rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + @@ -287,6 +283,102 @@ static int enic_set_coalesce(struct net_device *netdev, return 0; } +static int enic_grxclsrlall(struct enic *enic, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + int j, ret = 0, cnt = 0; + + cmd->data = enic->rfs_h.max - enic->rfs_h.free; + for (j = 0; j < (1 << ENIC_RFS_FLW_BITSHIFT); j++) { + struct hlist_head *hhead; + struct hlist_node *tmp; + struct enic_rfs_fltr_node *n; + + hhead = &enic->rfs_h.ht_head[j]; + hlist_for_each_entry_safe(n, tmp, hhead, node) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + rule_locs[cnt] = n->fltr_id; + cnt++; + } + } + cmd->rule_cnt = cnt; + + return ret; +} + +static int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + struct enic_rfs_fltr_node *n; + + n = htbl_fltr_search(enic, (u16)fsp->location); + if (!n) + return -EINVAL; + switch (n->keys.ip_proto) { + case IPPROTO_TCP: + fsp->flow_type = TCP_V4_FLOW; + break; + case IPPROTO_UDP: + fsp->flow_type = UDP_V4_FLOW; + break; + default: + return -EINVAL; + break; + } + + fsp->h_u.tcp_ip4_spec.ip4src = n->keys.src; + fsp->m_u.tcp_ip4_spec.ip4src = (__u32)~0; + + fsp->h_u.tcp_ip4_spec.ip4dst = n->keys.dst; + fsp->m_u.tcp_ip4_spec.ip4dst = (__u32)~0; + + fsp->h_u.tcp_ip4_spec.psrc = n->keys.port16[0]; + fsp->m_u.tcp_ip4_spec.psrc = (__u16)~0; + + fsp->h_u.tcp_ip4_spec.pdst = n->keys.port16[1]; + fsp->m_u.tcp_ip4_spec.pdst = (__u16)~0; + + fsp->ring_cookie = n->rq_id; + + return 0; +} + +static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct enic *enic = netdev_priv(dev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = enic->rq_count; + break; + case ETHTOOL_GRXCLSRLCNT: + spin_lock_bh(&enic->rfs_h.lock); + cmd->rule_cnt = enic->rfs_h.max - enic->rfs_h.free; + cmd->data = enic->rfs_h.max; + spin_unlock_bh(&enic->rfs_h.lock); + break; + case ETHTOOL_GRXCLSRLALL: + spin_lock_bh(&enic->rfs_h.lock); + ret = enic_grxclsrlall(enic, cmd, rule_locs); + spin_unlock_bh(&enic->rfs_h.lock); + break; + case ETHTOOL_GRXCLSRULE: + spin_lock_bh(&enic->rfs_h.lock); + ret = enic_grxclsrule(enic, cmd); + spin_unlock_bh(&enic->rfs_h.lock); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + static const struct ethtool_ops enic_ethtool_ops = { .get_settings = enic_get_settings, .get_drvinfo = enic_get_drvinfo, @@ -298,6 +390,7 @@ static const struct ethtool_ops enic_ethtool_ops = { .get_ethtool_stats = enic_get_ethtool_stats, .get_coalesce = enic_get_coalesce, .set_coalesce = enic_set_coalesce, + .get_rxnfc = enic_get_rxnfc, }; void enic_set_ethtool_ops(struct net_device *netdev) |