diff options
Diffstat (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_main.c')
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_main.c | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c index 9fcab646cbd5..8a1da044e908 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c @@ -12,11 +12,13 @@ #include "aq_ethtool.h" #include "aq_ptp.h" #include "aq_filters.h" +#include "aq_hw_utils.h" #include <linux/netdevice.h> #include <linux/module.h> #include <linux/ip.h> #include <linux/udp.h> +#include <net/pkt_cls.h> MODULE_LICENSE("GPL v2"); MODULE_AUTHOR(AQ_CFG_DRV_AUTHOR); @@ -38,7 +40,7 @@ struct net_device *aq_ndev_alloc(void) struct net_device *ndev = NULL; struct aq_nic_s *aq_nic = NULL; - ndev = alloc_etherdev_mq(sizeof(struct aq_nic_s), AQ_CFG_VECS_MAX); + ndev = alloc_etherdev_mq(sizeof(struct aq_nic_s), AQ_HW_QUEUES_MAX); if (!ndev) return NULL; @@ -330,6 +332,73 @@ static int aq_ndo_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, return 0; } +static int aq_validate_mqprio_opt(struct aq_nic_s *self, + struct tc_mqprio_qopt_offload *mqprio, + const unsigned int num_tc) +{ + const bool has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE); + struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(self); + const unsigned int tcs_max = min_t(u8, aq_nic_cfg->aq_hw_caps->tcs_max, + AQ_CFG_TCS_MAX); + + if (num_tc > tcs_max) { + netdev_err(self->ndev, "Too many TCs requested\n"); + return -EOPNOTSUPP; + } + + if (num_tc != 0 && !is_power_of_2(num_tc)) { + netdev_err(self->ndev, "TC count should be power of 2\n"); + return -EOPNOTSUPP; + } + + if (has_min_rate && !ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ANTIGUA)) { + netdev_err(self->ndev, "Min tx rate is not supported\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int aq_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct tc_mqprio_qopt_offload *mqprio = type_data; + struct aq_nic_s *aq_nic = netdev_priv(dev); + bool has_min_rate; + bool has_max_rate; + int err; + int i; + + if (type != TC_SETUP_QDISC_MQPRIO) + return -EOPNOTSUPP; + + has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE); + has_max_rate = !!(mqprio->flags & TC_MQPRIO_F_MAX_RATE); + + err = aq_validate_mqprio_opt(aq_nic, mqprio, mqprio->qopt.num_tc); + if (err) + return err; + + for (i = 0; i < mqprio->qopt.num_tc; i++) { + if (has_max_rate) { + u64 max_rate = mqprio->max_rate[i]; + + do_div(max_rate, AQ_MBPS_DIVISOR); + aq_nic_setup_tc_max_rate(aq_nic, i, (u32)max_rate); + } + + if (has_min_rate) { + u64 min_rate = mqprio->min_rate[i]; + + do_div(min_rate, AQ_MBPS_DIVISOR); + aq_nic_setup_tc_min_rate(aq_nic, i, (u32)min_rate); + } + } + + return aq_nic_setup_tc_mqprio(aq_nic, mqprio->qopt.num_tc, + mqprio->qopt.prio_tc_map); +} + static const struct net_device_ops aq_ndev_ops = { .ndo_open = aq_ndev_open, .ndo_stop = aq_ndev_close, @@ -341,6 +410,7 @@ static const struct net_device_ops aq_ndev_ops = { .ndo_do_ioctl = aq_ndev_ioctl, .ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid, + .ndo_setup_tc = aq_ndo_setup_tc, }; static int __init aq_ndev_init_module(void) |