Commit a83fe6b6 authored by Dmitry Bezrukov's avatar Dmitry Bezrukov Committed by David S. Miller
Browse files

net: atlantic: QoS implementation: multi-TC support



This patch adds multi-TC support.

PTP is automatically disabled when the user enables more than 2 TCs,
otherwise traffic on TC2 won't quite work, because it's reserved for PTP.

Signed-off-by: default avatarDmitry Bezrukov <dbezrukov@marvell.com>
Co-developed-by: default avatarDmitry Bogdanov <dbogdanov@marvell.com>
Signed-off-by: default avatarDmitry Bogdanov <dbogdanov@marvell.com>
Co-developed-by: default avatarMark Starovoytov <mstarovoitov@marvell.com>
Signed-off-by: default avatarMark Starovoytov <mstarovoitov@marvell.com>
Signed-off-by: default avatarIgor Russkikh <irusskikh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0aa7bc3e
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -153,6 +153,8 @@ aq_check_approve_fvlan(struct aq_nic_s *aq_nic,
		       struct aq_hw_rx_fltrs_s *rx_fltrs,
		       struct ethtool_rx_flow_spec *fsp)
{
	struct aq_nic_cfg_s *cfg = &aq_nic->aq_nic_cfg;

	if (fsp->location < AQ_RX_FIRST_LOC_FVLANID ||
	    fsp->location > AQ_RX_LAST_LOC_FVLANID) {
		netdev_err(aq_nic->ndev,
@@ -170,10 +172,10 @@ aq_check_approve_fvlan(struct aq_nic_s *aq_nic,
		return -EINVAL;
	}

	if (fsp->ring_cookie > aq_nic->aq_nic_cfg.num_rss_queues) {
	if (fsp->ring_cookie > cfg->num_rss_queues * cfg->tcs) {
		netdev_err(aq_nic->ndev,
			   "ethtool: queue number must be in range [0, %d]",
			   aq_nic->aq_nic_cfg.num_rss_queues - 1);
			   cfg->num_rss_queues * cfg->tcs - 1);
		return -EINVAL;
	}
	return 0;
@@ -262,6 +264,7 @@ static bool __must_check
aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
		       struct ethtool_rx_flow_spec *fsp)
{
	struct aq_nic_cfg_s *cfg = &aq_nic->aq_nic_cfg;
	bool rule_is_not_correct = false;

	if (!aq_nic) {
@@ -274,11 +277,11 @@ aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
	} else if (aq_check_filter(aq_nic, fsp)) {
		rule_is_not_correct = true;
	} else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
		if (fsp->ring_cookie >= aq_nic->aq_nic_cfg.num_rss_queues) {
		if (fsp->ring_cookie >= cfg->num_rss_queues * cfg->tcs) {
			netdev_err(aq_nic->ndev,
				   "ethtool: The specified action is invalid.\n"
				   "Maximum allowable value action is %u.\n",
				   aq_nic->aq_nic_cfg.num_rss_queues - 1);
				   cfg->num_rss_queues * cfg->tcs - 1);
			rule_is_not_correct = true;
		}
	}
+1 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ struct aq_stats_s {
#define AQ_HW_TXD_MULTIPLE 8U
#define AQ_HW_RXD_MULTIPLE 8U

#define AQ_HW_QUEUES_MAX                32U
#define AQ_HW_MULTICAST_ADDRESS_MAX     32U

#define AQ_HW_PTP_TC                    2U
+26 −0
Original line number Diff line number Diff line
@@ -79,3 +79,29 @@ int aq_hw_err_from_flags(struct aq_hw_s *hw)
err_exit:
	return err;
}

int aq_hw_num_tcs(struct aq_hw_s *hw)
{
	switch (hw->aq_nic_cfg->tc_mode) {
	case AQ_TC_MODE_8TCS:
		return 8;
	case AQ_TC_MODE_4TCS:
		return 4;
	default:
		break;
	}

	return 1;
}

int aq_hw_q_per_tc(struct aq_hw_s *hw)
{
	switch (hw->aq_nic_cfg->tc_mode) {
	case AQ_TC_MODE_8TCS:
		return 4;
	case AQ_TC_MODE_4TCS:
		return 8;
	default:
		return 4;
	}
}
+2 −0
Original line number Diff line number Diff line
@@ -34,5 +34,7 @@ u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg);
void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value);
u64 aq_hw_read_reg64(struct aq_hw_s *hw, u32 reg);
int aq_hw_err_from_flags(struct aq_hw_s *hw);
int aq_hw_num_tcs(struct aq_hw_s *hw);
int aq_hw_q_per_tc(struct aq_hw_s *hw);

#endif /* AQ_HW_UTILS_H */
+38 −1
Original line number Diff line number Diff line
@@ -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,40 @@ 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,
				  const unsigned int num_tc)
{
	if (num_tc > aq_hw_num_tcs(self->aq_hw)) {
		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;
	}

	return 0;
}

static int aq_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type,
			   void *type_data)
{
	struct aq_nic_s *aq_nic = netdev_priv(dev);
	struct tc_mqprio_qopt *mqprio = type_data;
	int err;

	if (type != TC_SETUP_QDISC_MQPRIO)
		return -EOPNOTSUPP;

	err = aq_validate_mqprio_opt(aq_nic, mqprio->num_tc);
	if (err)
		return err;

	return aq_nic_setup_tc_mqprio(aq_nic, mqprio->num_tc,
				      mqprio->prio_tc_map);
}

static const struct net_device_ops aq_ndev_ops = {
	.ndo_open = aq_ndev_open,
	.ndo_stop = aq_ndev_close,
@@ -341,6 +377,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)
Loading