Commit b7b871f5 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'ethtool-mm-api-consolidation'

Vladimir Oltean says:

====================
ethtool mm API consolidation

This series consolidates the behavior of the 2 drivers that implement
the ethtool MAC Merge layer by making NXP ENETC commit its preemptible
traffic classes to hardware only when MM TX is active (same as Ocelot).

Then, after resolving an issue with the ENETC driver, it restricts user
space from entering 2 states which don't make sense:

- pmac-enabled off tx-enabled on  verify-enabled *
- pmac-enabled *   tx-enabled off verify-enabled on

Then, it introduces a selftest (ethtool_mm.sh) which puts everything
together and tests all valid configurations known to me.

This is simultaneously the v2 of "[PATCH net-next 0/2] ethtool mm API
improvements":
https://lore.kernel.org/netdev/20230415173454.3970647-1-vladimir.oltean@nxp.com/
which had caused some problems to openlldp. Those were solved in the
meantime, see:
https://github.com/intel/openlldp/commit/11171b474f6f3cbccac5d608b7f26b32ff72c651

and of "[RFC PATCH net-next] selftests: forwarding: add a test for MAC
Merge layer":
https://lore.kernel.org/netdev/20230210221243.228932-1-vladimir.oltean@nxp.com/
====================

Link: https://lore.kernel.org/r/20230418111459.811553-1-vladimir.oltean@nxp.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 787e6144 e6991384
Loading
Loading
Loading
Loading
+6 −17
Original line number Diff line number Diff line
@@ -25,23 +25,12 @@ void enetc_port_mac_wr(struct enetc_si *si, u32 reg, u32 val)
}
EXPORT_SYMBOL_GPL(enetc_port_mac_wr);

void enetc_set_ptcfpr(struct enetc_hw *hw, unsigned long preemptible_tcs)
static void enetc_change_preemptible_tcs(struct enetc_ndev_priv *priv,
					 u8 preemptible_tcs)
{
	u32 val;
	int tc;

	for (tc = 0; tc < 8; tc++) {
		val = enetc_port_rd(hw, ENETC_PTCFPR(tc));

		if (preemptible_tcs & BIT(tc))
			val |= ENETC_PTCFPR_FPE;
		else
			val &= ~ENETC_PTCFPR_FPE;

		enetc_port_wr(hw, ENETC_PTCFPR(tc), val);
	}
	priv->preemptible_tcs = preemptible_tcs;
	enetc_mm_commit_preemptible_tcs(priv);
}
EXPORT_SYMBOL_GPL(enetc_set_ptcfpr);

static int enetc_num_stack_tx_queues(struct enetc_ndev_priv *priv)
{
@@ -2659,7 +2648,7 @@ static void enetc_reset_tc_mqprio(struct net_device *ndev)

	enetc_debug_tx_ring_prios(priv);

	enetc_set_ptcfpr(hw, 0);
	enetc_change_preemptible_tcs(priv, 0);
}

int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)
@@ -2714,7 +2703,7 @@ int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)

	enetc_debug_tx_ring_prios(priv);

	enetc_set_ptcfpr(hw, mqprio->preemptible_tcs);
	enetc_change_preemptible_tcs(priv, mqprio->preemptible_tcs);

	return 0;

+4 −1
Original line number Diff line number Diff line
@@ -355,6 +355,9 @@ struct enetc_ndev_priv {
	u16 rx_bd_count, tx_bd_count;

	u16 msg_enable;

	u8 preemptible_tcs;

	enum enetc_active_offloads active_offloads;

	u32 speed; /* store speed for compare update pspeed */
@@ -433,6 +436,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
/* ethtool */
void enetc_set_ethtool_ops(struct net_device *ndev);
void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link);
void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv);

/* control buffer descriptor ring (CBDR) */
int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
@@ -486,7 +490,6 @@ static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size,

void enetc_reset_ptcmsdur(struct enetc_hw *hw);
void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu);
void enetc_set_ptcfpr(struct enetc_hw *hw, unsigned long preemptible_tcs);

#ifdef CONFIG_FSL_ENETC_QOS
int enetc_qos_query_caps(struct net_device *ndev, void *type_data);
+89 −5
Original line number Diff line number Diff line
@@ -32,6 +32,12 @@ static const u32 enetc_port_regs[] = {
	ENETC_PM0_CMD_CFG, ENETC_PM0_MAXFRM, ENETC_PM0_IF_MODE
};

static const u32 enetc_port_mm_regs[] = {
	ENETC_MMCSR, ENETC_PFPMR, ENETC_PTCFPR(0), ENETC_PTCFPR(1),
	ENETC_PTCFPR(2), ENETC_PTCFPR(3), ENETC_PTCFPR(4), ENETC_PTCFPR(5),
	ENETC_PTCFPR(6), ENETC_PTCFPR(7),
};

static int enetc_get_reglen(struct net_device *ndev)
{
	struct enetc_ndev_priv *priv = netdev_priv(ndev);
@@ -45,6 +51,9 @@ static int enetc_get_reglen(struct net_device *ndev)
	if (hw->port)
		len += ARRAY_SIZE(enetc_port_regs);

	if (hw->port && !!(priv->si->hw_features & ENETC_SI_F_QBU))
		len += ARRAY_SIZE(enetc_port_mm_regs);

	len *= sizeof(u32) * 2; /* store 2 entries per reg: addr and value */

	return len;
@@ -90,6 +99,14 @@ static void enetc_get_regs(struct net_device *ndev, struct ethtool_regs *regs,
		*buf++ = addr;
		*buf++ = enetc_rd(hw, addr);
	}

	if (priv->si->hw_features & ENETC_SI_F_QBU) {
		for (i = 0; i < ARRAY_SIZE(enetc_port_mm_regs); i++) {
			addr = ENETC_PORT_BASE + enetc_port_mm_regs[i];
			*buf++ = addr;
			*buf++ = enetc_rd(hw, addr);
		}
	}
}

