Commit f395b69f authored by Ioana Ciornei's avatar Ioana Ciornei Committed by David S. Miller
Browse files

dpaa2-eth: Add PFC support through DCB ops



Add support in dpaa2-eth for PFC (Priority Flow Control)
through the DCB ops.

Instruct the hardware to respond to received PFC frames.
Current firmware doesn't allow us to selectively enable PFC
on the Rx side for some priorities only, so we will react to
all incoming PFC frames (and stop transmitting on the traffic
classes specified in the frame).

Also, configure the hardware to generate PFC frames based on Rx
congestion notifications. When a certain number of frames accumulate in
the ingress queues corresponding to a traffic class, priority flow
control frames are generated for that TC.

The number of PFC traffic classes available can be queried through
lldptool. Also, which of those traffic classes have PFC enabled is also
controlled through the same dcbnl_rtnl_ops callbacks.

Signed-off-by: default avatarIoana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3f8b826d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -9,6 +9,16 @@ config FSL_DPAA2_ETH
	  The driver manages network objects discovered on the Freescale
	  MC bus.

if FSL_DPAA2_ETH
config FSL_DPAA2_ETH_DCB
	bool "Data Center Bridging (DCB) Support"
	default n
	depends on DCB
	help
	  Enable Priority-Based Flow Control (PFC) support for DPAA2 Ethernet
	  devices.
endif

config FSL_DPAA2_PTP_CLOCK
	tristate "Freescale DPAA2 PTP Clock"
	depends on FSL_DPAA2_ETH && PTP_1588_CLOCK_QORIQ
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o
obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)	+= fsl-dpaa2-ptp.o

fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o
fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o
fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
fsl-dpaa2-ptp-objs	:= dpaa2-ptp.o dprtc.o

+146 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2020 NXP */

#include "dpaa2-eth.h"

static int dpaa2_eth_dcbnl_ieee_getpfc(struct net_device *net_dev,
				       struct ieee_pfc *pfc)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	if (!(priv->link_state.options & DPNI_LINK_OPT_PFC_PAUSE))
		return 0;

	memcpy(pfc, &priv->pfc, sizeof(priv->pfc));
	pfc->pfc_cap = dpaa2_eth_tc_count(priv);

	return 0;
}

static inline bool is_prio_enabled(u8 pfc_en, u8 tc)
{
	return !!(pfc_en & (1 << tc));
}

static int set_pfc_cn(struct dpaa2_eth_priv *priv, u8 pfc_en)
{
	struct dpni_congestion_notification_cfg cfg = {0};
	int i, err;

	cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL;
	cfg.units = DPNI_CONGESTION_UNIT_FRAMES;
	cfg.message_iova = 0ULL;
	cfg.message_ctx = 0ULL;

	for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
		if (is_prio_enabled(pfc_en, i)) {
			cfg.threshold_entry = DPAA2_ETH_CN_THRESH_ENTRY(priv);
			cfg.threshold_exit = DPAA2_ETH_CN_THRESH_EXIT(priv);
		} else {
			/* For priorities not set in the pfc_en mask, we leave
			 * the congestion thresholds at zero, which effectively
			 * disables generation of PFC frames for them
			 */
			cfg.threshold_entry = 0;
			cfg.threshold_exit = 0;
		}

		err = dpni_set_congestion_notification(priv->mc_io, 0,
						       priv->mc_token,
						       DPNI_QUEUE_RX, i, &cfg);
		if (err) {
			netdev_err(priv->net_dev,
				   "dpni_set_congestion_notification failed\n");
			return err;
		}
	}

	return 0;
}

static int dpaa2_eth_dcbnl_ieee_setpfc(struct net_device *net_dev,
				       struct ieee_pfc *pfc)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
	struct dpni_link_cfg link_cfg = {0};
	int err;

	if (pfc->mbc || pfc->delay)
		return -EOPNOTSUPP;

	/* If same PFC enabled mask, nothing to do */
	if (priv->pfc.pfc_en == pfc->pfc_en)
		return 0;

	/* We allow PFC configuration even if it won't have any effect until
	 * general pause frames are enabled
	 */
	if (!dpaa2_eth_rx_pause_enabled(priv->link_state.options) ||
	    !dpaa2_eth_tx_pause_enabled(priv->link_state.options))
		netdev_warn(net_dev, "Pause support must be enabled in order for PFC to work!\n");

	link_cfg.rate = priv->link_state.rate;
	link_cfg.options = priv->link_state.options;
	if (pfc->pfc_en)
		link_cfg.options |= DPNI_LINK_OPT_PFC_PAUSE;
	else
		link_cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE;
	err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg);
	if (err) {
		netdev_err(net_dev, "dpni_set_link_cfg failed\n");
		return err;
	}

	/* Configure congestion notifications for the enabled priorities */
	err = set_pfc_cn(priv, pfc->pfc_en);
	if (err)
		return err;

	memcpy(&priv->pfc, pfc, sizeof(priv->pfc));

	return 0;
}