static const struct {
@@ -976,7 +993,9 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)
	lafs = ENETC_MMCSR_GET_LAFS(val);
	state->rx_min_frag_size = ethtool_mm_frag_size_add_to_min(lafs);
	state->tx_enabled = !!(val & ENETC_MMCSR_LPE); /* mirror of MMCSR_ME */
	state->tx_active = !!(val & ENETC_MMCSR_LPA);
	state->tx_active = state->tx_enabled &&
			   (state->verify_status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED ||
			    state->verify_status == ETHTOOL_MM_VERIFY_STATUS_DISABLED);
	state->verify_enabled = !(val & ENETC_MMCSR_VDIS);
	state->verify_time = ENETC_MMCSR_GET_VT(val);
	/* A verifyTime of 128 ms would exceed the 7 bit width
@@ -989,6 +1008,64 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)
	return 0;
}

static int enetc_mm_wait_tx_active(struct enetc_hw *hw, int verify_time)
{
	int timeout = verify_time * USEC_PER_MSEC * ENETC_MM_VERIFY_RETRIES;
	u32 val;

	/* This will time out after the standard value of 3 verification
	 * attempts. To not sleep forever, it relies on a non-zero verify_time,
	 * guarantee which is provided by the ethtool nlattr policy.
	 */
	return read_poll_timeout(enetc_port_rd, val,
				 ENETC_MMCSR_GET_VSTS(val) == 3,
				 ENETC_MM_VERIFY_SLEEP_US, timeout,
				 true, hw, ENETC_MMCSR);
}

static void enetc_set_ptcfpr(struct enetc_hw *hw, u8 preemptible_tcs)
{
	u32 val;
	int tc;

	for (tc = 0; tc < 8; tc++) {
		val = enetc_port_rd(hw, ENETC_PTCFPR(tc));

		if (preemptible_tcs & BIT(tc))
			val |= ENETC_PTCFPR_FPE;
		else
			val &= ~ENETC_PTCFPR_FPE;

		enetc_port_wr(hw, ENETC_PTCFPR(tc), val);
	}
}

/* ENETC does not have an IRQ to notify changes to the MAC Merge TX status
 * (active/inactive), but the preemptible traffic classes should only be
 * committed to hardware once TX is active. Resort to polling.
 */
void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv)
{
	struct enetc_hw *hw = &priv->si->hw;
	u8 preemptible_tcs = 0;
	u32 val;
	int err;

	val = enetc_port_rd(hw, ENETC_MMCSR);
	if (!(val & ENETC_MMCSR_ME))
		goto out;

	if (!(val & ENETC_MMCSR_VDIS)) {
		err = enetc_mm_wait_tx_active(hw, ENETC_MMCSR_GET_VT(val));
		if (err)
			goto out;
	}

	preemptible_tcs = priv->preemptible_tcs;
out:
	enetc_set_ptcfpr(hw, preemptible_tcs);
}

/* FIXME: Workaround for the link partner's verification failing if ENETC
 * priorly received too much express traffic. The documentation doesn't
 * suggest this is needed.
@@ -1041,10 +1118,13 @@ static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,
	else
		priv->active_offloads &= ~ENETC_F_QBU;

	/* If link is up, enable MAC Merge right away */
	if (!!(priv->active_offloads & ENETC_F_QBU) &&
	    !(val & ENETC_MMCSR_LINK_FAIL))
	/* If link is up, enable/disable MAC Merge right away */
	if (!(val & ENETC_MMCSR_LINK_FAIL)) {
		if (!!(priv->active_offloads & ENETC_F_QBU))
			val |= ENETC_MMCSR_ME;
		else
			val &= ~ENETC_MMCSR_ME;
	}

	val &= ~ENETC_MMCSR_VT_MASK;
	val |= ENETC_MMCSR_VT(cfg->verify_time);
@@ -1056,6 +1136,8 @@ static int enetc_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,

	enetc_restart_emac_rx(priv->si);

	enetc_mm_commit_preemptible_tcs(priv);

	mutex_unlock(&priv->mm_lock);

	return 0;
@@ -1089,6 +1171,8 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link)

	enetc_port_wr(hw, ENETC_MMCSR, val);

	enetc_mm_commit_preemptible_tcs(priv);

	mutex_unlock(&priv->mm_lock);
}
EXPORT_SYMBOL_GPL(enetc_mm_link_state_update);
+3 −0
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@

#include <linux/bitops.h>

#define ENETC_MM_VERIFY_SLEEP_US	USEC_PER_MSEC
#define ENETC_MM_VERIFY_RETRIES		3

/* ENETC device IDs */
#define ENETC_DEV_ID_PF		0xe100
#define ENETC_DEV_ID_VF		0xef00
+10 −0
Original line number Diff line number Diff line
@@ -214,6 +214,16 @@ static int ethnl_set_mm(struct ethnl_req_info *req_info, struct genl_info *info)
		return -ERANGE;
	}

	if (cfg.verify_enabled && !cfg.tx_enabled) {
		NL_SET_ERR_MSG(extack, "Verification requires TX enabled");
		return -EINVAL;
	}

	if (cfg.tx_enabled && !cfg.pmac_enabled) {
		NL_SET_ERR_MSG(extack, "TX enabled requires pMAC enabled");
		return -EINVAL;
	}

	ret = dev->ethtool_ops->set_mm(dev, &cfg, extack);
	return ret < 0 ? ret : 1;
}
Loading