static u8 dpaa2_eth_dcbnl_getdcbx(struct net_device *net_dev)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	return priv->dcbx_mode;
}

static u8 dpaa2_eth_dcbnl_setdcbx(struct net_device *net_dev, u8 mode)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	return (mode != (priv->dcbx_mode)) ? 1 : 0;
}

static u8 dpaa2_eth_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	switch (capid) {
	case DCB_CAP_ATTR_PFC:
		*cap = true;
		break;
	case DCB_CAP_ATTR_PFC_TCS:
		*cap = 1 << (dpaa2_eth_tc_count(priv) - 1);
		break;
	case DCB_CAP_ATTR_DCBX:
		*cap = priv->dcbx_mode;
		break;
	default:
		*cap = false;
		break;
	}

	return 0;
}

const struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops = {
	.ieee_getpfc	= dpaa2_eth_dcbnl_ieee_getpfc,
	.ieee_setpfc	= dpaa2_eth_dcbnl_ieee_setpfc,
	.getdcbx	= dpaa2_eth_dcbnl_getdcbx,
	.setdcbx	= dpaa2_eth_dcbnl_setdcbx,
	.getcap		= dpaa2_eth_dcbnl_getcap,
};
+9 −0
Original line number Diff line number Diff line
@@ -3844,6 +3844,15 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
	if (err)
		goto err_alloc_rings;

#ifdef CONFIG_FSL_DPAA2_ETH_DCB
	if (dpaa2_eth_has_pause_support(priv) && priv->vlan_cls_enabled) {
		priv->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
		net_dev->dcbnl_ops = &dpaa2_eth_dcbnl_ops;
	} else {
		dev_dbg(dev, "PFC not supported\n");
	}
#endif

	err = setup_irqs(dpni_dev);
	if (err) {
		netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n");
+18 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#ifndef __DPAA2_ETH_H
#define __DPAA2_ETH_H

#include <linux/dcbnl.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <linux/fsl/mc.h>
@@ -65,6 +66,17 @@
#define DPAA2_ETH_CG_TAILDROP_THRESH(priv)				\
	(1024 * dpaa2_eth_queue_count(priv) / dpaa2_eth_tc_count(priv))

/* Congestion group notification threshold: when this many frames accumulate
 * on the Rx queues belonging to the same TC, the MAC is instructed to send
 * PFC frames for that TC.
 * When number of pending frames drops below exit threshold transmission of
 * PFC frames is stopped.
 */
#define DPAA2_ETH_CN_THRESH_ENTRY(priv) \
	(DPAA2_ETH_CG_TAILDROP_THRESH(priv) / 2)
#define DPAA2_ETH_CN_THRESH_EXIT(priv) \
	(DPAA2_ETH_CN_THRESH_ENTRY(priv) * 3 / 4)

/* Maximum number of buffers that can be acquired/released through a single
 * QBMan command
 */
@@ -436,6 +448,10 @@ struct dpaa2_eth_priv {
	struct dpaa2_eth_cls_rule *cls_rules;
	u8 rx_cls_enabled;
	u8 vlan_cls_enabled;
#ifdef CONFIG_FSL_DPAA2_ETH_DCB
	u8 dcbx_mode;
	struct ieee_pfc pfc;
#endif
	struct bpf_prog *xdp_prog;
#ifdef CONFIG_DEBUG_FS
	struct dpaa2_debugfs dbg;
@@ -568,4 +584,6 @@ int dpaa2_eth_cls_key_size(u64 key);
int dpaa2_eth_cls_fld_off(int prot, int field);
void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields);

extern const struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops;

#endif	/* __DPAA2_H */
Loading