You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8094 lines
230 KiB
8094 lines
230 KiB
From 6ec4d0cf0b0e5e41abc91012db4ebff7d415a92b Mon Sep 17 00:00:00 2001
|
|
From: Biwen Li <biwen.li@nxp.com>
|
|
Date: Tue, 30 Oct 2018 18:26:13 +0800
|
|
Subject: [PATCH 08/40] dpaa2-ethernet: support layerscape
|
|
This is an integrated patch of dpaa2-ethernet for
|
|
layerscape
|
|
|
|
Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
|
|
Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
Signed-off-by: Guanhua Gao <guanhua.gao@nxp.com>
|
|
Signed-off-by: Horia Geantă <horia.geanta@nxp.com>
|
|
Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
|
|
Signed-off-by: Nipun Gupta <nipun.gupta@nxp.com>
|
|
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
|
Signed-off-by: Biwen Li <biwen.li@nxp.com>
|
|
---
|
|
drivers/staging/fsl-dpaa2/Kconfig | 7 +
|
|
drivers/staging/fsl-dpaa2/ethernet/Makefile | 2 +
|
|
.../fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c | 1240 +++++++++
|
|
.../fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h | 183 ++
|
|
.../fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c | 357 +++
|
|
.../fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h | 60 +
|
|
.../staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 2335 +++++++++++++----
|
|
.../staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 388 ++-
|
|
.../fsl-dpaa2/ethernet/dpaa2-ethtool.c | 625 ++++-
|
|
drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h | 192 +-
|
|
drivers/staging/fsl-dpaa2/ethernet/dpni.c | 604 ++++-
|
|
drivers/staging/fsl-dpaa2/ethernet/dpni.h | 344 ++-
|
|
12 files changed, 5723 insertions(+), 614 deletions(-)
|
|
create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c
|
|
create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h
|
|
create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c
|
|
create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h
|
|
|
|
--- a/drivers/staging/fsl-dpaa2/Kconfig
|
|
+++ b/drivers/staging/fsl-dpaa2/Kconfig
|
|
@@ -17,6 +17,13 @@ config FSL_DPAA2_ETH
|
|
Ethernet driver for Freescale DPAA2 SoCs, using the
|
|
Freescale MC bus driver
|
|
|
|
+config FSL_DPAA2_ETH_CEETM
|
|
+ depends on NET_SCHED
|
|
+ bool "DPAA2 Ethernet CEETM QoS"
|
|
+ default n
|
|
+ ---help---
|
|
+ Enable QoS offloading support through the CEETM hardware block.
|
|
+
|
|
if FSL_DPAA2_ETH
|
|
config FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
bool "Enable Rx error queue"
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/Makefile
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/Makefile
|
|
@@ -5,6 +5,8 @@
|
|
obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o
|
|
|
|
fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o
|
|
+fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DEBUGFS} += dpaa2-eth-debugfs.o
|
|
+fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_CEETM} += dpaa2-eth-ceetm.o
|
|
|
|
# Needed by the tracing framework
|
|
CFLAGS_dpaa2-eth.o := -I$(src)
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c
|
|
@@ -0,0 +1,1240 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright 2017 NXP
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include "dpaa2-eth-ceetm.h"
|
|
+#include "dpaa2-eth.h"
|
|
+
|
|
+#define DPAA2_CEETM_DESCRIPTION "FSL DPAA2 CEETM qdisc"
|
|
+/* Conversion formula from userspace passed Bps to expected Mbit */
|
|
+#define dpaa2_eth_bps_to_mbit(rate) (rate >> 17)
|
|
+
|
|
+static const struct nla_policy dpaa2_ceetm_policy[DPAA2_CEETM_TCA_MAX] = {
|
|
+ [DPAA2_CEETM_TCA_COPT] = { .len = sizeof(struct dpaa2_ceetm_tc_copt) },
|
|
+ [DPAA2_CEETM_TCA_QOPS] = { .len = sizeof(struct dpaa2_ceetm_tc_qopt) },
|
|
+};
|
|
+
|
|
+struct Qdisc_ops dpaa2_ceetm_qdisc_ops;
|
|
+
|
|
+static inline int dpaa2_eth_set_ch_shaping(struct dpaa2_eth_priv *priv,
|
|
+ struct dpni_tx_shaping_cfg *scfg,
|
|
+ struct dpni_tx_shaping_cfg *ecfg,
|
|
+ int coupled, int ch_id)
|
|
+{
|
|
+ int err = 0;
|
|
+
|
|
+ netdev_dbg(priv->net_dev, "%s: ch_id %d rate %d mbps\n", __func__,
|
|
+ ch_id, scfg->rate_limit);
|
|
+ err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, scfg,
|
|
+ ecfg, coupled);
|
|
+ if (err)
|
|
+ netdev_err(priv->net_dev, "dpni_set_tx_shaping err\n");
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static inline int dpaa2_eth_reset_ch_shaping(struct dpaa2_eth_priv *priv,
|
|
+ int ch_id)
|
|
+{
|
|
+ struct dpni_tx_shaping_cfg cfg = { 0 };
|
|
+
|
|
+ return dpaa2_eth_set_ch_shaping(priv, &cfg, &cfg, 0, ch_id);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+dpaa2_eth_update_shaping_cfg(struct net_device *dev,
|
|
+ struct dpaa2_ceetm_shaping_cfg cfg,
|
|
+ struct dpni_tx_shaping_cfg *scfg,
|
|
+ struct dpni_tx_shaping_cfg *ecfg)
|
|
+{
|
|
+ scfg->rate_limit = dpaa2_eth_bps_to_mbit(cfg.cir);
|
|
+ ecfg->rate_limit = dpaa2_eth_bps_to_mbit(cfg.eir);
|
|
+
|
|
+ if (cfg.cbs > DPAA2_ETH_MAX_BURST_SIZE) {
|
|
+ netdev_err(dev, "Committed burst size must be under %d\n",
|
|
+ DPAA2_ETH_MAX_BURST_SIZE);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ scfg->max_burst_size = cfg.cbs;
|
|
+
|
|
+ if (cfg.ebs > DPAA2_ETH_MAX_BURST_SIZE) {
|
|
+ netdev_err(dev, "Excess burst size must be under %d\n",
|
|
+ DPAA2_ETH_MAX_BURST_SIZE);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ecfg->max_burst_size = cfg.ebs;
|
|
+
|
|
+ if ((!cfg.cir || !cfg.eir) && cfg.coupled) {
|
|
+ netdev_err(dev, "Coupling can be set when both CIR and EIR are finite\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+enum update_tx_prio {
|
|
+ DPAA2_ETH_ADD_CQ,
|
|
+ DPAA2_ETH_DEL_CQ,
|
|
+};
|
|
+
|
|
+/* Normalize weights based on max passed value */
|
|
+static inline int dpaa2_eth_normalize_tx_prio(struct dpaa2_ceetm_qdisc *priv)
|
|
+{
|
|
+ struct dpni_tx_schedule_cfg *sched_cfg;
|
|
+ struct dpaa2_ceetm_class *cl;
|
|
+ u32 qpri;
|
|
+ u16 weight_max = 0, increment;
|
|
+ int i;
|
|
+
|
|
+ /* Check the boundaries of the provided values */
|
|
+ for (i = 0; i < priv->clhash.hashsize; i++)
|
|
+ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode)
|
|
+ weight_max = (weight_max == 0 ? cl->prio.weight :
|
|
+ (weight_max < cl->prio.weight ?
|
|
+ cl->prio.weight : weight_max));
|
|
+
|
|
+ /* If there are no elements, there's nothing to do */
|
|
+ if (weight_max == 0)
|
|
+ return 0;
|
|
+
|
|
+ increment = (DPAA2_CEETM_MAX_WEIGHT - DPAA2_CEETM_MIN_WEIGHT) /
|
|
+ weight_max;
|
|
+
|
|
+ for (i = 0; i < priv->clhash.hashsize; i++) {
|
|
+ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) {
|
|
+ if (cl->prio.mode == STRICT_PRIORITY)
|
|
+ continue;
|
|
+
|
|
+ qpri = cl->prio.qpri;
|
|
+ sched_cfg = &priv->prio.tx_prio_cfg.tc_sched[qpri];
|
|
+
|
|
+ sched_cfg->delta_bandwidth =
|
|
+ DPAA2_CEETM_MIN_WEIGHT +
|
|
+ (cl->prio.weight * increment);
|
|
+
|
|
+ pr_debug("%s: Normalized CQ qpri %d weight to %d\n",
|
|
+ __func__, qpri, sched_cfg->delta_bandwidth);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int dpaa2_eth_update_tx_prio(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_ceetm_class *cl,
|
|
+ enum update_tx_prio type)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *sch = qdisc_priv(cl->parent);
|
|
+ struct dpni_congestion_notification_cfg notif_cfg = {0};
|
|
+ struct dpni_tx_schedule_cfg *sched_cfg;
|
|
+ struct dpni_taildrop td = {0};
|
|
+ u8 ch_id = 0, tc_id = 0;
|
|
+ u32 qpri = 0;
|
|
+ int err = 0;
|
|
+
|
|
+ qpri = cl->prio.qpri;
|
|
+ tc_id = DPNI_BUILD_CH_TC(ch_id, qpri);
|
|
+
|
|
+ switch (type) {
|
|
+ case DPAA2_ETH_ADD_CQ:
|
|
+ /* Disable congestion notifications */
|
|
+ notif_cfg.threshold_entry = 0;
|
|
+ notif_cfg.threshold_exit = 0;
|
|
+ err = dpni_set_congestion_notification(priv->mc_io, 0,
|
|
+ priv->mc_token,
|
|
+ DPNI_QUEUE_TX, tc_id,
|
|
+ ¬if_cfg);
|
|
+ if (err) {
|
|
+ netdev_err(priv->net_dev, "Error disabling congestion notifications %d\n",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+ /* Enable taildrop */
|
|
+ td.enable = 1;
|
|
+ td.units = DPNI_CONGESTION_UNIT_FRAMES;
|
|
+ td.threshold = DPAA2_CEETM_TD_THRESHOLD;
|
|
+ err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_CP_GROUP, DPNI_QUEUE_TX, tc_id,
|
|
+ 0, &td);
|
|
+ if (err) {
|
|
+ netdev_err(priv->net_dev, "Error enabling Tx taildrop %d\n",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+ break;
|
|
+ case DPAA2_ETH_DEL_CQ:
|
|
+ /* Disable taildrop */
|
|
+ td.enable = 0;
|
|
+ err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_CP_GROUP, DPNI_QUEUE_TX, tc_id,
|
|
+ 0, &td);
|
|
+ if (err) {
|
|
+ netdev_err(priv->net_dev, "Error disabling Tx taildrop %d\n",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+ /* Enable congestion notifications */
|
|
+ notif_cfg.units = DPNI_CONGESTION_UNIT_BYTES;
|
|
+ notif_cfg.threshold_entry = DPAA2_ETH_TX_CONG_ENTRY_THRESH;
|
|
+ notif_cfg.threshold_exit = DPAA2_ETH_TX_CONG_EXIT_THRESH;
|
|
+ notif_cfg.message_ctx = (u64)priv;
|
|
+ notif_cfg.message_iova = priv->cscn_dma;
|
|
+ notif_cfg.notification_mode = DPNI_CONG_OPT_WRITE_MEM_ON_ENTER |
|
|
+ DPNI_CONG_OPT_WRITE_MEM_ON_EXIT |
|
|
+ DPNI_CONG_OPT_COHERENT_WRITE;
|
|
+ err = dpni_set_congestion_notification(priv->mc_io, 0,
|
|
+ priv->mc_token,
|
|
+ DPNI_QUEUE_TX, tc_id,
|
|
+ ¬if_cfg);
|
|
+ if (err) {
|
|
+ netdev_err(priv->net_dev, "Error enabling congestion notifications %d\n",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* We can zero out the structure in the tx_prio_conf array */
|
|
+ if (type == DPAA2_ETH_DEL_CQ) {
|
|
+ sched_cfg = &sch->prio.tx_prio_cfg.tc_sched[qpri];
|
|
+ memset(sched_cfg, 0, sizeof(*sched_cfg));
|
|
+ }
|
|
+
|
|
+ /* Normalize priorities */
|
|
+ err = dpaa2_eth_normalize_tx_prio(sch);
|
|
+
|
|
+ /* Debug print goes here */
|
|
+ print_hex_dump_debug("tx_prio: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
+ &sch->prio.tx_prio_cfg,
|
|
+ sizeof(sch->prio.tx_prio_cfg), 0);
|
|
+
|
|
+ /* Call dpni_set_tx_priorities for the entire prio qdisc */
|
|
+ err = dpni_set_tx_priorities(priv->mc_io, 0, priv->mc_token,
|
|
+ &sch->prio.tx_prio_cfg);
|
|
+ if (err)
|
|
+ netdev_err(priv->net_dev, "dpni_set_tx_priorities err %d\n",
|
|
+ err);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void dpaa2_eth_ceetm_enable(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ priv->ceetm_en = true;
|
|
+}
|
|
+
|
|
+static void dpaa2_eth_ceetm_disable(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ priv->ceetm_en = false;
|
|
+}
|
|
+
|
|
+/* Find class in qdisc hash table using given handle */
|
|
+static inline struct dpaa2_ceetm_class *dpaa2_ceetm_find(u32 handle,
|
|
+ struct Qdisc *sch)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct Qdisc_class_common *clc;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : find class %X in qdisc %X\n",
|
|
+ __func__, handle, sch->handle);
|
|
+
|
|
+ clc = qdisc_class_find(&priv->clhash, handle);
|
|
+ return clc ? container_of(clc, struct dpaa2_ceetm_class, common) : NULL;
|
|
+}
|
|
+
|
|
+/* Insert a class in the qdisc's class hash */
|
|
+static void dpaa2_ceetm_link_class(struct Qdisc *sch,
|
|
+ struct Qdisc_class_hash *clhash,
|
|
+ struct Qdisc_class_common *common)
|
|
+{
|
|
+ sch_tree_lock(sch);
|
|
+ qdisc_class_hash_insert(clhash, common);
|
|
+ sch_tree_unlock(sch);
|
|
+ qdisc_class_hash_grow(sch, clhash);
|
|
+}
|
|
+
|
|
+/* Destroy a ceetm class */
|
|
+static void dpaa2_ceetm_cls_destroy(struct Qdisc *sch,
|
|
+ struct dpaa2_ceetm_class *cl)
|
|
+{
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if (!cl)
|
|
+ return;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : destroy class %X from under %X\n",
|
|
+ __func__, cl->common.classid, sch->handle);
|
|
+
|
|
+ /* Recurse into child first */
|
|
+ if (cl->child) {
|
|
+ qdisc_destroy(cl->child);
|
|
+ cl->child = NULL;
|
|
+ }
|
|
+
|
|
+ switch (cl->type) {
|
|
+ case CEETM_ROOT:
|
|
+ if (dpaa2_eth_reset_ch_shaping(priv, cl->root.ch_id))
|
|
+ netdev_err(dev, "Error resetting channel shaping\n");
|
|
+
|
|
+ break;
|
|
+
|
|
+ case CEETM_PRIO:
|
|
+ if (dpaa2_eth_update_tx_prio(priv, cl, DPAA2_ETH_DEL_CQ))
|
|
+ netdev_err(dev, "Error resetting tx_priorities\n");
|
|
+
|
|
+ if (cl->prio.cstats)
|
|
+ free_percpu(cl->prio.cstats);
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ tcf_block_put(cl->block);
|
|
+ kfree(cl);
|
|
+}
|
|
+
|
|
+/* Destroy a ceetm qdisc */
|
|
+static void dpaa2_ceetm_destroy(struct Qdisc *sch)
|
|
+{
|
|
+ unsigned int i;
|
|
+ struct hlist_node *next;
|
|
+ struct dpaa2_ceetm_class *cl;
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_eth_priv *priv_eth = netdev_priv(dev);
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : destroy qdisc %X\n",
|
|
+ __func__, sch->handle);
|
|
+
|
|
+ /* All filters need to be removed before destroying the classes */
|
|
+ tcf_block_put(priv->block);
|
|
+
|
|
+ for (i = 0; i < priv->clhash.hashsize; i++) {
|
|
+ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode)
|
|
+ tcf_block_put(cl->block);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < priv->clhash.hashsize; i++) {
|
|
+ hlist_for_each_entry_safe(cl, next, &priv->clhash.hash[i],
|
|
+ common.hnode)
|
|
+ dpaa2_ceetm_cls_destroy(sch, cl);
|
|
+ }
|
|
+
|
|
+ qdisc_class_hash_destroy(&priv->clhash);
|
|
+
|
|
+ switch (priv->type) {
|
|
+ case CEETM_ROOT:
|
|
+ dpaa2_eth_ceetm_disable(priv_eth);
|
|
+
|
|
+ if (priv->root.qstats)
|
|
+ free_percpu(priv->root.qstats);
|
|
+
|
|
+ if (!priv->root.qdiscs)
|
|
+ break;
|
|
+
|
|
+ /* Destroy the pfifo qdiscs in case they haven't been attached
|
|
+ * to the netdev queues yet.
|
|
+ */
|
|
+ for (i = 0; i < dev->num_tx_queues; i++)
|
|
+ if (priv->root.qdiscs[i])
|
|
+ qdisc_destroy(priv->root.qdiscs[i]);
|
|
+
|
|
+ kfree(priv->root.qdiscs);
|
|
+ break;
|
|
+
|
|
+ case CEETM_PRIO:
|
|
+ if (priv->prio.parent)
|
|
+ priv->prio.parent->child = NULL;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_dump(struct Qdisc *sch, struct sk_buff *skb)
|
|
+{
|
|
+ struct Qdisc *qdisc;
|
|
+ unsigned int ntx, i;
|
|
+ struct nlattr *nest;
|
|
+ struct dpaa2_ceetm_tc_qopt qopt;
|
|
+ struct dpaa2_ceetm_qdisc_stats *qstats;
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ sch_tree_lock(sch);
|
|
+ memset(&qopt, 0, sizeof(qopt));
|
|
+ qopt.type = priv->type;
|
|
+ qopt.shaped = priv->shaped;
|
|
+
|
|
+ switch (priv->type) {
|
|
+ case CEETM_ROOT:
|
|
+ /* Gather statistics from the underlying pfifo qdiscs */
|
|
+ sch->q.qlen = 0;
|
|
+ memset(&sch->bstats, 0, sizeof(sch->bstats));
|
|
+ memset(&sch->qstats, 0, sizeof(sch->qstats));
|
|
+
|
|
+ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
|
|
+ qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping;
|
|
+ sch->q.qlen += qdisc->q.qlen;
|
|
+ sch->bstats.bytes += qdisc->bstats.bytes;
|
|
+ sch->bstats.packets += qdisc->bstats.packets;
|
|
+ sch->qstats.qlen += qdisc->qstats.qlen;
|
|
+ sch->qstats.backlog += qdisc->qstats.backlog;
|
|
+ sch->qstats.drops += qdisc->qstats.drops;
|
|
+ sch->qstats.requeues += qdisc->qstats.requeues;
|
|
+ sch->qstats.overlimits += qdisc->qstats.overlimits;
|
|
+ }
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ qstats = per_cpu_ptr(priv->root.qstats, i);
|
|
+ sch->qstats.drops += qstats->drops;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+
|
|
+ case CEETM_PRIO:
|
|
+ qopt.prio_group_A = priv->prio.tx_prio_cfg.prio_group_A;
|
|
+ qopt.prio_group_B = priv->prio.tx_prio_cfg.prio_group_B;
|
|
+ qopt.separate_groups = priv->prio.tx_prio_cfg.separate_groups;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__);
|
|
+ sch_tree_unlock(sch);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ nest = nla_nest_start(skb, TCA_OPTIONS);
|
|
+ if (!nest)
|
|
+ goto nla_put_failure;
|
|
+ if (nla_put(skb, DPAA2_CEETM_TCA_QOPS, sizeof(qopt), &qopt))
|
|
+ goto nla_put_failure;
|
|
+ nla_nest_end(skb, nest);
|
|
+
|
|
+ sch_tree_unlock(sch);
|
|
+ return skb->len;
|
|
+
|
|
+nla_put_failure:
|
|
+ sch_tree_unlock(sch);
|
|
+ nla_nest_cancel(skb, nest);
|
|
+ return -EMSGSIZE;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_change_prio(struct Qdisc *sch,
|
|
+ struct dpaa2_ceetm_qdisc *priv,
|
|
+ struct dpaa2_ceetm_tc_qopt *qopt)
|
|
+{
|
|
+ /* TODO: Once LX2 support is added */
|
|
+ /* priv->shaped = parent_cl->shaped; */
|
|
+ priv->prio.tx_prio_cfg.prio_group_A = qopt->prio_group_A;
|
|
+ priv->prio.tx_prio_cfg.prio_group_B = qopt->prio_group_B;
|
|
+ priv->prio.tx_prio_cfg.separate_groups = qopt->separate_groups;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Edit a ceetm qdisc */
|
|
+static int dpaa2_ceetm_change(struct Qdisc *sch, struct nlattr *opt)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct nlattr *tb[DPAA2_CEETM_TCA_QOPS + 1];
|
|
+ struct dpaa2_ceetm_tc_qopt *qopt;
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ err = nla_parse_nested(tb, DPAA2_CEETM_TCA_QOPS, opt,
|
|
+ dpaa2_ceetm_policy, NULL);
|
|
+ if (err < 0) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "nla_parse_nested");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (!tb[DPAA2_CEETM_TCA_QOPS]) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "tb");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (TC_H_MIN(sch->handle)) {
|
|
+ pr_err("CEETM: a qdisc should not have a minor\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ qopt = nla_data(tb[DPAA2_CEETM_TCA_QOPS]);
|
|
+
|
|
+ if (priv->type != qopt->type) {
|
|
+ pr_err("CEETM: qdisc %X is not of the provided type\n",
|
|
+ sch->handle);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (priv->type) {
|
|
+ case CEETM_PRIO:
|
|
+ err = dpaa2_ceetm_change_prio(sch, priv, qopt);
|
|
+ break;
|
|
+ default:
|
|
+ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__);
|
|
+ err = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Configure a root ceetm qdisc */
|
|
+static int dpaa2_ceetm_init_root(struct Qdisc *sch,
|
|
+ struct dpaa2_ceetm_qdisc *priv,
|
|
+ struct dpaa2_ceetm_tc_qopt *qopt)
|
|
+{
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_eth_priv *priv_eth = netdev_priv(dev);
|
|
+ struct netdev_queue *dev_queue;
|
|
+ unsigned int i, parent_id;
|
|
+ struct Qdisc *qdisc;
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ /* Validate inputs */
|
|
+ if (sch->parent != TC_H_ROOT) {
|
|
+ pr_err("CEETM: a root ceetm qdisc can not be attached to a class\n");
|
|
+ tcf_block_put(priv->block);
|
|
+ qdisc_class_hash_destroy(&priv->clhash);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Pre-allocate underlying pfifo qdiscs.
|
|
+ *
|
|
+ * We want to offload shaping and scheduling decisions to the hardware.
|
|
+ * The pfifo qdiscs will be attached to the netdev queues and will
|
|
+ * guide the traffic from the IP stack down to the driver with minimum
|
|
+ * interference.
|
|
+ *
|
|
+ * The CEETM qdiscs and classes will be crossed when the traffic
|
|
+ * reaches the driver.
|
|
+ */
|
|
+ priv->root.qdiscs = kcalloc(dev->num_tx_queues,
|
|
+ sizeof(priv->root.qdiscs[0]),
|
|
+ GFP_KERNEL);
|
|
+ if (!priv->root.qdiscs) {
|
|
+ err = -ENOMEM;
|
|
+ goto err_init_root;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev->num_tx_queues; i++) {
|
|
+ dev_queue = netdev_get_tx_queue(dev, i);
|
|
+ parent_id = TC_H_MAKE(TC_H_MAJ(sch->handle),
|
|
+ TC_H_MIN(i + PFIFO_MIN_OFFSET));
|
|
+
|
|
+ qdisc = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
|
|
+ parent_id);
|
|
+ if (!qdisc) {
|
|
+ err = -ENOMEM;
|
|
+ goto err_init_root;
|
|
+ }
|
|
+
|
|
+ priv->root.qdiscs[i] = qdisc;
|
|
+ qdisc->flags |= TCQ_F_ONETXQUEUE;
|
|
+ }
|
|
+
|
|
+ sch->flags |= TCQ_F_MQROOT;
|
|
+
|
|
+ priv->root.qstats = alloc_percpu(struct dpaa2_ceetm_qdisc_stats);
|
|
+ if (!priv->root.qstats) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n",
|
|
+ __func__);
|
|
+ err = -ENOMEM;
|
|
+ goto err_init_root;
|
|
+ }
|
|
+
|
|
+ dpaa2_eth_ceetm_enable(priv_eth);
|
|
+ return 0;
|
|
+
|
|
+err_init_root:
|
|
+ dpaa2_ceetm_destroy(sch);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Configure a prio ceetm qdisc */
|
|
+static int dpaa2_ceetm_init_prio(struct Qdisc *sch,
|
|
+ struct dpaa2_ceetm_qdisc *priv,
|
|
+ struct dpaa2_ceetm_tc_qopt *qopt)
|
|
+{
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_ceetm_class *parent_cl;
|
|
+ struct Qdisc *parent_qdisc;
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ if (sch->parent == TC_H_ROOT) {
|
|
+ pr_err("CEETM: a prio ceetm qdisc can not be root\n");
|
|
+ err = -EINVAL;
|
|
+ goto err_init_prio;
|
|
+ }
|
|
+
|
|
+ parent_qdisc = qdisc_lookup(dev, TC_H_MAJ(sch->parent));
|
|
+ if (strcmp(parent_qdisc->ops->id, dpaa2_ceetm_qdisc_ops.id)) {
|
|
+ pr_err("CEETM: a ceetm qdisc can not be attached to other qdisc/class types\n");
|
|
+ err = -EINVAL;
|
|
+ goto err_init_prio;
|
|
+ }
|
|
+
|
|
+ /* Obtain the parent root ceetm_class */
|
|
+ parent_cl = dpaa2_ceetm_find(sch->parent, parent_qdisc);
|
|
+
|
|
+ if (!parent_cl || parent_cl->type != CEETM_ROOT) {
|
|
+ pr_err("CEETM: a prio ceetm qdiscs can be added only under a root ceetm class\n");
|
|
+ err = -EINVAL;
|
|
+ goto err_init_prio;
|
|
+ }
|
|
+
|
|
+ priv->prio.parent = parent_cl;
|
|
+ parent_cl->child = sch;
|
|
+
|
|
+ err = dpaa2_ceetm_change_prio(sch, priv, qopt);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_init_prio:
|
|
+ dpaa2_ceetm_destroy(sch);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Configure a generic ceetm qdisc */
|
|
+static int dpaa2_ceetm_init(struct Qdisc *sch, struct nlattr *opt)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct nlattr *tb[DPAA2_CEETM_TCA_QOPS + 1];
|
|
+ struct dpaa2_ceetm_tc_qopt *qopt;
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ if (!netif_is_multiqueue(dev))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ err = tcf_block_get(&priv->block, &priv->filter_list);
|
|
+ if (err) {
|
|
+ pr_err("CEETM: unable to get tcf_block\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (!opt) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error - opt = NULL\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ err = nla_parse_nested(tb, DPAA2_CEETM_TCA_QOPS, opt,
|
|
+ dpaa2_ceetm_policy, NULL);
|
|
+ if (err < 0) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "nla_parse_nested");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (!tb[DPAA2_CEETM_TCA_QOPS]) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "tb");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (TC_H_MIN(sch->handle)) {
|
|
+ pr_err("CEETM: a qdisc should not have a minor\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ qopt = nla_data(tb[DPAA2_CEETM_TCA_QOPS]);
|
|
+
|
|
+ /* Initialize the class hash list. Each qdisc has its own class hash */
|
|
+ err = qdisc_class_hash_init(&priv->clhash);
|
|
+ if (err < 0) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : qdisc_class_hash_init failed\n",
|
|
+ __func__);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ priv->type = qopt->type;
|
|
+ priv->shaped = qopt->shaped;
|
|
+
|
|
+ switch (priv->type) {
|
|
+ case CEETM_ROOT:
|
|
+ err = dpaa2_ceetm_init_root(sch, priv, qopt);
|
|
+ break;
|
|
+ case CEETM_PRIO:
|
|
+ err = dpaa2_ceetm_init_prio(sch, priv, qopt);
|
|
+ break;
|
|
+ default:
|
|
+ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__);
|
|
+ dpaa2_ceetm_destroy(sch);
|
|
+ err = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Attach the underlying pfifo qdiscs */
|
|
+static void dpaa2_ceetm_attach(struct Qdisc *sch)
|
|
+{
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct Qdisc *qdisc, *old_qdisc;
|
|
+ unsigned int i;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ for (i = 0; i < dev->num_tx_queues; i++) {
|
|
+ qdisc = priv->root.qdiscs[i];
|
|
+ old_qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc);
|
|
+ if (old_qdisc)
|
|
+ qdisc_destroy(old_qdisc);
|
|
+ }
|
|
+
|
|
+ /* Remove the references to the pfifo qdiscs since the kernel will
|
|
+ * destroy them when needed. No cleanup from our part is required from
|
|
+ * this point on.
|
|
+ */
|
|
+ kfree(priv->root.qdiscs);
|
|
+ priv->root.qdiscs = NULL;
|
|
+}
|
|
+
|
|
+static unsigned long dpaa2_ceetm_cls_find(struct Qdisc *sch, u32 classid)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : classid %X from qdisc %X\n",
|
|
+ __func__, classid, sch->handle);
|
|
+ cl = dpaa2_ceetm_find(classid, sch);
|
|
+
|
|
+ return (unsigned long)cl;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_change_root(struct dpaa2_ceetm_class *cl,
|
|
+ struct dpaa2_ceetm_tc_copt *copt,
|
|
+ struct net_device *dev)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
|
+ struct dpni_tx_shaping_cfg scfg = { 0 }, ecfg = { 0 };
|
|
+ int err = 0;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X\n", __func__,
|
|
+ cl->common.classid);
|
|
+
|
|
+ if (!cl->shaped)
|
|
+ return 0;
|
|
+
|
|
+ if (dpaa2_eth_update_shaping_cfg(dev, copt->shaping_cfg,
|
|
+ &scfg, &ecfg))
|
|
+ return -EINVAL;
|
|
+
|
|
+ err = dpaa2_eth_set_ch_shaping(priv, &scfg, &ecfg,
|
|
+ copt->shaping_cfg.coupled,
|
|
+ cl->root.ch_id);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ memcpy(&cl->root.shaping_cfg, &copt->shaping_cfg,
|
|
+ sizeof(struct dpaa2_ceetm_shaping_cfg));
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_change_prio(struct dpaa2_ceetm_class *cl,
|
|
+ struct dpaa2_ceetm_tc_copt *copt,
|
|
+ struct net_device *dev)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *sch = qdisc_priv(cl->parent);
|
|
+ struct dpni_tx_schedule_cfg *sched_cfg;
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X mode %d weight %d\n",
|
|
+ __func__, cl->common.classid, copt->mode, copt->weight);
|
|
+
|
|
+ if (!cl->prio.cstats) {
|
|
+ cl->prio.cstats = alloc_percpu(struct dpaa2_ceetm_class_stats);
|
|
+ if (!cl->prio.cstats) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n",
|
|
+ __func__);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cl->prio.mode = copt->mode;
|
|
+ cl->prio.weight = copt->weight;
|
|
+
|
|
+ sched_cfg = &sch->prio.tx_prio_cfg.tc_sched[cl->prio.qpri];
|
|
+
|
|
+ switch (copt->mode) {
|
|
+ case STRICT_PRIORITY:
|
|
+ sched_cfg->mode = DPNI_TX_SCHED_STRICT_PRIORITY;
|
|
+ break;
|
|
+ case WEIGHTED_A:
|
|
+ sched_cfg->mode = DPNI_TX_SCHED_WEIGHTED_A;
|
|
+ break;
|
|
+ case WEIGHTED_B:
|
|
+ sched_cfg->mode = DPNI_TX_SCHED_WEIGHTED_B;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ err = dpaa2_eth_update_tx_prio(priv, cl, DPAA2_ETH_ADD_CQ);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Add a new ceetm class */
|
|
+static int dpaa2_ceetm_cls_add(struct Qdisc *sch, u32 classid,
|
|
+ struct dpaa2_ceetm_tc_copt *copt,
|
|
+ unsigned long *arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_eth_priv *priv_eth = netdev_priv(dev);
|
|
+ struct dpaa2_ceetm_class *cl;
|
|
+ int err;
|
|
+
|
|
+ if (copt->type == CEETM_ROOT &&
|
|
+ priv->clhash.hashelems == dpaa2_eth_ch_count(priv_eth)) {
|
|
+ pr_err("CEETM: only %d channel%s per DPNI allowed, sorry\n",
|
|
+ dpaa2_eth_ch_count(priv_eth),
|
|
+ dpaa2_eth_ch_count(priv_eth) == 1 ? "" : "s");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (copt->type == CEETM_PRIO &&
|
|
+ priv->clhash.hashelems == dpaa2_eth_tc_count(priv_eth)) {
|
|
+ pr_err("CEETM: only %d queue%s per channel allowed, sorry\n",
|
|
+ dpaa2_eth_tc_count(priv_eth),
|
|
+ dpaa2_eth_tc_count(priv_eth) == 1 ? "" : "s");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
|
|
+ if (!cl)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ err = tcf_block_get(&cl->block, &cl->filter_list);
|
|
+ if (err) {
|
|
+ pr_err("%s: Unable to set new root class\n", __func__);
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ cl->common.classid = classid;
|
|
+ cl->parent = sch;
|
|
+ cl->child = NULL;
|
|
+
|
|
+ /* Add class handle in Qdisc */
|
|
+ dpaa2_ceetm_link_class(sch, &priv->clhash, &cl->common);
|
|
+
|
|
+ cl->shaped = copt->shaped;
|
|
+ cl->type = copt->type;
|
|
+
|
|
+ /* Claim a CEETM channel / tc - DPAA2. will assume transition from
|
|
+ * classid to qdid/qpri, starting from qdid / qpri 0
|
|
+ */
|
|
+ switch (copt->type) {
|
|
+ case CEETM_ROOT:
|
|
+ cl->root.ch_id = classid - sch->handle - 1;
|
|
+ err = dpaa2_ceetm_cls_change_root(cl, copt, dev);
|
|
+ break;
|
|
+ case CEETM_PRIO:
|
|
+ cl->prio.qpri = classid - sch->handle - 1;
|
|
+ err = dpaa2_ceetm_cls_change_prio(cl, copt, dev);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (err) {
|
|
+ pr_err("%s: Unable to set new %s class\n", __func__,
|
|
+ (copt->type == CEETM_ROOT ? "root" : "prio"));
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ switch (copt->type) {
|
|
+ case CEETM_ROOT:
|
|
+ pr_debug(KBUILD_BASENAME " : %s : configured root class %X associated with channel qdid %d\n",
|
|
+ __func__, classid, cl->root.ch_id);
|
|
+ break;
|
|
+ case CEETM_PRIO:
|
|
+ pr_debug(KBUILD_BASENAME " : %s : configured prio class %X associated with queue qpri %d\n",
|
|
+ __func__, classid, cl->prio.qpri);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *arg = (unsigned long)cl;
|
|
+ return 0;
|
|
+
|
|
+out_free:
|
|
+ kfree(cl);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Add or configure a ceetm class */
|
|
+static int dpaa2_ceetm_cls_change(struct Qdisc *sch, u32 classid, u32 parentid,
|
|
+ struct nlattr **tca, unsigned long *arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv;
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)*arg;
|
|
+ struct nlattr *opt = tca[TCA_OPTIONS];
|
|
+ struct nlattr *tb[DPAA2_CEETM_TCA_MAX];
|
|
+ struct dpaa2_ceetm_tc_copt *copt;
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ int err;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : classid %X under qdisc %X\n",
|
|
+ __func__, classid, sch->handle);
|
|
+
|
|
+ if (strcmp(sch->ops->id, dpaa2_ceetm_qdisc_ops.id)) {
|
|
+ pr_err("CEETM: a ceetm class can not be attached to other qdisc/class types\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ priv = qdisc_priv(sch);
|
|
+
|
|
+ if (!opt) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error NULL opt\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ err = nla_parse_nested(tb, DPAA2_CEETM_TCA_COPT, opt,
|
|
+ dpaa2_ceetm_policy, NULL);
|
|
+ if (err < 0) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "nla_parse_nested");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!tb[DPAA2_CEETM_TCA_COPT]) {
|
|
+ pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__,
|
|
+ "tb");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ copt = nla_data(tb[DPAA2_CEETM_TCA_COPT]);
|
|
+
|
|
+ /* Configure an existing ceetm class */
|
|
+ if (cl) {
|
|
+ if (copt->type != cl->type) {
|
|
+ pr_err("CEETM: class %X is not of the provided type\n",
|
|
+ cl->common.classid);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (copt->type) {
|
|
+ case CEETM_ROOT:
|
|
+ return dpaa2_ceetm_cls_change_root(cl, copt, dev);
|
|
+ case CEETM_PRIO:
|
|
+ return dpaa2_ceetm_cls_change_prio(cl, copt, dev);
|
|
+
|
|
+ default:
|
|
+ pr_err(KBUILD_BASENAME " : %s : invalid class\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return dpaa2_ceetm_cls_add(sch, classid, copt, arg);
|
|
+}
|
|
+
|
|
+static void dpaa2_ceetm_cls_walk(struct Qdisc *sch, struct qdisc_walker *arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct dpaa2_ceetm_class *cl;
|
|
+ unsigned int i;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle);
|
|
+
|
|
+ if (arg->stop)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < priv->clhash.hashsize; i++) {
|
|
+ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) {
|
|
+ if (arg->count < arg->skip) {
|
|
+ arg->count++;
|
|
+ continue;
|
|
+ }
|
|
+ if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
|
|
+ arg->stop = 1;
|
|
+ return;
|
|
+ }
|
|
+ arg->count++;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_dump(struct Qdisc *sch, unsigned long arg,
|
|
+ struct sk_buff *skb, struct tcmsg *tcm)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+ struct nlattr *nest;
|
|
+ struct dpaa2_ceetm_tc_copt copt;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n",
|
|
+ __func__, cl->common.classid, sch->handle);
|
|
+
|
|
+ sch_tree_lock(sch);
|
|
+
|
|
+ tcm->tcm_parent = ((struct Qdisc *)cl->parent)->handle;
|
|
+ tcm->tcm_handle = cl->common.classid;
|
|
+
|
|
+ memset(&copt, 0, sizeof(copt));
|
|
+
|
|
+ copt.shaped = cl->shaped;
|
|
+ copt.type = cl->type;
|
|
+
|
|
+ switch (cl->type) {
|
|
+ case CEETM_ROOT:
|
|
+ if (cl->child)
|
|
+ tcm->tcm_info = cl->child->handle;
|
|
+
|
|
+ memcpy(&copt.shaping_cfg, &cl->root.shaping_cfg,
|
|
+ sizeof(struct dpaa2_ceetm_shaping_cfg));
|
|
+
|
|
+ break;
|
|
+
|
|
+ case CEETM_PRIO:
|
|
+ if (cl->child)
|
|
+ tcm->tcm_info = cl->child->handle;
|
|
+
|
|
+ copt.mode = cl->prio.mode;
|
|
+ copt.weight = cl->prio.weight;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ nest = nla_nest_start(skb, TCA_OPTIONS);
|
|
+ if (!nest)
|
|
+ goto nla_put_failure;
|
|
+ if (nla_put(skb, DPAA2_CEETM_TCA_COPT, sizeof(copt), &copt))
|
|
+ goto nla_put_failure;
|
|
+ nla_nest_end(skb, nest);
|
|
+ sch_tree_unlock(sch);
|
|
+ return skb->len;
|
|
+
|
|
+nla_put_failure:
|
|
+ sch_tree_unlock(sch);
|
|
+ nla_nest_cancel(skb, nest);
|
|
+ return -EMSGSIZE;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_delete(struct Qdisc *sch, unsigned long arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n",
|
|
+ __func__, cl->common.classid, sch->handle);
|
|
+
|
|
+ sch_tree_lock(sch);
|
|
+ qdisc_class_hash_remove(&priv->clhash, &cl->common);
|
|
+ sch_tree_unlock(sch);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Get the class' child qdisc, if any */
|
|
+static struct Qdisc *dpaa2_ceetm_cls_leaf(struct Qdisc *sch, unsigned long arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n",
|
|
+ __func__, cl->common.classid, sch->handle);
|
|
+
|
|
+ switch (cl->type) {
|
|
+ case CEETM_ROOT:
|
|
+ case CEETM_PRIO:
|
|
+ return cl->child;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_graft(struct Qdisc *sch, unsigned long arg,
|
|
+ struct Qdisc *new, struct Qdisc **old)
|
|
+{
|
|
+ if (new && strcmp(new->ops->id, dpaa2_ceetm_qdisc_ops.id)) {
|
|
+ pr_err("CEETM: only ceetm qdiscs can be attached to ceetm classes\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_ceetm_cls_dump_stats(struct Qdisc *sch, unsigned long arg,
|
|
+ struct gnet_dump *d)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+ struct gnet_stats_basic_packed tmp_bstats;
|
|
+ struct dpaa2_ceetm_tc_xstats xstats;
|
|
+ union dpni_statistics dpni_stats;
|
|
+ struct net_device *dev = qdisc_dev(sch);
|
|
+ struct dpaa2_eth_priv *priv_eth = netdev_priv(dev);
|
|
+ u8 ch_id = 0;
|
|
+ int err;
|
|
+
|
|
+ memset(&xstats, 0, sizeof(xstats));
|
|
+ memset(&tmp_bstats, 0, sizeof(tmp_bstats));
|
|
+
|
|
+ if (cl->type == CEETM_ROOT)
|
|
+ return 0;
|
|
+
|
|
+ err = dpni_get_statistics(priv_eth->mc_io, 0, priv_eth->mc_token, 3,
|
|
+ DPNI_BUILD_CH_TC(ch_id, cl->prio.qpri),
|
|
+ &dpni_stats);
|
|
+ if (err)
|
|
+ netdev_warn(dev, "dpni_get_stats(%d) failed - %d\n", 3, err);
|
|
+
|
|
+ xstats.ceetm_dequeue_bytes = dpni_stats.page_3.ceetm_dequeue_bytes;
|
|
+ xstats.ceetm_dequeue_frames = dpni_stats.page_3.ceetm_dequeue_frames;
|
|
+ xstats.ceetm_reject_bytes = dpni_stats.page_3.ceetm_reject_bytes;
|
|
+ xstats.ceetm_reject_frames = dpni_stats.page_3.ceetm_reject_frames;
|
|
+
|
|
+ return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
|
|
+}
|
|
+
|
|
+static struct tcf_block *dpaa2_ceetm_tcf_block(struct Qdisc *sch,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__,
|
|
+ cl ? cl->common.classid : 0, sch->handle);
|
|
+ return cl ? cl->block : priv->block;
|
|
+}
|
|
+
|
|
+static unsigned long dpaa2_ceetm_tcf_bind(struct Qdisc *sch,
|
|
+ unsigned long parent,
|
|
+ u32 classid)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl = dpaa2_ceetm_find(classid, sch);
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__,
|
|
+ cl ? cl->common.classid : 0, sch->handle);
|
|
+ return (unsigned long)cl;
|
|
+}
|
|
+
|
|
+static void dpaa2_ceetm_tcf_unbind(struct Qdisc *sch, unsigned long arg)
|
|
+{
|
|
+ struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg;
|
|
+
|
|
+ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__,
|
|
+ cl ? cl->common.classid : 0, sch->handle);
|
|
+}
|
|
+
|
|
+const struct Qdisc_class_ops dpaa2_ceetm_cls_ops = {
|
|
+ .graft = dpaa2_ceetm_cls_graft,
|
|
+ .leaf = dpaa2_ceetm_cls_leaf,
|
|
+ .find = dpaa2_ceetm_cls_find,
|
|
+ .change = dpaa2_ceetm_cls_change,
|
|
+ .delete = dpaa2_ceetm_cls_delete,
|
|
+ .walk = dpaa2_ceetm_cls_walk,
|
|
+ .tcf_block = dpaa2_ceetm_tcf_block,
|
|
+ .bind_tcf = dpaa2_ceetm_tcf_bind,
|
|
+ .unbind_tcf = dpaa2_ceetm_tcf_unbind,
|
|
+ .dump = dpaa2_ceetm_cls_dump,
|
|
+ .dump_stats = dpaa2_ceetm_cls_dump_stats,
|
|
+};
|
|
+
|
|
+struct Qdisc_ops dpaa2_ceetm_qdisc_ops __read_mostly = {
|
|
+ .id = "ceetm",
|
|
+ .priv_size = sizeof(struct dpaa2_ceetm_qdisc),
|
|
+ .cl_ops = &dpaa2_ceetm_cls_ops,
|
|
+ .init = dpaa2_ceetm_init,
|
|
+ .destroy = dpaa2_ceetm_destroy,
|
|
+ .change = dpaa2_ceetm_change,
|
|
+ .dump = dpaa2_ceetm_dump,
|
|
+ .attach = dpaa2_ceetm_attach,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+/* Run the filters and classifiers attached to the qdisc on the provided skb */
|
|
+int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch,
|
|
+ int *qdid, u8 *qpri)
|
|
+{
|
|
+ struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch);
|
|
+ struct dpaa2_ceetm_class *cl = NULL;
|
|
+ struct tcf_result res;
|
|
+ struct tcf_proto *tcf;
|
|
+ int result;
|
|
+
|
|
+ tcf = rcu_dereference_bh(priv->filter_list);
|
|
+ while (tcf && (result = tcf_classify(skb, tcf, &res, false)) >= 0) {
|
|
+#ifdef CONFIG_NET_CLS_ACT
|
|
+ switch (result) {
|
|
+ case TC_ACT_QUEUED:
|
|
+ case TC_ACT_STOLEN:
|
|
+ case TC_ACT_SHOT:
|
|
+ /* No valid class found due to action */
|
|
+ return -1;
|
|
+ }
|
|
+#endif
|
|
+ cl = (void *)res.class;
|
|
+ if (!cl) {
|
|
+ /* The filter leads to the qdisc */
|
|
+ if (res.classid == sch->handle)
|
|
+ return 0;
|
|
+
|
|
+ cl = dpaa2_ceetm_find(res.classid, sch);
|
|
+ /* The filter leads to an invalid class */
|
|
+ if (!cl)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* The class might have its own filters attached */
|
|
+ tcf = rcu_dereference_bh(cl->filter_list);
|
|
+ }
|
|
+
|
|
+ /* No valid class found */
|
|
+ if (!cl)
|
|
+ return 0;
|
|
+
|
|
+ switch (cl->type) {
|
|
+ case CEETM_ROOT:
|
|
+ *qdid = cl->root.ch_id;
|
|
+
|
|
+ /* The root class does not have a child prio qdisc */
|
|
+ if (!cl->child)
|
|
+ return 0;
|
|
+
|
|
+ /* Run the prio qdisc classifiers */
|
|
+ return dpaa2_ceetm_classify(skb, cl->child, qdid, qpri);
|
|
+
|
|
+ case CEETM_PRIO:
|
|
+ *qpri = cl->prio.qpri;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int __init dpaa2_ceetm_register(void)
|
|
+{
|
|
+ int err = 0;
|
|
+
|
|
+ pr_debug(KBUILD_MODNAME ": " DPAA2_CEETM_DESCRIPTION "\n");
|
|
+
|
|
+ err = register_qdisc(&dpaa2_ceetm_qdisc_ops);
|
|
+ if (unlikely(err))
|
|
+ pr_err(KBUILD_MODNAME
|
|
+ ": %s:%hu:%s(): register_qdisc() = %d\n",
|
|
+ KBUILD_BASENAME ".c", __LINE__, __func__, err);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void __exit dpaa2_ceetm_unregister(void)
|
|
+{
|
|
+ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n",
|
|
+ KBUILD_BASENAME ".c", __func__);
|
|
+
|
|
+ unregister_qdisc(&dpaa2_ceetm_qdisc_ops);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h
|
|
@@ -0,0 +1,183 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright 2017 NXP
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __DPAA2_ETH_CEETM_H
|
|
+#define __DPAA2_ETH_CEETM_H
|
|
+
|
|
+#include <net/pkt_sched.h>
|
|
+#include <net/pkt_cls.h>
|
|
+#include <net/netlink.h>
|
|
+
|
|
+#include "dpaa2-eth.h"
|
|
+
|
|
+/* For functional purposes, there are num_tx_queues pfifo qdiscs through which
|
|
+ * frames reach the driver. Their handles start from 1:21. Handles 1:1 to 1:20
|
|
+ * are reserved for the maximum 32 CEETM channels (majors and minors are in
|
|
+ * hex).
|
|
+ */
|
|
+#define PFIFO_MIN_OFFSET 0x21
|
|
+
|
|
+#define DPAA2_CEETM_MIN_WEIGHT 100
|
|
+#define DPAA2_CEETM_MAX_WEIGHT 24800
|
|
+
|
|
+#define DPAA2_CEETM_TD_THRESHOLD 1000
|
|
+
|
|
+enum wbfs_group_type {
|
|
+ WBFS_GRP_A,
|
|
+ WBFS_GRP_B,
|
|
+ WBFS_GRP_LARGE
|
|
+};
|
|
+
|
|
+enum {
|
|
+ DPAA2_CEETM_TCA_UNSPEC,
|
|
+ DPAA2_CEETM_TCA_COPT,
|
|
+ DPAA2_CEETM_TCA_QOPS,
|
|
+ DPAA2_CEETM_TCA_MAX,
|
|
+};
|
|
+
|
|
+/* CEETM configuration types */
|
|
+enum dpaa2_ceetm_type {
|
|
+ CEETM_ROOT = 1,
|
|
+ CEETM_PRIO,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ STRICT_PRIORITY = 0,
|
|
+ WEIGHTED_A,
|
|
+ WEIGHTED_B,
|
|
+};
|
|
+
|
|
+struct dpaa2_ceetm_shaping_cfg {
|
|
+ __u64 cir; /* committed information rate */
|
|
+ __u64 eir; /* excess information rate */
|
|
+ __u16 cbs; /* committed burst size */
|
|
+ __u16 ebs; /* excess burst size */
|
|
+ __u8 coupled; /* shaper coupling */
|
|
+};
|
|
+
|
|
+extern const struct nla_policy ceetm_policy[DPAA2_CEETM_TCA_MAX];
|
|
+
|
|
+struct dpaa2_ceetm_class;
|
|
+struct dpaa2_ceetm_qdisc_stats;
|
|
+struct dpaa2_ceetm_class_stats;
|
|
+
|
|
+/* corresponds to CEETM shaping at LNI level */
|
|
+struct dpaa2_root_q {
|
|
+ struct Qdisc **qdiscs;
|
|
+ struct dpaa2_ceetm_qdisc_stats __percpu *qstats;
|
|
+};
|
|
+
|
|
+/* corresponds to the number of priorities a channel serves */
|
|
+struct dpaa2_prio_q {
|
|
+ struct dpaa2_ceetm_class *parent;
|
|
+ struct dpni_tx_priorities_cfg tx_prio_cfg;
|
|
+};
|
|
+
|
|
+struct dpaa2_ceetm_qdisc {
|
|
+ struct Qdisc_class_hash clhash;
|
|
+ struct tcf_proto *filter_list; /* qdisc attached filters */
|
|
+ struct tcf_block *block;
|
|
+
|
|
+ enum dpaa2_ceetm_type type; /* ROOT/PRIO */
|
|
+ bool shaped;
|
|
+ union {
|
|
+ struct dpaa2_root_q root;
|
|
+ struct dpaa2_prio_q prio;
|
|
+ };
|
|
+};
|
|
+
|
|
+/* CEETM Qdisc configuration parameters */
|
|
+struct dpaa2_ceetm_tc_qopt {
|
|
+ enum dpaa2_ceetm_type type;
|
|
+ __u16 shaped;
|
|
+ __u8 prio_group_A;
|
|
+ __u8 prio_group_B;
|
|
+ __u8 separate_groups;
|
|
+};
|
|
+
|
|
+/* root class - corresponds to a channel */
|
|
+struct dpaa2_root_c {
|
|
+ struct dpaa2_ceetm_shaping_cfg shaping_cfg;
|
|
+ u32 ch_id;
|
|
+};
|
|
+
|
|
+/* prio class - corresponds to a strict priority queue (group) */
|
|
+struct dpaa2_prio_c {
|
|
+ struct dpaa2_ceetm_class_stats __percpu *cstats;
|
|
+ u32 qpri;
|
|
+ u8 mode;
|
|
+ u16 weight;
|
|
+};
|
|
+
|
|
+struct dpaa2_ceetm_class {
|
|
+ struct Qdisc_class_common common;
|
|
+ struct tcf_proto *filter_list; /* class attached filters */
|
|
+ struct tcf_block *block;
|
|
+ struct Qdisc *parent;
|
|
+ struct Qdisc *child;
|
|
+
|
|
+ enum dpaa2_ceetm_type type; /* ROOT/PRIO */
|
|
+ bool shaped;
|
|
+ union {
|
|
+ struct dpaa2_root_c root;
|
|
+ struct dpaa2_prio_c prio;
|
|
+ };
|
|
+};
|
|
+
|
|
+/* CEETM Class configuration parameters */
|
|
+struct dpaa2_ceetm_tc_copt {
|
|
+ enum dpaa2_ceetm_type type;
|
|
+ struct dpaa2_ceetm_shaping_cfg shaping_cfg;
|
|
+ __u16 shaped;
|
|
+ __u8 mode;
|
|
+ __u16 weight;
|
|
+};
|
|
+
|
|
+/* CEETM stats */
|
|
+struct dpaa2_ceetm_qdisc_stats {
|
|
+ __u32 drops;
|
|
+};
|
|
+
|
|
+struct dpaa2_ceetm_class_stats {
|
|
+ /* Software counters */
|
|
+ struct gnet_stats_basic_packed bstats;
|
|
+ __u32 ern_drop_count;
|
|
+ __u32 congested_count;
|
|
+};
|
|
+
|
|
+struct dpaa2_ceetm_tc_xstats {
|
|
+ __u64 ceetm_dequeue_bytes;
|
|
+ __u64 ceetm_dequeue_frames;
|
|
+ __u64 ceetm_reject_bytes;
|
|
+ __u64 ceetm_reject_frames;
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_CEETM
|
|
+int __init dpaa2_ceetm_register(void);
|
|
+void __exit dpaa2_ceetm_unregister(void);
|
|
+int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch,
|
|
+ int *qdid, u8 *qpri);
|
|
+#else
|
|
+static inline int dpaa2_ceetm_register(void)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void dpaa2_ceetm_unregister(void) {}
|
|
+
|
|
+static inline int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch,
|
|
+ int *qdid, u8 *qpri)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline bool dpaa2_eth_ceetm_is_enabled(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return priv->ceetm_en;
|
|
+}
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c
|
|
@@ -0,0 +1,357 @@
|
|
+
|
|
+/* Copyright 2015 Freescale Semiconductor Inc.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * * Neither the name of Freescale Semiconductor nor the
|
|
+ * names of its contributors may be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ *
|
|
+ * ALTERNATIVELY, this software may be distributed under the terms of the
|
|
+ * GNU General Public License ("GPL") as published by the Free Software
|
|
+ * Foundation, either version 2 of that License or (at your option) any
|
|
+ * later version.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
|
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
|
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include "dpaa2-eth.h"
|
|
+#include "dpaa2-eth-debugfs.h"
|
|
+
|
|
+#define DPAA2_ETH_DBG_ROOT "dpaa2-eth"
|
|
+
|
|
+static struct dentry *dpaa2_dbg_root;
|
|
+
|
|
+static int dpaa2_dbg_cpu_show(struct seq_file *file, void *offset)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
|
|
+ struct rtnl_link_stats64 *stats;
|
|
+ struct dpaa2_eth_drv_stats *extras;
|
|
+ int i;
|
|
+
|
|
+ seq_printf(file, "Per-CPU stats for %s\n", priv->net_dev->name);
|
|
+ seq_printf(file, "%s%16s%16s%16s%16s%16s%16s%16s%16s%16s\n",
|
|
+ "CPU", "Rx", "Rx Err", "Rx SG", "Tx", "Tx Err", "Tx conf",
|
|
+ "Tx SG", "Tx realloc", "Enq busy");
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ stats = per_cpu_ptr(priv->percpu_stats, i);
|
|
+ extras = per_cpu_ptr(priv->percpu_extras, i);
|
|
+ seq_printf(file, "%3d%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu\n",
|
|
+ i,
|
|
+ stats->rx_packets,
|
|
+ stats->rx_errors,
|
|
+ extras->rx_sg_frames,
|
|
+ stats->tx_packets,
|
|
+ stats->tx_errors,
|
|
+ extras->tx_conf_frames,
|
|
+ extras->tx_sg_frames,
|
|
+ extras->tx_reallocs,
|
|
+ extras->tx_portal_busy);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_dbg_cpu_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int err;
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
|
|
+
|
|
+ err = single_open(file, dpaa2_dbg_cpu_show, priv);
|
|
+ if (err < 0)
|
|
+ netdev_err(priv->net_dev, "single_open() failed\n");
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static const struct file_operations dpaa2_dbg_cpu_ops = {
|
|
+ .open = dpaa2_dbg_cpu_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+static char *fq_type_to_str(struct dpaa2_eth_fq *fq)
|
|
+{
|
|
+ switch (fq->type) {
|
|
+ case DPAA2_RX_FQ:
|
|
+ return "Rx";
|
|
+ case DPAA2_TX_CONF_FQ:
|
|
+ return "Tx conf";
|
|
+ case DPAA2_RX_ERR_FQ:
|
|
+ return "Rx err";
|
|
+ default:
|
|
+ return "N/A";
|
|
+ }
|
|
+}
|
|
+
|
|
+static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
|
|
+ struct dpaa2_eth_fq *fq;
|
|
+ u32 fcnt, bcnt;
|
|
+ int i, err;
|
|
+
|
|
+ seq_printf(file, "non-zero FQ stats for %s:\n", priv->net_dev->name);
|
|
+ seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n",
|
|
+ "VFQID", "CPU", "Traffic Class", "Type", "Frames",
|
|
+ "Pending frames", "Congestion");
|
|
+
|
|
+ for (i = 0; i < priv->num_fqs; i++) {
|
|
+ fq = &priv->fq[i];
|
|
+ err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt);
|
|
+ if (err)
|
|
+ fcnt = 0;
|
|
+
|
|
+ /* A lot of queues, no use displaying zero traffic ones */
|
|
+ if (!fq->stats.frames && !fcnt)
|
|
+ continue;
|
|
+
|
|
+ seq_printf(file, "%5d%16d%16d%16s%16llu%16u%16llu\n",
|
|
+ fq->fqid,
|
|
+ fq->target_cpu,
|
|
+ fq->tc,
|
|
+ fq_type_to_str(fq),
|
|
+ fq->stats.frames,
|
|
+ fcnt,
|
|
+ fq->stats.congestion_entry);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_dbg_fqs_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int err;
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
|
|
+
|
|
+ err = single_open(file, dpaa2_dbg_fqs_show, priv);
|
|
+ if (err < 0)
|
|
+ netdev_err(priv->net_dev, "single_open() failed\n");
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static const struct file_operations dpaa2_dbg_fq_ops = {
|
|
+ .open = dpaa2_dbg_fqs_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
|
|
+ struct dpaa2_eth_channel *ch;
|
|
+ int i;
|
|
+
|
|
+ seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name);
|
|
+ seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n",
|
|
+ "CHID", "CPU", "Deq busy", "Frames", "CDANs",
|
|
+ "Avg frm/CDAN", "Buf count");
|
|
+
|
|
+ for (i = 0; i < priv->num_channels; i++) {
|
|
+ ch = priv->channel[i];
|
|
+ seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu%16d\n",
|
|
+ ch->ch_id,
|
|
+ ch->nctx.desired_cpu,
|
|
+ ch->stats.dequeue_portal_busy,
|
|
+ ch->stats.frames,
|
|
+ ch->stats.cdan,
|
|
+ ch->stats.frames / ch->stats.cdan,
|
|
+ ch->buf_count);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_dbg_ch_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int err;
|
|
+ struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
|
|
+
|
|
+ err = single_open(file, dpaa2_dbg_ch_show, priv);
|
|
+ if (err < 0)
|
|
+ netdev_err(priv->net_dev, "single_open() failed\n");
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static const struct file_operations dpaa2_dbg_ch_ops = {
|
|
+ .open = dpaa2_dbg_ch_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+static ssize_t dpaa2_dbg_reset_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *offset)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = file->private_data;
|
|
+ struct rtnl_link_stats64 *percpu_stats;
|
|
+ struct dpaa2_eth_drv_stats *percpu_extras;
|
|
+ struct dpaa2_eth_fq *fq;
|
|
+ struct dpaa2_eth_channel *ch;
|
|
+ int i;
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ percpu_stats = per_cpu_ptr(priv->percpu_stats, i);
|
|
+ memset(percpu_stats, 0, sizeof(*percpu_stats));
|
|
+
|
|
+ percpu_extras = per_cpu_ptr(priv->percpu_extras, i);
|
|
+ memset(percpu_extras, 0, sizeof(*percpu_extras));
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < priv->num_fqs; i++) {
|
|
+ fq = &priv->fq[i];
|
|
+ memset(&fq->stats, 0, sizeof(fq->stats));
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < priv->num_channels; i++) {
|
|
+ ch = priv->channel[i];
|
|
+ memset(&ch->stats, 0, sizeof(ch->stats));
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const struct file_operations dpaa2_dbg_reset_ops = {
|
|
+ .open = simple_open,
|
|
+ .write = dpaa2_dbg_reset_write,
|
|
+};
|
|
+
|
|
+static ssize_t dpaa2_dbg_reset_mc_write(struct file *file,
|
|
+ const char __user *buf,
|
|
+ size_t count, loff_t *offset)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = file->private_data;
|
|
+ int err;
|
|
+
|
|
+ err = dpni_reset_statistics(priv->mc_io, 0, priv->mc_token);
|
|
+ if (err)
|
|
+ netdev_err(priv->net_dev,
|
|
+ "dpni_reset_statistics() failed %d\n", err);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const struct file_operations dpaa2_dbg_reset_mc_ops = {
|
|
+ .open = simple_open,
|
|
+ .write = dpaa2_dbg_reset_mc_write,
|
|
+};
|
|
+
|
|
+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ if (!dpaa2_dbg_root)
|
|
+ return;
|
|
+
|
|
+ /* Create a directory for the interface */
|
|
+ priv->dbg.dir = debugfs_create_dir(priv->net_dev->name,
|
|
+ dpaa2_dbg_root);
|
|
+ if (!priv->dbg.dir) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_dir() failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* per-cpu stats file */
|
|
+ priv->dbg.cpu_stats = debugfs_create_file("cpu_stats", 0444,
|
|
+ priv->dbg.dir, priv,
|
|
+ &dpaa2_dbg_cpu_ops);
|
|
+ if (!priv->dbg.cpu_stats) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
|
|
+ goto err_cpu_stats;
|
|
+ }
|
|
+
|
|
+ /* per-fq stats file */
|
|
+ priv->dbg.fq_stats = debugfs_create_file("fq_stats", 0444,
|
|
+ priv->dbg.dir, priv,
|
|
+ &dpaa2_dbg_fq_ops);
|
|
+ if (!priv->dbg.fq_stats) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
|
|
+ goto err_fq_stats;
|
|
+ }
|
|
+
|
|
+ /* per-fq stats file */
|
|
+ priv->dbg.ch_stats = debugfs_create_file("ch_stats", 0444,
|
|
+ priv->dbg.dir, priv,
|
|
+ &dpaa2_dbg_ch_ops);
|
|
+ if (!priv->dbg.fq_stats) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
|
|
+ goto err_ch_stats;
|
|
+ }
|
|
+
|
|
+ /* reset stats */
|
|
+ priv->dbg.reset_stats = debugfs_create_file("reset_stats", 0200,
|
|
+ priv->dbg.dir, priv,
|
|
+ &dpaa2_dbg_reset_ops);
|
|
+ if (!priv->dbg.reset_stats) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
|
|
+ goto err_reset_stats;
|
|
+ }
|
|
+
|
|
+ /* reset MC stats */
|
|
+ priv->dbg.reset_mc_stats = debugfs_create_file("reset_mc_stats",
|
|
+ 0222, priv->dbg.dir, priv,
|
|
+ &dpaa2_dbg_reset_mc_ops);
|
|
+ if (!priv->dbg.reset_mc_stats) {
|
|
+ netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
|
|
+ goto err_reset_mc_stats;
|
|
+ }
|
|
+
|
|
+ return;
|
|
+
|
|
+err_reset_mc_stats:
|
|
+ debugfs_remove(priv->dbg.reset_stats);
|
|
+err_reset_stats:
|
|
+ debugfs_remove(priv->dbg.ch_stats);
|
|
+err_ch_stats:
|
|
+ debugfs_remove(priv->dbg.fq_stats);
|
|
+err_fq_stats:
|
|
+ debugfs_remove(priv->dbg.cpu_stats);
|
|
+err_cpu_stats:
|
|
+ debugfs_remove(priv->dbg.dir);
|
|
+}
|
|
+
|
|
+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ debugfs_remove(priv->dbg.reset_mc_stats);
|
|
+ debugfs_remove(priv->dbg.reset_stats);
|
|
+ debugfs_remove(priv->dbg.fq_stats);
|
|
+ debugfs_remove(priv->dbg.ch_stats);
|
|
+ debugfs_remove(priv->dbg.cpu_stats);
|
|
+ debugfs_remove(priv->dbg.dir);
|
|
+}
|
|
+
|
|
+void dpaa2_eth_dbg_init(void)
|
|
+{
|
|
+ dpaa2_dbg_root = debugfs_create_dir(DPAA2_ETH_DBG_ROOT, NULL);
|
|
+ if (!dpaa2_dbg_root) {
|
|
+ pr_err("DPAA2-ETH: debugfs create failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pr_info("DPAA2-ETH: debugfs created\n");
|
|
+}
|
|
+
|
|
+void __exit dpaa2_eth_dbg_exit(void)
|
|
+{
|
|
+ debugfs_remove(dpaa2_dbg_root);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h
|
|
@@ -0,0 +1,60 @@
|
|
+/* Copyright 2015 Freescale Semiconductor Inc.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * * Neither the name of Freescale Semiconductor nor the
|
|
+ * names of its contributors may be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ *
|
|
+ * ALTERNATIVELY, this software may be distributed under the terms of the
|
|
+ * GNU General Public License ("GPL") as published by the Free Software
|
|
+ * Foundation, either version 2 of that License or (at your option) any
|
|
+ * later version.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
|
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
|
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#ifndef DPAA2_ETH_DEBUGFS_H
|
|
+#define DPAA2_ETH_DEBUGFS_H
|
|
+
|
|
+#include <linux/dcache.h>
|
|
+
|
|
+struct dpaa2_eth_priv;
|
|
+
|
|
+struct dpaa2_debugfs {
|
|
+ struct dentry *dir;
|
|
+ struct dentry *fq_stats;
|
|
+ struct dentry *ch_stats;
|
|
+ struct dentry *cpu_stats;
|
|
+ struct dentry *reset_stats;
|
|
+ struct dentry *reset_mc_stats;
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS
|
|
+void dpaa2_eth_dbg_init(void);
|
|
+void dpaa2_eth_dbg_exit(void);
|
|
+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv);
|
|
+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv);
|
|
+#else
|
|
+static inline void dpaa2_eth_dbg_init(void) {}
|
|
+static inline void dpaa2_eth_dbg_exit(void) {}
|
|
+static inline void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) {}
|
|
+static inline void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) {}
|
|
+#endif /* CONFIG_FSL_DPAA2_ETH_DEBUGFS */
|
|
+
|
|
+#endif /* DPAA2_ETH_DEBUGFS_H */
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
|
|
@@ -38,9 +38,14 @@
|
|
#include <linux/msi.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/iommu.h>
|
|
-
|
|
+#include <linux/net_tstamp.h>
|
|
+#include <linux/bpf.h>
|
|
+#include <linux/filter.h>
|
|
+#include <linux/atomic.h>
|
|
+#include <net/sock.h>
|
|
#include "../../fsl-mc/include/mc.h"
|
|
#include "dpaa2-eth.h"
|
|
+#include "dpaa2-eth-ceetm.h"
|
|
|
|
/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files
|
|
* using trace events only need to #include <trace/events/sched.h>
|
|
@@ -104,13 +109,15 @@ static void free_rx_fd(struct dpaa2_eth_
|
|
/* We don't support any other format */
|
|
return;
|
|
|
|
- /* For S/G frames, we first need to free all SG entries */
|
|
+ /* For S/G frames, we first need to free all SG entries
|
|
+ * except the first one, which was taken care of already
|
|
+ */
|
|
sgt = vaddr + dpaa2_fd_get_offset(fd);
|
|
- for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) {
|
|
+ for (i = 1; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) {
|
|
addr = dpaa2_sg_get_addr(&sgt[i]);
|
|
sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
|
|
dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
- DMA_FROM_DEVICE);
|
|
+ DMA_BIDIRECTIONAL);
|
|
|
|
skb_free_frag(sg_vaddr);
|
|
if (dpaa2_sg_is_final(&sgt[i]))
|
|
@@ -133,8 +140,7 @@ static struct sk_buff *build_linear_skb(
|
|
|
|
ch->buf_count--;
|
|
|
|
- skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_SIZE +
|
|
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
|
|
+ skb = build_skb(fd_vaddr, DPAA2_ETH_SKB_SIZE);
|
|
if (unlikely(!skb))
|
|
return NULL;
|
|
|
|
@@ -170,15 +176,19 @@ static struct sk_buff *build_frag_skb(st
|
|
sg_addr = dpaa2_sg_get_addr(sge);
|
|
sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, sg_addr);
|
|
dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
- DMA_FROM_DEVICE);
|
|
+ DMA_BIDIRECTIONAL);
|
|
|
|
sg_length = dpaa2_sg_get_len(sge);
|
|
|
|
if (i == 0) {
|
|
/* We build the skb around the first data buffer */
|
|
- skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_SIZE +
|
|
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
|
|
+ skb = build_skb(sg_vaddr, DPAA2_ETH_SKB_SIZE);
|
|
if (unlikely(!skb)) {
|
|
+ /* Free the first SG entry now, since we already
|
|
+ * unmapped it and obtained the virtual address
|
|
+ */
|
|
+ skb_free_frag(sg_vaddr);
|
|
+
|
|
/* We still need to subtract the buffers used
|
|
* by this FD from our software counter
|
|
*/
|
|
@@ -213,17 +223,173 @@ static struct sk_buff *build_frag_skb(st
|
|
break;
|
|
}
|
|
|
|
+ WARN_ONCE(i == DPAA2_ETH_MAX_SG_ENTRIES, "Final bit not set in SGT");
|
|
+
|
|
/* Count all data buffers + SG table buffer */
|
|
ch->buf_count -= i + 2;
|
|
|
|
return skb;
|
|
}
|
|
|
|
+static int dpaa2_eth_xdp_tx(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_fd *fd,
|
|
+ void *buf_start,
|
|
+ u16 queue_id)
|
|
+{
|
|
+ struct dpaa2_eth_fq *fq;
|
|
+ struct rtnl_link_stats64 *percpu_stats;
|
|
+ struct dpaa2_eth_drv_stats *percpu_extras;
|
|
+ struct dpaa2_faead *faead;
|
|
+ u32 ctrl, frc;
|
|
+ int i, err;
|
|
+
|
|
+ /* Mark the egress frame annotation area as valid */
|
|
+ frc = dpaa2_fd_get_frc(fd);
|
|
+ dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV);
|
|
+ dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL);
|
|
+
|
|
+ ctrl = DPAA2_FAEAD_A4V | DPAA2_FAEAD_A2V | DPAA2_FAEAD_EBDDV;
|
|
+ faead = dpaa2_get_faead(buf_start, false);
|
|
+ faead->ctrl = cpu_to_le32(ctrl);
|
|
+ faead->conf_fqid = 0;
|
|
+
|
|
+ percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
+ percpu_extras = this_cpu_ptr(priv->percpu_extras);
|
|
+
|
|
+ fq = &priv->fq[queue_id];
|
|
+ for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
|
|
+ err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
|
|
+ priv->tx_qdid, 0,
|
|
+ fq->tx_qdbin, fd);
|
|
+ if (err != -EBUSY)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ percpu_extras->tx_portal_busy += i;
|
|
+ if (unlikely(err)) {
|
|
+ percpu_stats->tx_errors++;
|
|
+ } else {
|
|
+ percpu_stats->tx_packets++;
|
|
+ percpu_stats->tx_bytes += dpaa2_fd_get_len(fd);
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ void *vaddr;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ /* Same logic as on regular Rx path */
|
|
+ vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
|
|
+ dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ skb_free_frag(vaddr);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void release_fd_buf(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_eth_channel *ch,
|
|
+ dma_addr_t addr)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ ch->rel_buf_array[ch->rel_buf_cnt++] = addr;
|
|
+ if (likely(ch->rel_buf_cnt < DPAA2_ETH_BUFS_PER_CMD))
|
|
+ return;
|
|
+
|
|
+ while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid,
|
|
+ ch->rel_buf_array,
|
|
+ ch->rel_buf_cnt)) == -EBUSY)
|
|
+ cpu_relax();
|
|
+
|
|
+ if (err)
|
|
+ free_bufs(priv, ch->rel_buf_array, ch->rel_buf_cnt);
|
|
+
|
|
+ ch->rel_buf_cnt = 0;
|
|
+}
|
|
+
|
|
+static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_eth_channel *ch,
|
|
+ struct dpaa2_fd *fd,
|
|
+ u16 queue_id,
|
|
+ void *vaddr)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ dma_addr_t addr = dpaa2_fd_get_addr(fd);
|
|
+ struct rtnl_link_stats64 *percpu_stats;
|
|
+ struct bpf_prog *xdp_prog;
|
|
+ struct xdp_buff xdp;
|
|
+ u32 xdp_act = XDP_PASS;
|
|
+
|
|
+ xdp_prog = READ_ONCE(ch->xdp_prog);
|
|
+ if (!xdp_prog)
|
|
+ return xdp_act;
|
|
+
|
|
+ percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
+
|
|
+ xdp.data = vaddr + dpaa2_fd_get_offset(fd);
|
|
+ xdp.data_end = xdp.data + dpaa2_fd_get_len(fd);
|
|
+ /* Allow the XDP program to use the specially reserved headroom */
|
|
+ xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
|
+
|
|
+ /* xdp.data pointer may have changed */
|
|
+ dpaa2_fd_set_offset(fd, xdp.data - vaddr);
|
|
+ dpaa2_fd_set_len(fd, xdp.data_end - xdp.data);
|
|
+
|
|
+ switch (xdp_act) {
|
|
+ case XDP_PASS:
|
|
+ break;
|
|
+ default:
|
|
+ bpf_warn_invalid_xdp_action(xdp_act);
|
|
+ case XDP_ABORTED:
|
|
+ case XDP_DROP:
|
|
+ /* This is our buffer, so we can release it back to hardware */
|
|
+ release_fd_buf(priv, ch, addr);
|
|
+ percpu_stats->rx_dropped++;
|
|
+ break;
|
|
+ case XDP_TX:
|
|
+ if (dpaa2_eth_xdp_tx(priv, fd, vaddr, queue_id)) {
|
|
+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ free_rx_fd(priv, fd, vaddr);
|
|
+ ch->buf_count--;
|
|
+ }
|
|
+ break;
|
|
+ case XDP_REDIRECT:
|
|
+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ ch->buf_count--;
|
|
+ ch->flush = true;
|
|
+ /* Mark the actual start of the data buffer */
|
|
+ xdp.data_hard_start = vaddr;
|
|
+ if (xdp_do_redirect(priv->net_dev, &xdp, xdp_prog))
|
|
+ free_rx_fd(priv, fd, vaddr);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (xdp_act == XDP_TX || xdp_act == XDP_REDIRECT) {
|
|
+ percpu_stats->rx_packets++;
|
|
+ percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
|
|
+ }
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return xdp_act;
|
|
+}
|
|
+
|
|
/* Main Rx frame processing routine */
|
|
static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
|
|
struct dpaa2_eth_channel *ch,
|
|
const struct dpaa2_fd *fd,
|
|
- struct napi_struct *napi)
|
|
+ struct napi_struct *napi,
|
|
+ u16 queue_id)
|
|
{
|
|
dma_addr_t addr = dpaa2_fd_get_addr(fd);
|
|
u8 fd_format = dpaa2_fd_get_format(fd);
|
|
@@ -235,14 +401,16 @@ static void dpaa2_eth_rx(struct dpaa2_et
|
|
struct dpaa2_fas *fas;
|
|
void *buf_data;
|
|
u32 status = 0;
|
|
+ u32 xdp_act;
|
|
|
|
/* Tracing point */
|
|
trace_dpaa2_rx_fd(priv->net_dev, fd);
|
|
|
|
vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
|
|
- dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
|
+ dma_sync_single_for_cpu(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
|
|
- fas = dpaa2_get_fas(vaddr);
|
|
+ fas = dpaa2_get_fas(vaddr, false);
|
|
prefetch(fas);
|
|
buf_data = vaddr + dpaa2_fd_get_offset(fd);
|
|
prefetch(buf_data);
|
|
@@ -251,22 +419,41 @@ static void dpaa2_eth_rx(struct dpaa2_et
|
|
percpu_extras = this_cpu_ptr(priv->percpu_extras);
|
|
|
|
if (fd_format == dpaa2_fd_single) {
|
|
+ xdp_act = dpaa2_eth_run_xdp(priv, ch, (struct dpaa2_fd *)fd,
|
|
+ queue_id, vaddr);
|
|
+ if (xdp_act != XDP_PASS)
|
|
+ return;
|
|
+
|
|
+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
skb = build_linear_skb(priv, ch, fd, vaddr);
|
|
} else if (fd_format == dpaa2_fd_sg) {
|
|
+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
+ DMA_BIDIRECTIONAL);
|
|
skb = build_frag_skb(priv, ch, buf_data);
|
|
skb_free_frag(vaddr);
|
|
percpu_extras->rx_sg_frames++;
|
|
percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd);
|
|
} else {
|
|
/* We don't support any other format */
|
|
- goto err_frame_format;
|
|
+ goto drop_cnt;
|
|
}
|
|
|
|
if (unlikely(!skb))
|
|
- goto err_build_skb;
|
|
+ goto drop_fd;
|
|
|
|
prefetch(skb->data);
|
|
|
|
+ /* Get the timestamp value */
|
|
+ if (priv->ts_rx_en) {
|
|
+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
|
|
+ u64 *ns = dpaa2_get_ts(vaddr, false);
|
|
+
|
|
+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns);
|
|
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
|
|
+ shhwtstamps->hwtstamp = ns_to_ktime(*ns);
|
|
+ }
|
|
+
|
|
/* Check if we need to validate the L4 csum */
|
|
if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) {
|
|
status = le32_to_cpu(fas->status);
|
|
@@ -275,6 +462,12 @@ static void dpaa2_eth_rx(struct dpaa2_et
|
|
|
|
skb->protocol = eth_type_trans(skb, priv->net_dev);
|
|
|
|
+ /* Record Rx queue - this will be used when picking a Tx queue to
|
|
+ * forward the frames. We're keeping flow affinity through the
|
|
+ * network stack.
|
|
+ */
|
|
+ skb_record_rx_queue(skb, queue_id);
|
|
+
|
|
percpu_stats->rx_packets++;
|
|
percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
|
|
|
|
@@ -282,22 +475,74 @@ static void dpaa2_eth_rx(struct dpaa2_et
|
|
|
|
return;
|
|
|
|
-err_build_skb:
|
|
+drop_fd:
|
|
free_rx_fd(priv, fd, vaddr);
|
|
-err_frame_format:
|
|
+drop_cnt:
|
|
percpu_stats->rx_dropped++;
|
|
}
|
|
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
+/* Processing of Rx frames received on the error FQ
|
|
+ * We check and print the error bits and then free the frame
|
|
+ */
|
|
+static void dpaa2_eth_rx_err(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_eth_channel *ch,
|
|
+ const struct dpaa2_fd *fd,
|
|
+ struct napi_struct *napi __always_unused,
|
|
+ u16 queue_id __always_unused)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ dma_addr_t addr = dpaa2_fd_get_addr(fd);
|
|
+ void *vaddr;
|
|
+ struct rtnl_link_stats64 *percpu_stats;
|
|
+ struct dpaa2_fas *fas;
|
|
+ u32 status = 0;
|
|
+ u32 fd_errors;
|
|
+ bool has_fas_errors = false;
|
|
+
|
|
+ vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
|
|
+ dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_BIDIRECTIONAL);
|
|
+
|
|
+ /* check frame errors in the FD field */
|
|
+ fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_RX_ERR_MASK;
|
|
+ if (likely(fd_errors)) {
|
|
+ has_fas_errors = (fd_errors & FD_CTRL_FAERR) &&
|
|
+ !!(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV);
|
|
+ if (net_ratelimit())
|
|
+ netdev_dbg(priv->net_dev, "RX frame FD err: %08x\n",
|
|
+ fd_errors);
|
|
+ }
|
|
+
|
|
+ /* check frame errors in the FAS field */
|
|
+ if (has_fas_errors) {
|
|
+ fas = dpaa2_get_fas(vaddr, false);
|
|
+ status = le32_to_cpu(fas->status);
|
|
+ if (net_ratelimit())
|
|
+ netdev_dbg(priv->net_dev, "Rx frame FAS err: 0x%08x\n",
|
|
+ status & DPAA2_FAS_RX_ERR_MASK);
|
|
+ }
|
|
+ free_rx_fd(priv, fd, vaddr);
|
|
+
|
|
+ percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
+ percpu_stats->rx_errors++;
|
|
+ ch->buf_count--;
|
|
+}
|
|
+#endif
|
|
+
|
|
/* Consume all frames pull-dequeued into the store. This is the simplest way to
|
|
* make sure we don't accidentally issue another volatile dequeue which would
|
|
* overwrite (leak) frames already in the store.
|
|
*
|
|
+ * The number of frames is returned using the last 2 output arguments,
|
|
+ * separately for Rx and Tx confirmations.
|
|
+ *
|
|
* Observance of NAPI budget is not our concern, leaving that to the caller.
|
|
*/
|
|
-static int consume_frames(struct dpaa2_eth_channel *ch)
|
|
+static bool consume_frames(struct dpaa2_eth_channel *ch, int *rx_cleaned,
|
|
+ int *tx_conf_cleaned)
|
|
{
|
|
struct dpaa2_eth_priv *priv = ch->priv;
|
|
- struct dpaa2_eth_fq *fq;
|
|
+ struct dpaa2_eth_fq *fq = NULL;
|
|
struct dpaa2_dq *dq;
|
|
const struct dpaa2_fd *fd;
|
|
int cleaned = 0;
|
|
@@ -315,14 +560,60 @@ static int consume_frames(struct dpaa2_e
|
|
}
|
|
|
|
fd = dpaa2_dq_fd(dq);
|
|
- fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq);
|
|
- fq->stats.frames++;
|
|
+ prefetch(fd);
|
|
|
|
- fq->consume(priv, ch, fd, &ch->napi);
|
|
+ fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq);
|
|
+ fq->consume(priv, ch, fd, &ch->napi, fq->flowid);
|
|
cleaned++;
|
|
} while (!is_last);
|
|
|
|
- return cleaned;
|
|
+ if (!cleaned)
|
|
+ return false;
|
|
+
|
|
+ /* All frames brought in store by a volatile dequeue
|
|
+ * come from the same queue
|
|
+ */
|
|
+ if (fq->type == DPAA2_TX_CONF_FQ) {
|
|
+ *tx_conf_cleaned += cleaned;
|
|
+ } else {
|
|
+ *rx_cleaned += cleaned;
|
|
+ /* If we processed XDP_REDIRECT frames, flush them now */
|
|
+ /* FIXME: Since we don't actually do anything inside
|
|
+ * ndo_xdp_flush, we call it here simply for compliance
|
|
+ * reasons
|
|
+ */
|
|
+ if (ch->flush) {
|
|
+ xdp_do_flush_map();
|
|
+ ch->flush = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fq->stats.frames += cleaned;
|
|
+ ch->stats.frames += cleaned;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/* Configure the egress frame annotation for timestamp update */
|
|
+static void enable_tx_tstamp(struct dpaa2_fd *fd, void *buf_start)
|
|
+{
|
|
+ struct dpaa2_faead *faead;
|
|
+ u32 ctrl, frc;
|
|
+
|
|
+ /* Mark the egress frame annotation area as valid */
|
|
+ frc = dpaa2_fd_get_frc(fd);
|
|
+ dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV);
|
|
+
|
|
+ /* Set hardware annotation size */
|
|
+ ctrl = dpaa2_fd_get_ctrl(fd);
|
|
+ dpaa2_fd_set_ctrl(fd, ctrl | DPAA2_FD_CTRL_ASAL);
|
|
+
|
|
+ /* enable UPD (update prepanded data) bit in FAEAD field of
|
|
+ * hardware frame annotation area
|
|
+ */
|
|
+ ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD;
|
|
+ faead = dpaa2_get_faead(buf_start, true);
|
|
+ faead->ctrl = cpu_to_le32(ctrl);
|
|
}
|
|
|
|
/* Create a frame descriptor based on a fragmented skb */
|
|
@@ -341,7 +632,6 @@ static int build_sg_fd(struct dpaa2_eth_
|
|
int num_sg;
|
|
int num_dma_bufs;
|
|
struct dpaa2_eth_swa *swa;
|
|
- struct dpaa2_fas *fas;
|
|
|
|
/* Create and map scatterlist.
|
|
* We don't advertise NETIF_F_FRAGLIST, so skb_to_sgvec() will not have
|
|
@@ -365,21 +655,14 @@ static int build_sg_fd(struct dpaa2_eth_
|
|
|
|
/* Prepare the HW SGT structure */
|
|
sgt_buf_size = priv->tx_data_offset +
|
|
- sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs);
|
|
- sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, GFP_ATOMIC);
|
|
+ sizeof(struct dpaa2_sg_entry) * num_dma_bufs;
|
|
+ sgt_buf = netdev_alloc_frag(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN);
|
|
if (unlikely(!sgt_buf)) {
|
|
err = -ENOMEM;
|
|
goto sgt_buf_alloc_failed;
|
|
}
|
|
sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN);
|
|
-
|
|
- /* PTA from egress side is passed as is to the confirmation side so
|
|
- * we need to clear some fields here in order to find consistent values
|
|
- * on TX confirmation. We are clearing FAS (Frame Annotation Status)
|
|
- * field from the hardware annotation area
|
|
- */
|
|
- fas = dpaa2_get_fas(sgt_buf);
|
|
- memset(fas, 0, DPAA2_FAS_SIZE);
|
|
+ memset(sgt_buf, 0, sgt_buf_size);
|
|
|
|
sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset);
|
|
|
|
@@ -402,10 +685,11 @@ static int build_sg_fd(struct dpaa2_eth_
|
|
* all of them on Tx Conf.
|
|
*/
|
|
swa = (struct dpaa2_eth_swa *)sgt_buf;
|
|
- swa->skb = skb;
|
|
- swa->scl = scl;
|
|
- swa->num_sg = num_sg;
|
|
- swa->num_dma_bufs = num_dma_bufs;
|
|
+ swa->type = DPAA2_ETH_SWA_SG;
|
|
+ swa->sg.skb = skb;
|
|
+ swa->sg.scl = scl;
|
|
+ swa->sg.num_sg = num_sg;
|
|
+ swa->sg.sgt_size = sgt_buf_size;
|
|
|
|
/* Separately map the SGT buffer */
|
|
addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL);
|
|
@@ -417,13 +701,15 @@ static int build_sg_fd(struct dpaa2_eth_
|
|
dpaa2_fd_set_format(fd, dpaa2_fd_sg);
|
|
dpaa2_fd_set_addr(fd, addr);
|
|
dpaa2_fd_set_len(fd, skb->len);
|
|
- dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA |
|
|
- DPAA2_FD_CTRL_PTV1);
|
|
+ dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA);
|
|
+
|
|
+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
|
|
+ enable_tx_tstamp(fd, sgt_buf);
|
|
|
|
return 0;
|
|
|
|
dma_map_single_failed:
|
|
- kfree(sgt_buf);
|
|
+ skb_free_frag(sgt_buf);
|
|
sgt_buf_alloc_failed:
|
|
dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL);
|
|
dma_map_sg_failed:
|
|
@@ -437,29 +723,27 @@ static int build_single_fd(struct dpaa2_
|
|
struct dpaa2_fd *fd)
|
|
{
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
- u8 *buffer_start;
|
|
- struct dpaa2_fas *fas;
|
|
- struct sk_buff **skbh;
|
|
+ u8 *buffer_start, *aligned_start;
|
|
+ struct dpaa2_eth_swa *swa;
|
|
dma_addr_t addr;
|
|
|
|
- buffer_start = PTR_ALIGN(skb->data - priv->tx_data_offset -
|
|
- DPAA2_ETH_TX_BUF_ALIGN,
|
|
- DPAA2_ETH_TX_BUF_ALIGN);
|
|
-
|
|
- /* PTA from egress side is passed as is to the confirmation side so
|
|
- * we need to clear some fields here in order to find consistent values
|
|
- * on TX confirmation. We are clearing FAS (Frame Annotation Status)
|
|
- * field from the hardware annotation area
|
|
+ buffer_start = skb->data - dpaa2_eth_needed_headroom(priv, skb);
|
|
+
|
|
+ /* If there's enough room to align the FD address, do it.
|
|
+ * It will help hardware optimize accesses.
|
|
*/
|
|
- fas = dpaa2_get_fas(buffer_start);
|
|
- memset(fas, 0, DPAA2_FAS_SIZE);
|
|
+ aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN,
|
|
+ DPAA2_ETH_TX_BUF_ALIGN);
|
|
+ if (aligned_start >= skb->head)
|
|
+ buffer_start = aligned_start;
|
|
|
|
/* Store a backpointer to the skb at the beginning of the buffer
|
|
* (in the private data area) such that we can release it
|
|
* on Tx confirm
|
|
*/
|
|
- skbh = (struct sk_buff **)buffer_start;
|
|
- *skbh = skb;
|
|
+ swa = (struct dpaa2_eth_swa *)buffer_start;
|
|
+ swa->type = DPAA2_ETH_SWA_SINGLE;
|
|
+ swa->single.skb = skb;
|
|
|
|
addr = dma_map_single(dev, buffer_start,
|
|
skb_tail_pointer(skb) - buffer_start,
|
|
@@ -471,8 +755,10 @@ static int build_single_fd(struct dpaa2_
|
|
dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start));
|
|
dpaa2_fd_set_len(fd, skb->len);
|
|
dpaa2_fd_set_format(fd, dpaa2_fd_single);
|
|
- dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA |
|
|
- DPAA2_FD_CTRL_PTV1);
|
|
+ dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA);
|
|
+
|
|
+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
|
|
+ enable_tx_tstamp(fd, buffer_start);
|
|
|
|
return 0;
|
|
}
|
|
@@ -486,92 +772,128 @@ static int build_single_fd(struct dpaa2_
|
|
* Optionally, return the frame annotation status word (FAS), which needs
|
|
* to be checked if we're on the confirmation path.
|
|
*/
|
|
-static void free_tx_fd(const struct dpaa2_eth_priv *priv,
|
|
+static void free_tx_fd(struct dpaa2_eth_priv *priv,
|
|
const struct dpaa2_fd *fd,
|
|
- u32 *status)
|
|
+ bool in_napi)
|
|
{
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
dma_addr_t fd_addr;
|
|
- struct sk_buff **skbh, *skb;
|
|
+ struct sk_buff *skb = NULL;
|
|
unsigned char *buffer_start;
|
|
- int unmap_size;
|
|
- struct scatterlist *scl;
|
|
- int num_sg, num_dma_bufs;
|
|
struct dpaa2_eth_swa *swa;
|
|
u8 fd_format = dpaa2_fd_get_format(fd);
|
|
- struct dpaa2_fas *fas;
|
|
|
|
fd_addr = dpaa2_fd_get_addr(fd);
|
|
- skbh = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr);
|
|
- fas = dpaa2_get_fas(skbh);
|
|
+ buffer_start = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr);
|
|
+ swa = (struct dpaa2_eth_swa *)buffer_start;
|
|
|
|
if (fd_format == dpaa2_fd_single) {
|
|
- skb = *skbh;
|
|
- buffer_start = (unsigned char *)skbh;
|
|
- /* Accessing the skb buffer is safe before dma unmap, because
|
|
- * we didn't map the actual skb shell.
|
|
- */
|
|
- dma_unmap_single(dev, fd_addr,
|
|
- skb_tail_pointer(skb) - buffer_start,
|
|
- DMA_BIDIRECTIONAL);
|
|
+ if (swa->type == DPAA2_ETH_SWA_SINGLE) {
|
|
+ skb = swa->single.skb;
|
|
+ /* Accessing the skb buffer is safe before dma unmap,
|
|
+ * because we didn't map the actual skb shell.
|
|
+ */
|
|
+ dma_unmap_single(dev, fd_addr,
|
|
+ skb_tail_pointer(skb) - buffer_start,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ } else {
|
|
+ WARN_ONCE(swa->type != DPAA2_ETH_SWA_XDP,
|
|
+ "Wrong SWA type");
|
|
+ dma_unmap_single(dev, fd_addr, swa->xdp.dma_size,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ }
|
|
} else if (fd_format == dpaa2_fd_sg) {
|
|
- swa = (struct dpaa2_eth_swa *)skbh;
|
|
- skb = swa->skb;
|
|
- scl = swa->scl;
|
|
- num_sg = swa->num_sg;
|
|
- num_dma_bufs = swa->num_dma_bufs;
|
|
+ skb = swa->sg.skb;
|
|
|
|
/* Unmap the scatterlist */
|
|
- dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL);
|
|
- kfree(scl);
|
|
+ dma_unmap_sg(dev, swa->sg.scl, swa->sg.num_sg, DMA_BIDIRECTIONAL);
|
|
+ kfree(swa->sg.scl);
|
|
|
|
/* Unmap the SGT buffer */
|
|
- unmap_size = priv->tx_data_offset +
|
|
- sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs);
|
|
- dma_unmap_single(dev, fd_addr, unmap_size, DMA_BIDIRECTIONAL);
|
|
+ dma_unmap_single(dev, fd_addr, swa->sg.sgt_size,
|
|
+ DMA_BIDIRECTIONAL);
|
|
} else {
|
|
- /* Unsupported format, mark it as errored and give up */
|
|
- if (status)
|
|
- *status = ~0;
|
|
+ netdev_dbg(priv->net_dev, "Invalid FD format\n");
|
|
return;
|
|
}
|
|
|
|
- /* Read the status from the Frame Annotation after we unmap the first
|
|
- * buffer but before we free it. The caller function is responsible
|
|
- * for checking the status value.
|
|
- */
|
|
- if (status)
|
|
- *status = le32_to_cpu(fas->status);
|
|
+ if (swa->type == DPAA2_ETH_SWA_XDP) {
|
|
+ page_frag_free(buffer_start);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Get the timestamp value */
|
|
+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
|
|
+ struct skb_shared_hwtstamps shhwtstamps;
|
|
+ u64 *ns;
|
|
+
|
|
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
|
|
+
|
|
+ ns = dpaa2_get_ts(buffer_start, true);
|
|
+ *ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ns);
|
|
+ shhwtstamps.hwtstamp = ns_to_ktime(*ns);
|
|
+ skb_tstamp_tx(skb, &shhwtstamps);
|
|
+ }
|
|
|
|
- /* Free SGT buffer kmalloc'ed on tx */
|
|
+ /* Free SGT buffer allocated on tx */
|
|
if (fd_format != dpaa2_fd_single)
|
|
- kfree(skbh);
|
|
+ skb_free_frag(buffer_start);
|
|
|
|
/* Move on with skb release */
|
|
- dev_kfree_skb(skb);
|
|
+ napi_consume_skb(skb, in_napi);
|
|
}
|
|
|
|
static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
|
|
{
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct device *dev = net_dev->dev.parent;
|
|
struct dpaa2_fd fd;
|
|
struct rtnl_link_stats64 *percpu_stats;
|
|
struct dpaa2_eth_drv_stats *percpu_extras;
|
|
struct dpaa2_eth_fq *fq;
|
|
u16 queue_mapping;
|
|
- int err, i;
|
|
+ unsigned int needed_headroom;
|
|
+ u8 prio;
|
|
+ int err, i, ch_id = 0;
|
|
+
|
|
+ queue_mapping = skb_get_queue_mapping(skb);
|
|
+ prio = netdev_txq_to_tc(net_dev, queue_mapping);
|
|
+
|
|
+ /* Hardware interprets priority level 0 as being the highest,
|
|
+ * so we need to do a reverse mapping to the netdev tc index
|
|
+ */
|
|
+ if (net_dev->num_tc)
|
|
+ prio = net_dev->num_tc - prio - 1;
|
|
+
|
|
+ queue_mapping %= dpaa2_eth_queue_count(priv);
|
|
+ fq = &priv->fq[queue_mapping];
|
|
+
|
|
+ /* If we're congested, stop this tx queue; transmission of
|
|
+ * the current skb happens regardless of congestion state
|
|
+ */
|
|
+ dma_sync_single_for_cpu(dev, priv->cscn_dma,
|
|
+ DPAA2_CSCN_SIZE, DMA_FROM_DEVICE);
|
|
+ if (unlikely(dpaa2_cscn_state_congested(priv->cscn_mem))) {
|
|
+ netif_stop_subqueue(net_dev, queue_mapping);
|
|
+ fq->stats.congestion_entry++;
|
|
+ }
|
|
|
|
percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
percpu_extras = this_cpu_ptr(priv->percpu_extras);
|
|
|
|
- if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) {
|
|
+ /* For non-linear skb we don't need a minimum headroom */
|
|
+ needed_headroom = dpaa2_eth_needed_headroom(priv, skb);
|
|
+ if (skb_headroom(skb) < needed_headroom) {
|
|
struct sk_buff *ns;
|
|
|
|
- ns = skb_realloc_headroom(skb, DPAA2_ETH_NEEDED_HEADROOM(priv));
|
|
+ ns = skb_realloc_headroom(skb, needed_headroom);
|
|
if (unlikely(!ns)) {
|
|
percpu_stats->tx_dropped++;
|
|
goto err_alloc_headroom;
|
|
}
|
|
+ percpu_extras->tx_reallocs++;
|
|
+ if (skb->sk)
|
|
+ skb_set_owner_w(ns, skb->sk);
|
|
dev_kfree_skb(skb);
|
|
skb = ns;
|
|
}
|
|
@@ -605,13 +927,15 @@ static netdev_tx_t dpaa2_eth_tx(struct s
|
|
/* Tracing point */
|
|
trace_dpaa2_tx_fd(net_dev, &fd);
|
|
|
|
- /* TxConf FQ selection primarily based on cpu affinity; this is
|
|
- * non-migratable context, so it's safe to call smp_processor_id().
|
|
- */
|
|
- queue_mapping = smp_processor_id() % dpaa2_eth_queue_count(priv);
|
|
- fq = &priv->fq[queue_mapping];
|
|
+ if (dpaa2_eth_ceetm_is_enabled(priv)) {
|
|
+ err = dpaa2_ceetm_classify(skb, net_dev->qdisc, &ch_id, &prio);
|
|
+ if (err)
|
|
+ goto err_ceetm_classify;
|
|
+ }
|
|
+
|
|
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
|
|
- err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0,
|
|
+ err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
|
|
+ priv->tx_qdid, prio,
|
|
fq->tx_qdbin, &fd);
|
|
if (err != -EBUSY)
|
|
break;
|
|
@@ -620,7 +944,7 @@ static netdev_tx_t dpaa2_eth_tx(struct s
|
|
if (unlikely(err < 0)) {
|
|
percpu_stats->tx_errors++;
|
|
/* Clean up everything, including freeing the skb */
|
|
- free_tx_fd(priv, &fd, NULL);
|
|
+ free_tx_fd(priv, &fd, false);
|
|
} else {
|
|
percpu_stats->tx_packets++;
|
|
percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
|
|
@@ -628,6 +952,8 @@ static netdev_tx_t dpaa2_eth_tx(struct s
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
+err_ceetm_classify:
|
|
+ free_tx_fd(priv, &fd, false);
|
|
err_build_fd:
|
|
err_alloc_headroom:
|
|
dev_kfree_skb(skb);
|
|
@@ -639,13 +965,13 @@ err_alloc_headroom:
|
|
static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
|
|
struct dpaa2_eth_channel *ch,
|
|
const struct dpaa2_fd *fd,
|
|
- struct napi_struct *napi __always_unused)
|
|
+ struct napi_struct *napi __always_unused,
|
|
+ u16 queue_id)
|
|
{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
struct rtnl_link_stats64 *percpu_stats;
|
|
struct dpaa2_eth_drv_stats *percpu_extras;
|
|
- u32 status = 0;
|
|
u32 fd_errors;
|
|
- bool has_fas_errors = false;
|
|
|
|
/* Tracing point */
|
|
trace_dpaa2_tx_conf_fd(priv->net_dev, fd);
|
|
@@ -654,31 +980,28 @@ static void dpaa2_eth_tx_conf(struct dpa
|
|
percpu_extras->tx_conf_frames++;
|
|
percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd);
|
|
|
|
- /* Check frame errors in the FD field */
|
|
- fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK;
|
|
- if (unlikely(fd_errors)) {
|
|
- /* We only check error bits in the FAS field if corresponding
|
|
- * FAERR bit is set in FD and the FAS field is marked as valid
|
|
- */
|
|
- has_fas_errors = (fd_errors & DPAA2_FD_CTRL_FAERR) &&
|
|
- !!(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV);
|
|
- if (net_ratelimit())
|
|
- netdev_dbg(priv->net_dev, "TX frame FD error: 0x%08x\n",
|
|
- fd_errors);
|
|
+ /* Check congestion state and wake all queues if necessary */
|
|
+ if (unlikely(__netif_subqueue_stopped(priv->net_dev, queue_id))) {
|
|
+ dma_sync_single_for_cpu(dev, priv->cscn_dma,
|
|
+ DPAA2_CSCN_SIZE, DMA_FROM_DEVICE);
|
|
+ if (!dpaa2_cscn_state_congested(priv->cscn_mem))
|
|
+ netif_tx_wake_all_queues(priv->net_dev);
|
|
}
|
|
|
|
- free_tx_fd(priv, fd, has_fas_errors ? &status : NULL);
|
|
+ /* Check frame errors in the FD field */
|
|
+ fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK;
|
|
+ free_tx_fd(priv, fd, true);
|
|
|
|
if (likely(!fd_errors))
|
|
return;
|
|
|
|
+ if (net_ratelimit())
|
|
+ netdev_dbg(priv->net_dev, "TX frame FD error: 0x%08x\n",
|
|
+ fd_errors);
|
|
+
|
|
percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
/* Tx-conf logically pertains to the egress path. */
|
|
percpu_stats->tx_errors++;
|
|
-
|
|
- if (has_fas_errors && net_ratelimit())
|
|
- netdev_dbg(priv->net_dev, "TX frame FAS error: 0x%08x\n",
|
|
- status & DPAA2_FAS_TX_ERR_MASK);
|
|
}
|
|
|
|
static int set_rx_csum(struct dpaa2_eth_priv *priv, bool enable)
|
|
@@ -728,26 +1051,27 @@ static int set_tx_csum(struct dpaa2_eth_
|
|
/* Perform a single release command to add buffers
|
|
* to the specified buffer pool
|
|
*/
|
|
-static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid)
|
|
+static int add_bufs(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_eth_channel *ch, u16 bpid)
|
|
{
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
|
|
void *buf;
|
|
dma_addr_t addr;
|
|
- int i;
|
|
+ int i, err;
|
|
|
|
for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
|
|
/* Allocate buffer visible to WRIOP + skb shared info +
|
|
* alignment padding
|
|
*/
|
|
- buf = napi_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE);
|
|
+ buf = napi_alloc_frag(dpaa2_eth_buf_raw_size(priv));
|
|
if (unlikely(!buf))
|
|
goto err_alloc;
|
|
|
|
- buf = PTR_ALIGN(buf, DPAA2_ETH_RX_BUF_ALIGN);
|
|
+ buf = PTR_ALIGN(buf, priv->rx_buf_align);
|
|
|
|
addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE,
|
|
- DMA_FROM_DEVICE);
|
|
+ DMA_BIDIRECTIONAL);
|
|
if (unlikely(dma_mapping_error(dev, addr)))
|
|
goto err_map;
|
|
|
|
@@ -755,28 +1079,31 @@ static int add_bufs(struct dpaa2_eth_pri
|
|
|
|
/* tracing point */
|
|
trace_dpaa2_eth_buf_seed(priv->net_dev,
|
|
- buf, DPAA2_ETH_BUF_RAW_SIZE,
|
|
+ buf, dpaa2_eth_buf_raw_size(priv),
|
|
addr, DPAA2_ETH_RX_BUF_SIZE,
|
|
bpid);
|
|
}
|
|
|
|
release_bufs:
|
|
- /* In case the portal is busy, retry until successful.
|
|
- * The buffer release function would only fail if the QBMan portal
|
|
- * was busy, which implies portal contention (i.e. more CPUs than
|
|
- * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes,
|
|
- * there is little we can realistically do, short of giving up -
|
|
- * in which case we'd risk depleting the buffer pool and never again
|
|
- * receiving the Rx interrupt which would kick-start the refill logic.
|
|
- * So just keep retrying, at the risk of being moved to ksoftirqd.
|
|
- */
|
|
- while (dpaa2_io_service_release(NULL, bpid, buf_array, i))
|
|
+ /* In case the portal is busy, retry until successful */
|
|
+ while ((err = dpaa2_io_service_release(ch->dpio, bpid,
|
|
+ buf_array, i)) == -EBUSY)
|
|
cpu_relax();
|
|
+
|
|
+ /* If release command failed, clean up and bail out; not much
|
|
+ * else we can do about it
|
|
+ */
|
|
+ if (err) {
|
|
+ free_bufs(priv, buf_array, i);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
return i;
|
|
|
|
err_map:
|
|
skb_free_frag(buf);
|
|
err_alloc:
|
|
+ /* If we managed to allocate at least some buffers, release them */
|
|
if (i)
|
|
goto release_bufs;
|
|
|
|
@@ -796,9 +1123,10 @@ static int seed_pool(struct dpaa2_eth_pr
|
|
*/
|
|
preempt_disable();
|
|
for (j = 0; j < priv->num_channels; j++) {
|
|
- for (i = 0; i < DPAA2_ETH_NUM_BUFS;
|
|
+ priv->channel[j]->buf_count = 0;
|
|
+ for (i = 0; i < priv->max_bufs_per_ch;
|
|
i += DPAA2_ETH_BUFS_PER_CMD) {
|
|
- new_count = add_bufs(priv, bpid);
|
|
+ new_count = add_bufs(priv, priv->channel[j], bpid);
|
|
priv->channel[j]->buf_count += new_count;
|
|
|
|
if (new_count < DPAA2_ETH_BUFS_PER_CMD) {
|
|
@@ -818,10 +1146,8 @@ static int seed_pool(struct dpaa2_eth_pr
|
|
*/
|
|
static void drain_bufs(struct dpaa2_eth_priv *priv, int count)
|
|
{
|
|
- struct device *dev = priv->net_dev->dev.parent;
|
|
u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
|
|
- void *vaddr;
|
|
- int ret, i;
|
|
+ int ret;
|
|
|
|
do {
|
|
ret = dpaa2_io_service_acquire(NULL, priv->bpid,
|
|
@@ -830,27 +1156,16 @@ static void drain_bufs(struct dpaa2_eth_
|
|
netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n");
|
|
return;
|
|
}
|
|
- for (i = 0; i < ret; i++) {
|
|
- /* Same logic as on regular Rx path */
|
|
- vaddr = dpaa2_iova_to_virt(priv->iommu_domain,
|
|
- buf_array[i]);
|
|
- dma_unmap_single(dev, buf_array[i],
|
|
- DPAA2_ETH_RX_BUF_SIZE,
|
|
- DMA_FROM_DEVICE);
|
|
- skb_free_frag(vaddr);
|
|
- }
|
|
+ free_bufs(priv, buf_array, ret);
|
|
} while (ret);
|
|
}
|
|
|
|
static void drain_pool(struct dpaa2_eth_priv *priv)
|
|
{
|
|
- int i;
|
|
-
|
|
+ preempt_disable();
|
|
drain_bufs(priv, DPAA2_ETH_BUFS_PER_CMD);
|
|
drain_bufs(priv, 1);
|
|
-
|
|
- for (i = 0; i < priv->num_channels; i++)
|
|
- priv->channel[i]->buf_count = 0;
|
|
+ preempt_enable();
|
|
}
|
|
|
|
/* Function is called from softirq context only, so we don't need to guard
|
|
@@ -862,19 +1177,19 @@ static int refill_pool(struct dpaa2_eth_
|
|
{
|
|
int new_count;
|
|
|
|
- if (likely(ch->buf_count >= DPAA2_ETH_REFILL_THRESH))
|
|
+ if (likely(ch->buf_count >= priv->refill_thresh))
|
|
return 0;
|
|
|
|
do {
|
|
- new_count = add_bufs(priv, bpid);
|
|
+ new_count = add_bufs(priv, ch, bpid);
|
|
if (unlikely(!new_count)) {
|
|
/* Out of memory; abort for now, we'll try later on */
|
|
break;
|
|
}
|
|
ch->buf_count += new_count;
|
|
- } while (ch->buf_count < DPAA2_ETH_NUM_BUFS);
|
|
+ } while (ch->buf_count < priv->max_bufs_per_ch);
|
|
|
|
- if (unlikely(ch->buf_count < DPAA2_ETH_NUM_BUFS))
|
|
+ if (unlikely(ch->buf_count < priv->max_bufs_per_ch))
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
@@ -887,7 +1202,8 @@ static int pull_channel(struct dpaa2_eth
|
|
|
|
/* Retry while portal is busy */
|
|
do {
|
|
- err = dpaa2_io_service_pull_channel(NULL, ch->ch_id, ch->store);
|
|
+ err = dpaa2_io_service_pull_channel(ch->dpio, ch->ch_id,
|
|
+ ch->store);
|
|
dequeues++;
|
|
cpu_relax();
|
|
} while (err == -EBUSY);
|
|
@@ -902,20 +1218,21 @@ static int pull_channel(struct dpaa2_eth
|
|
/* NAPI poll routine
|
|
*
|
|
* Frames are dequeued from the QMan channel associated with this NAPI context.
|
|
- * Rx, Tx confirmation and (if configured) Rx error frames all count
|
|
- * towards the NAPI budget.
|
|
+ * Rx and (if configured) Rx error frames count towards the NAPI budget. Tx
|
|
+ * confirmation frames are limited by a threshold per NAPI poll cycle.
|
|
*/
|
|
static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
struct dpaa2_eth_channel *ch;
|
|
- int cleaned = 0, store_cleaned;
|
|
+ int rx_cleaned = 0, tx_conf_cleaned = 0;
|
|
+ bool store_cleaned;
|
|
struct dpaa2_eth_priv *priv;
|
|
int err;
|
|
|
|
ch = container_of(napi, struct dpaa2_eth_channel, napi);
|
|
priv = ch->priv;
|
|
|
|
- while (cleaned < budget) {
|
|
+ do {
|
|
err = pull_channel(ch);
|
|
if (unlikely(err))
|
|
break;
|
|
@@ -923,29 +1240,29 @@ static int dpaa2_eth_poll(struct napi_st
|
|
/* Refill pool if appropriate */
|
|
refill_pool(priv, ch, priv->bpid);
|
|
|
|
- store_cleaned = consume_frames(ch);
|
|
- cleaned += store_cleaned;
|
|
+ store_cleaned = consume_frames(ch, &rx_cleaned,
|
|
+ &tx_conf_cleaned);
|
|
|
|
- /* If we have enough budget left for a full store,
|
|
- * try a new pull dequeue, otherwise we're done here
|
|
+ /* If we've either consumed the budget with Rx frames,
|
|
+ * or reached the Tx conf threshold, we're done.
|
|
*/
|
|
- if (store_cleaned == 0 ||
|
|
- cleaned > budget - DPAA2_ETH_STORE_SIZE)
|
|
- break;
|
|
- }
|
|
-
|
|
- if (cleaned < budget) {
|
|
- napi_complete_done(napi, cleaned);
|
|
- /* Re-enable data available notifications */
|
|
- do {
|
|
- err = dpaa2_io_service_rearm(NULL, &ch->nctx);
|
|
- cpu_relax();
|
|
- } while (err == -EBUSY);
|
|
- }
|
|
+ if (rx_cleaned >= budget ||
|
|
+ tx_conf_cleaned >= TX_CONF_PER_NAPI_POLL)
|
|
+ return budget;
|
|
+ } while (store_cleaned);
|
|
|
|
- ch->stats.frames += cleaned;
|
|
+ /* We didn't consume the entire budget, finish napi and
|
|
+ * re-enable data availability notifications
|
|
+ */
|
|
+ napi_complete(napi);
|
|
+ do {
|
|
+ err = dpaa2_io_service_rearm(ch->dpio, &ch->nctx);
|
|
+ cpu_relax();
|
|
+ } while (err == -EBUSY);
|
|
+ WARN_ONCE(err, "CDAN notifications rearm failed on core %d",
|
|
+ ch->nctx.desired_cpu);
|
|
|
|
- return cleaned;
|
|
+ return max(rx_cleaned, 1);
|
|
}
|
|
|
|
static void enable_ch_napi(struct dpaa2_eth_priv *priv)
|
|
@@ -1006,28 +1323,30 @@ static int dpaa2_eth_open(struct net_dev
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
int err;
|
|
|
|
- err = seed_pool(priv, priv->bpid);
|
|
- if (err) {
|
|
- /* Not much to do; the buffer pool, though not filled up,
|
|
- * may still contain some buffers which would enable us
|
|
- * to limp on.
|
|
- */
|
|
- netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n",
|
|
- priv->dpbp_dev->obj_desc.id, priv->bpid);
|
|
- }
|
|
-
|
|
/* We'll only start the txqs when the link is actually ready; make sure
|
|
* we don't race against the link up notification, which may come
|
|
* immediately after dpni_enable();
|
|
*/
|
|
netif_tx_stop_all_queues(net_dev);
|
|
- enable_ch_napi(priv);
|
|
+
|
|
/* Also, explicitly set carrier off, otherwise netif_carrier_ok() will
|
|
* return true and cause 'ip link show' to report the LOWER_UP flag,
|
|
* even though the link notification wasn't even received.
|
|
*/
|
|
netif_carrier_off(net_dev);
|
|
|
|
+ err = seed_pool(priv, priv->bpid);
|
|
+ if (err) {
|
|
+ /* Not much to do; the buffer pool, though not filled up,
|
|
+ * may still contain some buffers which would enable us
|
|
+ * to limp on.
|
|
+ */
|
|
+ netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n",
|
|
+ priv->dpbp_dev->obj_desc.id, priv->bpid);
|
|
+ }
|
|
+
|
|
+ priv->refill_thresh = DPAA2_ETH_REFILL_THRESH(priv);
|
|
+
|
|
err = dpni_enable(priv->mc_io, 0, priv->mc_token);
|
|
if (err < 0) {
|
|
netdev_err(net_dev, "dpni_enable() failed\n");
|
|
@@ -1047,48 +1366,17 @@ static int dpaa2_eth_open(struct net_dev
|
|
|
|
link_state_err:
|
|
enable_err:
|
|
- disable_ch_napi(priv);
|
|
+ priv->refill_thresh = 0;
|
|
drain_pool(priv);
|
|
return err;
|
|
}
|
|
|
|
-/* The DPIO store must be empty when we call this,
|
|
- * at the end of every NAPI cycle.
|
|
- */
|
|
-static u32 drain_channel(struct dpaa2_eth_priv *priv,
|
|
- struct dpaa2_eth_channel *ch)
|
|
-{
|
|
- u32 drained = 0, total = 0;
|
|
-
|
|
- do {
|
|
- pull_channel(ch);
|
|
- drained = consume_frames(ch);
|
|
- total += drained;
|
|
- } while (drained);
|
|
-
|
|
- return total;
|
|
-}
|
|
-
|
|
-static u32 drain_ingress_frames(struct dpaa2_eth_priv *priv)
|
|
-{
|
|
- struct dpaa2_eth_channel *ch;
|
|
- int i;
|
|
- u32 drained = 0;
|
|
-
|
|
- for (i = 0; i < priv->num_channels; i++) {
|
|
- ch = priv->channel[i];
|
|
- drained += drain_channel(priv, ch);
|
|
- }
|
|
-
|
|
- return drained;
|
|
-}
|
|
-
|
|
static int dpaa2_eth_stop(struct net_device *net_dev)
|
|
{
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
int dpni_enabled;
|
|
- int retries = 10;
|
|
- u32 drained;
|
|
+ int retries = 10, i;
|
|
+ int err = 0;
|
|
|
|
netif_tx_stop_all_queues(net_dev);
|
|
netif_carrier_off(net_dev);
|
|
@@ -1105,56 +1393,24 @@ static int dpaa2_eth_stop(struct net_dev
|
|
} while (dpni_enabled && --retries);
|
|
if (!retries) {
|
|
netdev_warn(net_dev, "Retry count exceeded disabling DPNI\n");
|
|
- /* Must go on and disable NAPI nonetheless, so we don't crash at
|
|
- * the next "ifconfig up"
|
|
+ /* Must go on and finish processing pending frames, so we don't
|
|
+ * crash at the next "ifconfig up"
|
|
*/
|
|
+ err = -ETIMEDOUT;
|
|
}
|
|
|
|
- /* Wait for NAPI to complete on every core and disable it.
|
|
- * In particular, this will also prevent NAPI from being rescheduled if
|
|
- * a new CDAN is serviced, effectively discarding the CDAN. We therefore
|
|
- * don't even need to disarm the channels, except perhaps for the case
|
|
- * of a huge coalescing value.
|
|
- */
|
|
- disable_ch_napi(priv);
|
|
+ priv->refill_thresh = 0;
|
|
|
|
- /* Manually drain the Rx and TxConf queues */
|
|
- drained = drain_ingress_frames(priv);
|
|
- if (drained)
|
|
- netdev_dbg(net_dev, "Drained %d frames.\n", drained);
|
|
+ /* Wait for all running napi poll routines to finish, so that no
|
|
+ * new refill operations are started
|
|
+ */
|
|
+ for (i = 0; i < priv->num_channels; i++)
|
|
+ napi_synchronize(&priv->channel[i]->napi);
|
|
|
|
/* Empty the buffer pool */
|
|
drain_pool(priv);
|
|
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int dpaa2_eth_init(struct net_device *net_dev)
|
|
-{
|
|
- u64 supported = 0;
|
|
- u64 not_supported = 0;
|
|
- struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
- u32 options = priv->dpni_attrs.options;
|
|
-
|
|
- /* Capabilities listing */
|
|
- supported |= IFF_LIVE_ADDR_CHANGE;
|
|
-
|
|
- if (options & DPNI_OPT_NO_MAC_FILTER)
|
|
- not_supported |= IFF_UNICAST_FLT;
|
|
- else
|
|
- supported |= IFF_UNICAST_FLT;
|
|
-
|
|
- net_dev->priv_flags |= supported;
|
|
- net_dev->priv_flags &= ~not_supported;
|
|
-
|
|
- /* Features */
|
|
- net_dev->features = NETIF_F_RXCSUM |
|
|
- NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
|
|
- NETIF_F_SG | NETIF_F_HIGHDMA |
|
|
- NETIF_F_LLTX;
|
|
- net_dev->hw_features = net_dev->features;
|
|
-
|
|
- return 0;
|
|
+ return err;
|
|
}
|
|
|
|
static int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr)
|
|
@@ -1200,25 +1456,6 @@ static void dpaa2_eth_get_stats(struct n
|
|
}
|
|
}
|
|
|
|
-static int dpaa2_eth_change_mtu(struct net_device *net_dev, int mtu)
|
|
-{
|
|
- struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
- int err;
|
|
-
|
|
- /* Set the maximum Rx frame length to match the transmit side;
|
|
- * account for L2 headers when computing the MFL
|
|
- */
|
|
- err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token,
|
|
- (u16)DPAA2_ETH_L2_MAX_FRM(mtu));
|
|
- if (err) {
|
|
- netdev_err(net_dev, "dpni_set_max_frame_length() failed\n");
|
|
- return err;
|
|
- }
|
|
-
|
|
- net_dev->mtu = mtu;
|
|
- return 0;
|
|
-}
|
|
-
|
|
/* Copy mac unicast addresses from @net_dev to @priv.
|
|
* Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable.
|
|
*/
|
|
@@ -1380,16 +1617,363 @@ static int dpaa2_eth_set_features(struct
|
|
return 0;
|
|
}
|
|
|
|
+static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
|
+ struct hwtstamp_config config;
|
|
+
|
|
+ if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ switch (config.tx_type) {
|
|
+ case HWTSTAMP_TX_OFF:
|
|
+ priv->ts_tx_en = false;
|
|
+ break;
|
|
+ case HWTSTAMP_TX_ON:
|
|
+ priv->ts_tx_en = true;
|
|
+ break;
|
|
+ default:
|
|
+ return -ERANGE;
|
|
+ }
|
|
+
|
|
+ if (config.rx_filter == HWTSTAMP_FILTER_NONE) {
|
|
+ priv->ts_rx_en = false;
|
|
+ } else {
|
|
+ priv->ts_rx_en = true;
|
|
+ /* TS is set for all frame types, not only those requested */
|
|
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
|
|
+ }
|
|
+
|
|
+ return copy_to_user(rq->ifr_data, &config, sizeof(config)) ?
|
|
+ -EFAULT : 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
+{
|
|
+ if (cmd == SIOCSHWTSTAMP)
|
|
+ return dpaa2_eth_ts_ioctl(dev, rq, cmd);
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int set_buffer_layout(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpni_buffer_layout buf_layout = {0};
|
|
+ int err;
|
|
+
|
|
+ /* We need to check for WRIOP version 1.0.0, but depending on the MC
|
|
+ * version, this number is not always provided correctly on rev1.
|
|
+ * We need to check for both alternatives in this situation.
|
|
+ */
|
|
+ if (priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(0, 0, 0) ||
|
|
+ priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(1, 0, 0))
|
|
+ priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1;
|
|
+ else
|
|
+ priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN;
|
|
+
|
|
+ /* tx buffer */
|
|
+ buf_layout.pass_timestamp = true;
|
|
+ buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE;
|
|
+ buf_layout.options = DPNI_BUF_LAYOUT_OPT_TIMESTAMP |
|
|
+ DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE;
|
|
+ err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_TX, &buf_layout);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_buffer_layout(TX) failed\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* tx-confirm buffer */
|
|
+ buf_layout.options = DPNI_BUF_LAYOUT_OPT_TIMESTAMP;
|
|
+ err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_TX_CONFIRM, &buf_layout);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Now that we've set our tx buffer layout, retrieve the minimum
|
|
+ * required tx data offset.
|
|
+ */
|
|
+ err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token,
|
|
+ &priv->tx_data_offset);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_get_tx_data_offset() failed\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if ((priv->tx_data_offset % 64) != 0)
|
|
+ dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n",
|
|
+ priv->tx_data_offset);
|
|
+
|
|
+ /* rx buffer */
|
|
+ buf_layout.pass_frame_status = true;
|
|
+ buf_layout.pass_parser_result = true;
|
|
+ buf_layout.data_align = priv->rx_buf_align;
|
|
+ buf_layout.private_data_size = 0;
|
|
+ buf_layout.data_head_room = dpaa2_eth_rx_headroom(priv);
|
|
+ /* If XDP program is attached, reserve extra space for
|
|
+ * potential header expansions
|
|
+ */
|
|
+ if (priv->has_xdp_prog)
|
|
+ buf_layout.data_head_room += XDP_PACKET_HEADROOM;
|
|
+ buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT |
|
|
+ DPNI_BUF_LAYOUT_OPT_FRAME_STATUS |
|
|
+ DPNI_BUF_LAYOUT_OPT_DATA_ALIGN |
|
|
+ DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM |
|
|
+ DPNI_BUF_LAYOUT_OPT_TIMESTAMP;
|
|
+ err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_RX, &buf_layout);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_buffer_layout(RX) failed\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_set_xdp(struct net_device *net_dev, struct bpf_prog *prog)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct dpaa2_eth_channel *ch;
|
|
+ struct bpf_prog *old_prog = NULL;
|
|
+ int i, err;
|
|
+
|
|
+ /* No support for SG frames */
|
|
+ if (DPAA2_ETH_L2_MAX_FRM(net_dev->mtu) > DPAA2_ETH_RX_BUF_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (netif_running(net_dev)) {
|
|
+ err = dpaa2_eth_stop(net_dev);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (prog) {
|
|
+ prog = bpf_prog_add(prog, priv->num_channels - 1);
|
|
+ if (IS_ERR(prog))
|
|
+ return PTR_ERR(prog);
|
|
+ }
|
|
+
|
|
+ priv->has_xdp_prog = !!prog;
|
|
+
|
|
+ for (i = 0; i < priv->num_channels; i++) {
|
|
+ ch = priv->channel[i];
|
|
+ old_prog = xchg(&ch->xdp_prog, prog);
|
|
+ if (old_prog)
|
|
+ bpf_prog_put(old_prog);
|
|
+ }
|
|
+
|
|
+ /* When turning XDP on/off we need to do some reconfiguring
|
|
+ * of the Rx buffer layout. Buffer pool was drained on dpaa2_eth_stop,
|
|
+ * so we are sure no old format buffers will be used from now on
|
|
+ */
|
|
+ if (priv->has_xdp_prog != !!old_prog)
|
|
+ set_buffer_layout(priv);
|
|
+
|
|
+ if (netif_running(net_dev)) {
|
|
+ err = dpaa2_eth_open(net_dev);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_xdp *xdp)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ switch (xdp->command) {
|
|
+ case XDP_SETUP_PROG:
|
|
+ return dpaa2_eth_set_xdp(dev, xdp->prog);
|
|
+ case XDP_QUERY_PROG:
|
|
+ xdp->prog_attached = priv->has_xdp_prog;
|
|
+ return 0;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, struct xdp_buff *xdp)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct device *dev = net_dev->dev.parent;
|
|
+ struct rtnl_link_stats64 *percpu_stats;
|
|
+ struct dpaa2_eth_drv_stats *percpu_extras;
|
|
+ unsigned int needed_headroom;
|
|
+ struct dpaa2_eth_swa *swa;
|
|
+ struct dpaa2_eth_fq *fq;
|
|
+ struct dpaa2_fd fd;
|
|
+ void *buffer_start, *aligned_start;
|
|
+ dma_addr_t addr;
|
|
+ int err, i;
|
|
+
|
|
+ if (!netif_running(net_dev))
|
|
+ return -ENETDOWN;
|
|
+
|
|
+ /* We require a minimum headroom to be able to transmit the frame.
|
|
+ * Otherwise return an error and let the original net_device handle it
|
|
+ */
|
|
+ /* TODO: Do we update i/f counters here or just on the Rx device? */
|
|
+ needed_headroom = dpaa2_eth_needed_headroom(priv, NULL);
|
|
+ if (xdp->data < xdp->data_hard_start ||
|
|
+ xdp->data - xdp->data_hard_start < needed_headroom) {
|
|
+ percpu_stats->tx_dropped++;
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ percpu_stats = this_cpu_ptr(priv->percpu_stats);
|
|
+ percpu_extras = this_cpu_ptr(priv->percpu_extras);
|
|
+
|
|
+ /* Setup the FD fields */
|
|
+ memset(&fd, 0, sizeof(fd));
|
|
+
|
|
+ /* Align FD address, if possible */
|
|
+ buffer_start = xdp->data - needed_headroom;
|
|
+ aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN,
|
|
+ DPAA2_ETH_TX_BUF_ALIGN);
|
|
+ if (aligned_start >= xdp->data_hard_start)
|
|
+ buffer_start = aligned_start;
|
|
+
|
|
+ swa = (struct dpaa2_eth_swa *)buffer_start;
|
|
+ /* fill in necessary fields here */
|
|
+ swa->type = DPAA2_ETH_SWA_XDP;
|
|
+ swa->xdp.dma_size = xdp->data_end - buffer_start;
|
|
+
|
|
+ addr = dma_map_single(dev, buffer_start,
|
|
+ xdp->data_end - buffer_start,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ if (unlikely(dma_mapping_error(dev, addr))) {
|
|
+ percpu_stats->tx_dropped++;
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dpaa2_fd_set_addr(&fd, addr);
|
|
+ dpaa2_fd_set_offset(&fd, xdp->data - buffer_start);
|
|
+ dpaa2_fd_set_len(&fd, xdp->data_end - xdp->data);
|
|
+ dpaa2_fd_set_format(&fd, dpaa2_fd_single);
|
|
+ dpaa2_fd_set_ctrl(&fd, FD_CTRL_PTA);
|
|
+
|
|
+ fq = &priv->fq[smp_processor_id()];
|
|
+ for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
|
|
+ err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0,
|
|
+ fq->tx_qdbin, &fd);
|
|
+ if (err != -EBUSY)
|
|
+ break;
|
|
+ }
|
|
+ percpu_extras->tx_portal_busy += i;
|
|
+ if (unlikely(err < 0)) {
|
|
+ percpu_stats->tx_errors++;
|
|
+ /* let the Rx device handle the cleanup */
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ percpu_stats->tx_packets++;
|
|
+ percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dpaa2_eth_xdp_flush(struct net_device *net_dev)
|
|
+{
|
|
+ /* We don't have hardware support for Tx batching,
|
|
+ * so we do the actual frame enqueue in ndo_xdp_xmit
|
|
+ */
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_update_xps(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ struct net_device *net_dev = priv->net_dev;
|
|
+ unsigned int i, num_queues;
|
|
+ struct cpumask xps_mask;
|
|
+ struct dpaa2_eth_fq *fq;
|
|
+ int err = 0;
|
|
+
|
|
+ num_queues = (net_dev->num_tc ? : 1) * dpaa2_eth_queue_count(priv);
|
|
+ for (i = 0; i < num_queues; i++) {
|
|
+ fq = &priv->fq[i % dpaa2_eth_queue_count(priv)];
|
|
+ cpumask_clear(&xps_mask);
|
|
+ cpumask_set_cpu(fq->target_cpu, &xps_mask);
|
|
+ err = netif_set_xps_queue(net_dev, &xps_mask, i);
|
|
+ if (err) {
|
|
+ dev_info_once(net_dev->dev.parent,
|
|
+ "Error setting XPS queue\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_setup_tc(struct net_device *net_dev,
|
|
+ enum tc_setup_type type,
|
|
+ void *type_data)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct tc_mqprio_qopt *mqprio = (struct tc_mqprio_qopt *)type_data;
|
|
+ int i, err = 0;
|
|
+
|
|
+ if (type != TC_SETUP_MQPRIO)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (mqprio->num_tc > dpaa2_eth_tc_count(priv)) {
|
|
+ netdev_err(net_dev, "Max %d traffic classes supported\n",
|
|
+ dpaa2_eth_tc_count(priv));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (mqprio->num_tc == net_dev->num_tc)
|
|
+ return 0;
|
|
+
|
|
+ mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
|
|
+
|
|
+ if (!mqprio->num_tc) {
|
|
+ netdev_reset_tc(net_dev);
|
|
+ err = netif_set_real_num_tx_queues(net_dev,
|
|
+ dpaa2_eth_queue_count(priv));
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ goto update_xps;
|
|
+ }
|
|
+
|
|
+ err = netdev_set_num_tc(net_dev, mqprio->num_tc);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ err = netif_set_real_num_tx_queues(net_dev, mqprio->num_tc *
|
|
+ dpaa2_eth_queue_count(priv));
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ for (i = 0; i < mqprio->num_tc; i++) {
|
|
+ err = netdev_set_tc_queue(net_dev, i,
|
|
+ dpaa2_eth_queue_count(priv),
|
|
+ i * dpaa2_eth_queue_count(priv));
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+update_xps:
|
|
+ err = dpaa2_eth_update_xps(priv);
|
|
+ return err;
|
|
+}
|
|
+
|
|
static const struct net_device_ops dpaa2_eth_ops = {
|
|
.ndo_open = dpaa2_eth_open,
|
|
.ndo_start_xmit = dpaa2_eth_tx,
|
|
.ndo_stop = dpaa2_eth_stop,
|
|
- .ndo_init = dpaa2_eth_init,
|
|
.ndo_set_mac_address = dpaa2_eth_set_addr,
|
|
.ndo_get_stats64 = dpaa2_eth_get_stats,
|
|
- .ndo_change_mtu = dpaa2_eth_change_mtu,
|
|
.ndo_set_rx_mode = dpaa2_eth_set_rx_mode,
|
|
.ndo_set_features = dpaa2_eth_set_features,
|
|
+ .ndo_do_ioctl = dpaa2_eth_ioctl,
|
|
+ .ndo_xdp = dpaa2_eth_xdp,
|
|
+ .ndo_xdp_xmit = dpaa2_eth_xdp_xmit,
|
|
+ .ndo_xdp_flush = dpaa2_eth_xdp_flush,
|
|
+ .ndo_setup_tc = dpaa2_eth_setup_tc,
|
|
};
|
|
|
|
static void cdan_cb(struct dpaa2_io_notification_ctx *ctx)
|
|
@@ -1422,34 +2006,32 @@ static struct fsl_mc_device *setup_dpcon
|
|
err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle);
|
|
if (err) {
|
|
dev_err(dev, "dpcon_open() failed\n");
|
|
- goto err_open;
|
|
+ goto free;
|
|
}
|
|
|
|
err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle);
|
|
if (err) {
|
|
dev_err(dev, "dpcon_reset() failed\n");
|
|
- goto err_reset;
|
|
+ goto close;
|
|
}
|
|
|
|
err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs);
|
|
if (err) {
|
|
dev_err(dev, "dpcon_get_attributes() failed\n");
|
|
- goto err_get_attr;
|
|
+ goto close;
|
|
}
|
|
|
|
err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle);
|
|
if (err) {
|
|
dev_err(dev, "dpcon_enable() failed\n");
|
|
- goto err_enable;
|
|
+ goto close;
|
|
}
|
|
|
|
return dpcon;
|
|
|
|
-err_enable:
|
|
-err_get_attr:
|
|
-err_reset:
|
|
+close:
|
|
dpcon_close(priv->mc_io, 0, dpcon->mc_handle);
|
|
-err_open:
|
|
+free:
|
|
fsl_mc_object_free(dpcon);
|
|
|
|
return NULL;
|
|
@@ -1502,7 +2084,14 @@ err_setup:
|
|
static void free_channel(struct dpaa2_eth_priv *priv,
|
|
struct dpaa2_eth_channel *channel)
|
|
{
|
|
+ struct bpf_prog *prog;
|
|
+
|
|
free_dpcon(priv, channel->dpcon);
|
|
+
|
|
+ prog = READ_ONCE(channel->xdp_prog);
|
|
+ if (prog)
|
|
+ bpf_prog_put(prog);
|
|
+
|
|
kfree(channel);
|
|
}
|
|
|
|
@@ -1546,7 +2135,8 @@ static int setup_dpio(struct dpaa2_eth_p
|
|
nctx->desired_cpu = i;
|
|
|
|
/* Register the new context */
|
|
- err = dpaa2_io_service_register(NULL, nctx);
|
|
+ channel->dpio = dpaa2_io_service_select(i);
|
|
+ err = dpaa2_io_service_register(channel->dpio, nctx);
|
|
if (err) {
|
|
dev_dbg(dev, "No affine DPIO for cpu %d\n", i);
|
|
/* If no affine DPIO for this core, there's probably
|
|
@@ -1586,7 +2176,7 @@ static int setup_dpio(struct dpaa2_eth_p
|
|
return 0;
|
|
|
|
err_set_cdan:
|
|
- dpaa2_io_service_deregister(NULL, nctx);
|
|
+ dpaa2_io_service_deregister(channel->dpio, nctx);
|
|
err_service_reg:
|
|
free_channel(priv, channel);
|
|
err_alloc_ch:
|
|
@@ -1609,7 +2199,7 @@ static void free_dpio(struct dpaa2_eth_p
|
|
/* deregister CDAN notifications and free channels */
|
|
for (i = 0; i < priv->num_channels; i++) {
|
|
ch = priv->channel[i];
|
|
- dpaa2_io_service_deregister(NULL, &ch->nctx);
|
|
+ dpaa2_io_service_deregister(ch->dpio, &ch->nctx);
|
|
free_channel(priv, ch);
|
|
}
|
|
}
|
|
@@ -1636,8 +2226,7 @@ static void set_fq_affinity(struct dpaa2
|
|
{
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
struct dpaa2_eth_fq *fq;
|
|
- int rx_cpu, txc_cpu;
|
|
- int i;
|
|
+ int rx_cpu, txc_cpu, i;
|
|
|
|
/* For each FQ, pick one channel/CPU to deliver frames to.
|
|
* This may well change at runtime, either through irqbalance or
|
|
@@ -1649,6 +2238,7 @@ static void set_fq_affinity(struct dpaa2
|
|
fq = &priv->fq[i];
|
|
switch (fq->type) {
|
|
case DPAA2_RX_FQ:
|
|
+ case DPAA2_RX_ERR_FQ:
|
|
fq->target_cpu = rx_cpu;
|
|
rx_cpu = cpumask_next(rx_cpu, &priv->dpio_cpumask);
|
|
if (rx_cpu >= nr_cpu_ids)
|
|
@@ -1656,6 +2246,7 @@ static void set_fq_affinity(struct dpaa2
|
|
break;
|
|
case DPAA2_TX_CONF_FQ:
|
|
fq->target_cpu = txc_cpu;
|
|
+
|
|
txc_cpu = cpumask_next(txc_cpu, &priv->dpio_cpumask);
|
|
if (txc_cpu >= nr_cpu_ids)
|
|
txc_cpu = cpumask_first(&priv->dpio_cpumask);
|
|
@@ -1665,11 +2256,13 @@ static void set_fq_affinity(struct dpaa2
|
|
}
|
|
fq->channel = get_affine_channel(priv, fq->target_cpu);
|
|
}
|
|
+
|
|
+ dpaa2_eth_update_xps(priv);
|
|
}
|
|
|
|
static void setup_fqs(struct dpaa2_eth_priv *priv)
|
|
{
|
|
- int i;
|
|
+ int i, j;
|
|
|
|
/* We have one TxConf FQ per Tx flow.
|
|
* The number of Tx and Rx queues is the same.
|
|
@@ -1681,11 +2274,19 @@ static void setup_fqs(struct dpaa2_eth_p
|
|
priv->fq[priv->num_fqs++].flowid = (u16)i;
|
|
}
|
|
|
|
- for (i = 0; i < dpaa2_eth_queue_count(priv); i++) {
|
|
- priv->fq[priv->num_fqs].type = DPAA2_RX_FQ;
|
|
- priv->fq[priv->num_fqs].consume = dpaa2_eth_rx;
|
|
- priv->fq[priv->num_fqs++].flowid = (u16)i;
|
|
- }
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++)
|
|
+ for (j = 0; j < dpaa2_eth_queue_count(priv); j++) {
|
|
+ priv->fq[priv->num_fqs].type = DPAA2_RX_FQ;
|
|
+ priv->fq[priv->num_fqs].consume = dpaa2_eth_rx;
|
|
+ priv->fq[priv->num_fqs].tc = (u8)i;
|
|
+ priv->fq[priv->num_fqs++].flowid = (u16)j;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
+ /* We have exactly one Rx error queue per DPNI */
|
|
+ priv->fq[priv->num_fqs].type = DPAA2_RX_ERR_FQ;
|
|
+ priv->fq[priv->num_fqs++].consume = dpaa2_eth_rx_err;
|
|
+#endif
|
|
|
|
/* For each FQ, decide on which core to process incoming frames */
|
|
set_fq_affinity(priv);
|
|
@@ -1735,6 +2336,9 @@ static int setup_dpbp(struct dpaa2_eth_p
|
|
}
|
|
priv->bpid = dpbp_attrs.bpid;
|
|
|
|
+ /* By default we start with flow control enabled */
|
|
+ priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_FC / priv->num_channels;
|
|
+
|
|
return 0;
|
|
|
|
err_get_attr:
|
|
@@ -1756,13 +2360,59 @@ static void free_dpbp(struct dpaa2_eth_p
|
|
fsl_mc_object_free(priv->dpbp_dev);
|
|
}
|
|
|
|
+static int setup_tx_congestion(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ struct dpni_congestion_notification_cfg notif_cfg = {0};
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ int err;
|
|
+
|
|
+ priv->cscn_unaligned = kzalloc(DPAA2_CSCN_SIZE + DPAA2_CSCN_ALIGN,
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (!priv->cscn_unaligned)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ priv->cscn_mem = PTR_ALIGN(priv->cscn_unaligned, DPAA2_CSCN_ALIGN);
|
|
+ priv->cscn_dma = dma_map_single(dev, priv->cscn_mem, DPAA2_CSCN_SIZE,
|
|
+ DMA_FROM_DEVICE);
|
|
+ if (dma_mapping_error(dev, priv->cscn_dma)) {
|
|
+ dev_err(dev, "Error mapping CSCN memory area\n");
|
|
+ err = -ENOMEM;
|
|
+ goto err_dma_map;
|
|
+ }
|
|
+
|
|
+ notif_cfg.units = DPNI_CONGESTION_UNIT_BYTES;
|
|
+ notif_cfg.threshold_entry = DPAA2_ETH_TX_CONG_ENTRY_THRESH;
|
|
+ notif_cfg.threshold_exit = DPAA2_ETH_TX_CONG_EXIT_THRESH;
|
|
+ notif_cfg.message_ctx = (u64)priv;
|
|
+ notif_cfg.message_iova = priv->cscn_dma;
|
|
+ notif_cfg.notification_mode = DPNI_CONG_OPT_WRITE_MEM_ON_ENTER |
|
|
+ DPNI_CONG_OPT_WRITE_MEM_ON_EXIT |
|
|
+ DPNI_CONG_OPT_COHERENT_WRITE;
|
|
+ err = dpni_set_congestion_notification(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_TX, 0, ¬if_cfg);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_congestion_notification failed\n");
|
|
+ goto err_set_cong;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_set_cong:
|
|
+ dma_unmap_single(dev, priv->cscn_dma, DPAA2_CSCN_SIZE, DMA_FROM_DEVICE);
|
|
+err_dma_map:
|
|
+ kfree(priv->cscn_unaligned);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
/* Configure the DPNI object this interface is associated with */
|
|
static int setup_dpni(struct fsl_mc_device *ls_dev)
|
|
{
|
|
struct device *dev = &ls_dev->dev;
|
|
struct dpaa2_eth_priv *priv;
|
|
struct net_device *net_dev;
|
|
- struct dpni_buffer_layout buf_layout = {0};
|
|
+ struct dpni_link_cfg cfg = {0};
|
|
int err;
|
|
|
|
net_dev = dev_get_drvdata(dev);
|
|
@@ -1772,7 +2422,22 @@ static int setup_dpni(struct fsl_mc_devi
|
|
err = dpni_open(priv->mc_io, 0, ls_dev->obj_desc.id, &priv->mc_token);
|
|
if (err) {
|
|
dev_err(dev, "dpni_open() failed\n");
|
|
- goto err_open;
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Check if we can work with this DPNI object */
|
|
+ err = dpni_get_api_version(priv->mc_io, 0, &priv->dpni_ver_major,
|
|
+ &priv->dpni_ver_minor);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_get_api_version() failed\n");
|
|
+ goto close;
|
|
+ }
|
|
+ if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_VER_MAJOR, DPNI_VER_MINOR) < 0) {
|
|
+ dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n",
|
|
+ priv->dpni_ver_major, priv->dpni_ver_minor,
|
|
+ DPNI_VER_MAJOR, DPNI_VER_MINOR);
|
|
+ err = -ENOTSUPP;
|
|
+ goto close;
|
|
}
|
|
|
|
ls_dev->mc_io = priv->mc_io;
|
|
@@ -1781,82 +2446,53 @@ static int setup_dpni(struct fsl_mc_devi
|
|
err = dpni_reset(priv->mc_io, 0, priv->mc_token);
|
|
if (err) {
|
|
dev_err(dev, "dpni_reset() failed\n");
|
|
- goto err_reset;
|
|
+ goto close;
|
|
}
|
|
|
|
err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token,
|
|
&priv->dpni_attrs);
|
|
if (err) {
|
|
dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err);
|
|
- goto err_get_attr;
|
|
+ goto close;
|
|
}
|
|
|
|
- /* Configure buffer layouts */
|
|
- /* rx buffer */
|
|
- buf_layout.pass_parser_result = true;
|
|
- buf_layout.pass_frame_status = true;
|
|
- buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE;
|
|
- buf_layout.data_align = DPAA2_ETH_RX_BUF_ALIGN;
|
|
- buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT |
|
|
- DPNI_BUF_LAYOUT_OPT_FRAME_STATUS |
|
|
- DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE |
|
|
- DPNI_BUF_LAYOUT_OPT_DATA_ALIGN;
|
|
- err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
- DPNI_QUEUE_RX, &buf_layout);
|
|
- if (err) {
|
|
- dev_err(dev, "dpni_set_buffer_layout(RX) failed\n");
|
|
- goto err_buf_layout;
|
|
- }
|
|
+ err = set_buffer_layout(priv);
|
|
+ if (err)
|
|
+ goto close;
|
|
|
|
- /* tx buffer */
|
|
- buf_layout.options = DPNI_BUF_LAYOUT_OPT_FRAME_STATUS |
|
|
- DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE;
|
|
- err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
- DPNI_QUEUE_TX, &buf_layout);
|
|
- if (err) {
|
|
- dev_err(dev, "dpni_set_buffer_layout(TX) failed\n");
|
|
- goto err_buf_layout;
|
|
- }
|
|
+ /* Enable congestion notifications for Tx queues */
|
|
+ err = setup_tx_congestion(priv);
|
|
+ if (err)
|
|
+ goto close;
|
|
|
|
- /* tx-confirm buffer */
|
|
- buf_layout.options = DPNI_BUF_LAYOUT_OPT_FRAME_STATUS;
|
|
- err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
|
|
- DPNI_QUEUE_TX_CONFIRM, &buf_layout);
|
|
- if (err) {
|
|
- dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n");
|
|
- goto err_buf_layout;
|
|
- }
|
|
+ /* allocate classification rule space */
|
|
+ priv->cls_rule = kzalloc(sizeof(*priv->cls_rule) *
|
|
+ dpaa2_eth_fs_count(priv), GFP_KERNEL);
|
|
+ if (!priv->cls_rule)
|
|
+ goto close;
|
|
|
|
- /* Now that we've set our tx buffer layout, retrieve the minimum
|
|
- * required tx data offset.
|
|
- */
|
|
- err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token,
|
|
- &priv->tx_data_offset);
|
|
+ /* Enable flow control */
|
|
+ cfg.options = DPNI_LINK_OPT_AUTONEG | DPNI_LINK_OPT_PAUSE;
|
|
+ priv->tx_pause_frames = true;
|
|
+ err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg);
|
|
if (err) {
|
|
- dev_err(dev, "dpni_get_tx_data_offset() failed\n");
|
|
- goto err_data_offset;
|
|
+ dev_err(dev, "dpni_set_link_cfg() failed\n");
|
|
+ goto cls_free;
|
|
}
|
|
|
|
- if ((priv->tx_data_offset % 64) != 0)
|
|
- dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n",
|
|
- priv->tx_data_offset);
|
|
-
|
|
- /* Accommodate software annotation space (SWA) */
|
|
- priv->tx_data_offset += DPAA2_ETH_SWA_SIZE;
|
|
-
|
|
return 0;
|
|
|
|
-err_data_offset:
|
|
-err_buf_layout:
|
|
-err_get_attr:
|
|
-err_reset:
|
|
+cls_free:
|
|
+ kfree(priv->cls_rule);
|
|
+close:
|
|
dpni_close(priv->mc_io, 0, priv->mc_token);
|
|
-err_open:
|
|
+
|
|
return err;
|
|
}
|
|
|
|
static void free_dpni(struct dpaa2_eth_priv *priv)
|
|
{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
int err;
|
|
|
|
err = dpni_reset(priv->mc_io, 0, priv->mc_token);
|
|
@@ -1865,6 +2501,11 @@ static void free_dpni(struct dpaa2_eth_p
|
|
err);
|
|
|
|
dpni_close(priv->mc_io, 0, priv->mc_token);
|
|
+
|
|
+ kfree(priv->cls_rule);
|
|
+
|
|
+ dma_unmap_single(dev, priv->cscn_dma, DPAA2_CSCN_SIZE, DMA_FROM_DEVICE);
|
|
+ kfree(priv->cscn_unaligned);
|
|
}
|
|
|
|
static int setup_rx_flow(struct dpaa2_eth_priv *priv,
|
|
@@ -1873,11 +2514,10 @@ static int setup_rx_flow(struct dpaa2_et
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
struct dpni_queue queue;
|
|
struct dpni_queue_id qid;
|
|
- struct dpni_taildrop td;
|
|
int err;
|
|
|
|
err = dpni_get_queue(priv->mc_io, 0, priv->mc_token,
|
|
- DPNI_QUEUE_RX, 0, fq->flowid, &queue, &qid);
|
|
+ DPNI_QUEUE_RX, fq->tc, fq->flowid, &queue, &qid);
|
|
if (err) {
|
|
dev_err(dev, "dpni_get_queue(RX) failed\n");
|
|
return err;
|
|
@@ -1890,7 +2530,7 @@ static int setup_rx_flow(struct dpaa2_et
|
|
queue.destination.priority = 1;
|
|
queue.user_context = (u64)(uintptr_t)fq;
|
|
err = dpni_set_queue(priv->mc_io, 0, priv->mc_token,
|
|
- DPNI_QUEUE_RX, 0, fq->flowid,
|
|
+ DPNI_QUEUE_RX, fq->tc, fq->flowid,
|
|
DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST,
|
|
&queue);
|
|
if (err) {
|
|
@@ -1898,15 +2538,121 @@ static int setup_rx_flow(struct dpaa2_et
|
|
return err;
|
|
}
|
|
|
|
- td.enable = 1;
|
|
- td.threshold = DPAA2_ETH_TAILDROP_THRESH;
|
|
- err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, DPNI_CP_QUEUE,
|
|
- DPNI_QUEUE_RX, 0, fq->flowid, &td);
|
|
- if (err) {
|
|
- dev_err(dev, "dpni_set_threshold() failed\n");
|
|
- return err;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int set_queue_taildrop(struct dpaa2_eth_priv *priv,
|
|
+ struct dpni_taildrop *td)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ int i, err;
|
|
+
|
|
+ for (i = 0; i < priv->num_fqs; i++) {
|
|
+ if (priv->fq[i].type != DPAA2_RX_FQ)
|
|
+ continue;
|
|
+
|
|
+ err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_CP_QUEUE, DPNI_QUEUE_RX,
|
|
+ priv->fq[i].tc, priv->fq[i].flowid,
|
|
+ td);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ dev_dbg(dev, "%s taildrop for Rx queue id %d tc %d\n",
|
|
+ (td->enable ? "Enabled" : "Disabled"),
|
|
+ priv->fq[i].flowid, priv->fq[i].tc);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int set_group_taildrop(struct dpaa2_eth_priv *priv,
|
|
+ struct dpni_taildrop *td)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpni_taildrop disable_td, *tc_td;
|
|
+ int i, err;
|
|
+
|
|
+ memset(&disable_td, 0, sizeof(struct dpni_taildrop));
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ if (td->enable && dpaa2_eth_is_pfc_enabled(priv, i))
|
|
+ /* Do not set taildrop thresholds for PFC-enabled
|
|
+ * traffic classes. We will enable congestion
|
|
+ * notifications for them.
|
|
+ */
|
|
+ tc_td = &disable_td;
|
|
+ else
|
|
+ tc_td = td;
|
|
+
|
|
+ err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_CP_GROUP, DPNI_QUEUE_RX,
|
|
+ i, 0, tc_td);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ dev_dbg(dev, "%s taildrop for Rx group tc %d\n",
|
|
+ (tc_td->enable ? "Enabled" : "Disabled"),
|
|
+ i);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Enable/disable Rx FQ taildrop
|
|
+ *
|
|
+ * Rx FQ taildrop is mutually exclusive with flow control and it only gets
|
|
+ * disabled when FC is active. Depending on FC status, we need to compute
|
|
+ * the maximum number of buffers in the pool differently, so use the
|
|
+ * opportunity to update max number of buffers as well.
|
|
+ */
|
|
+int set_rx_taildrop(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ enum dpaa2_eth_td_cfg cfg = dpaa2_eth_get_td_type(priv);
|
|
+ struct dpni_taildrop td_queue, td_group;
|
|
+ int err = 0;
|
|
+
|
|
+ switch (cfg) {
|
|
+ case DPAA2_ETH_TD_NONE:
|
|
+ memset(&td_queue, 0, sizeof(struct dpni_taildrop));
|
|
+ memset(&td_group, 0, sizeof(struct dpni_taildrop));
|
|
+ priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_FC /
|
|
+ priv->num_channels;
|
|
+ break;
|
|
+ case DPAA2_ETH_TD_QUEUE:
|
|
+ memset(&td_group, 0, sizeof(struct dpni_taildrop));
|
|
+ td_queue.enable = 1;
|
|
+ td_queue.units = DPNI_CONGESTION_UNIT_BYTES;
|
|
+ td_queue.threshold = DPAA2_ETH_TAILDROP_THRESH /
|
|
+ dpaa2_eth_tc_count(priv);
|
|
+ priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_PER_CH;
|
|
+ break;
|
|
+ case DPAA2_ETH_TD_GROUP:
|
|
+ memset(&td_queue, 0, sizeof(struct dpni_taildrop));
|
|
+ td_group.enable = 1;
|
|
+ td_group.units = DPNI_CONGESTION_UNIT_FRAMES;
|
|
+ td_group.threshold = NAPI_POLL_WEIGHT *
|
|
+ dpaa2_eth_queue_count(priv);
|
|
+ priv->max_bufs_per_ch = NAPI_POLL_WEIGHT *
|
|
+ dpaa2_eth_tc_count(priv);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
}
|
|
|
|
+ err = set_queue_taildrop(priv, &td_queue);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ err = set_group_taildrop(priv, &td_group);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ priv->refill_thresh = DPAA2_ETH_REFILL_THRESH(priv);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1953,23 +2699,88 @@ static int setup_tx_flow(struct dpaa2_et
|
|
return 0;
|
|
}
|
|
|
|
-/* Hash key is a 5-tuple: IPsrc, IPdst, IPnextproto, L4src, L4dst */
|
|
-static const struct dpaa2_eth_hash_fields hash_fields[] = {
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
+static int setup_rx_err_flow(struct dpaa2_eth_priv *priv,
|
|
+ struct dpaa2_eth_fq *fq)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpni_queue q = { { 0 } };
|
|
+ struct dpni_queue_id qid;
|
|
+ u8 q_opt = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST;
|
|
+ int err;
|
|
+
|
|
+ err = dpni_get_queue(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_RX_ERR, 0, 0, &q, &qid);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_get_queue() failed (%d)\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ fq->fqid = qid.fqid;
|
|
+
|
|
+ q.destination.id = fq->channel->dpcon_id;
|
|
+ q.destination.type = DPNI_DEST_DPCON;
|
|
+ q.destination.priority = 1;
|
|
+ q.user_context = (u64)fq;
|
|
+ err = dpni_set_queue(priv->mc_io, 0, priv->mc_token,
|
|
+ DPNI_QUEUE_RX_ERR, 0, 0, q_opt, &q);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_queue() failed (%d)\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* default hash key fields */
|
|
+static struct dpaa2_eth_dist_fields default_dist_fields[] = {
|
|
{
|
|
+ /* L2 header */
|
|
+ .rxnfc_field = RXH_L2DA,
|
|
+ .cls_prot = NET_PROT_ETH,
|
|
+ .cls_field = NH_FLD_ETH_DA,
|
|
+ .id = DPAA2_ETH_DIST_ETHDST,
|
|
+ .size = 6,
|
|
+ }, {
|
|
+ .cls_prot = NET_PROT_ETH,
|
|
+ .cls_field = NH_FLD_ETH_SA,
|
|
+ .id = DPAA2_ETH_DIST_ETHSRC,
|
|
+ .size = 6,
|
|
+ }, {
|
|
+ /* This is the last ethertype field parsed:
|
|
+ * depending on frame format, it can be the MAC ethertype
|
|
+ * or the VLAN etype.
|
|
+ */
|
|
+ .cls_prot = NET_PROT_ETH,
|
|
+ .cls_field = NH_FLD_ETH_TYPE,
|
|
+ .id = DPAA2_ETH_DIST_ETHTYPE,
|
|
+ .size = 2,
|
|
+ }, {
|
|
+ /* VLAN header */
|
|
+ .rxnfc_field = RXH_VLAN,
|
|
+ .cls_prot = NET_PROT_VLAN,
|
|
+ .cls_field = NH_FLD_VLAN_TCI,
|
|
+ .id = DPAA2_ETH_DIST_VLAN,
|
|
+ .size = 2,
|
|
+ }, {
|
|
/* IP header */
|
|
.rxnfc_field = RXH_IP_SRC,
|
|
.cls_prot = NET_PROT_IP,
|
|
.cls_field = NH_FLD_IP_SRC,
|
|
+ .id = DPAA2_ETH_DIST_IPSRC,
|
|
.size = 4,
|
|
}, {
|
|
.rxnfc_field = RXH_IP_DST,
|
|
.cls_prot = NET_PROT_IP,
|
|
.cls_field = NH_FLD_IP_DST,
|
|
+ .id = DPAA2_ETH_DIST_IPDST,
|
|
.size = 4,
|
|
}, {
|
|
.rxnfc_field = RXH_L3_PROTO,
|
|
.cls_prot = NET_PROT_IP,
|
|
.cls_field = NH_FLD_IP_PROTO,
|
|
+ .id = DPAA2_ETH_DIST_IPPROTO,
|
|
.size = 1,
|
|
}, {
|
|
/* Using UDP ports, this is functionally equivalent to raw
|
|
@@ -1978,90 +2789,182 @@ static const struct dpaa2_eth_hash_field
|
|
.rxnfc_field = RXH_L4_B_0_1,
|
|
.cls_prot = NET_PROT_UDP,
|
|
.cls_field = NH_FLD_UDP_PORT_SRC,
|
|
+ .id = DPAA2_ETH_DIST_L4SRC,
|
|
.size = 2,
|
|
}, {
|
|
.rxnfc_field = RXH_L4_B_2_3,
|
|
.cls_prot = NET_PROT_UDP,
|
|
.cls_field = NH_FLD_UDP_PORT_DST,
|
|
+ .id = DPAA2_ETH_DIST_L4DST,
|
|
.size = 2,
|
|
},
|
|
};
|
|
|
|
-/* Set RX hash options
|
|
- * flags is a combination of RXH_ bits
|
|
- */
|
|
-static int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
|
|
+static int legacy_config_dist_key(struct dpaa2_eth_priv *priv,
|
|
+ dma_addr_t key_iova)
|
|
{
|
|
- struct device *dev = net_dev->dev.parent;
|
|
- struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
- struct dpkg_profile_cfg cls_cfg;
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
struct dpni_rx_tc_dist_cfg dist_cfg;
|
|
- u8 *dma_mem;
|
|
- int i;
|
|
- int err = 0;
|
|
+ int i, err;
|
|
|
|
- if (!dpaa2_eth_hash_enabled(priv)) {
|
|
- dev_dbg(dev, "Hashing support is not enabled\n");
|
|
- return 0;
|
|
+ /* In legacy mode, we can't configure flow steering independently */
|
|
+ if (!dpaa2_eth_hash_enabled(priv))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
|
|
+
|
|
+ dist_cfg.key_cfg_iova = key_iova;
|
|
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
|
|
+ if (dpaa2_eth_fs_enabled(priv)) {
|
|
+ dist_cfg.dist_mode = DPNI_DIST_MODE_FS;
|
|
+ dist_cfg.fs_cfg.miss_action = DPNI_FS_MISS_HASH;
|
|
+ } else {
|
|
+ dist_cfg.dist_mode = DPNI_DIST_MODE_HASH;
|
|
}
|
|
|
|
- memset(&cls_cfg, 0, sizeof(cls_cfg));
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, i,
|
|
+ &dist_cfg);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_rx_tc_dist failed\n");
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int config_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key_iova)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpni_rx_dist_cfg dist_cfg;
|
|
+ int i, err;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) {
|
|
- struct dpkg_extract *key =
|
|
- &cls_cfg.extracts[cls_cfg.num_extracts];
|
|
+ if (!dpaa2_eth_hash_enabled(priv))
|
|
+ return -EOPNOTSUPP;
|
|
|
|
- if (!(flags & hash_fields[i].rxnfc_field))
|
|
- continue;
|
|
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
|
|
+
|
|
+ dist_cfg.key_cfg_iova = key_iova;
|
|
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
|
|
+ dist_cfg.enable = true;
|
|
+
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ dist_cfg.tc = i;
|
|
+
|
|
+ err = dpni_set_rx_hash_dist(priv->mc_io, 0,
|
|
+ priv->mc_token, &dist_cfg);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_rx_hash_dist failed\n");
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int config_fs_key(struct dpaa2_eth_priv *priv, dma_addr_t key_iova)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpni_rx_dist_cfg dist_cfg;
|
|
+ int i, err;
|
|
|
|
- if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) {
|
|
- dev_err(dev, "error adding key extraction rule, too many rules?\n");
|
|
- return -E2BIG;
|
|
+ if (!dpaa2_eth_fs_enabled(priv))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
|
|
+
|
|
+ dist_cfg.key_cfg_iova = key_iova;
|
|
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
|
|
+ dist_cfg.enable = true;
|
|
+
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ dist_cfg.tc = i;
|
|
+
|
|
+ err = dpni_set_rx_fs_dist(priv->mc_io, 0,
|
|
+ priv->mc_token, &dist_cfg);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_rx_fs_dist failed\n");
|
|
+ return err;
|
|
}
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
|
|
+int dpaa2_eth_set_dist_key(struct dpaa2_eth_priv *priv,
|
|
+ enum dpaa2_eth_rx_dist type, u32 key_fields)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpkg_profile_cfg cls_cfg;
|
|
+ struct dpkg_extract *key;
|
|
+ u32 hash_fields = 0;
|
|
+ dma_addr_t key_iova;
|
|
+ u8 *key_mem;
|
|
+ int i, err;
|
|
+
|
|
+ memset(&cls_cfg, 0, sizeof(cls_cfg));
|
|
+
|
|
+ for (i = 0; i < priv->num_dist_fields; i++) {
|
|
+ if (!(key_fields & priv->dist_fields[i].id))
|
|
+ continue;
|
|
+
|
|
+ key = &cls_cfg.extracts[cls_cfg.num_extracts];
|
|
key->type = DPKG_EXTRACT_FROM_HDR;
|
|
- key->extract.from_hdr.prot = hash_fields[i].cls_prot;
|
|
+ key->extract.from_hdr.prot = priv->dist_fields[i].cls_prot;
|
|
key->extract.from_hdr.type = DPKG_FULL_FIELD;
|
|
- key->extract.from_hdr.field = hash_fields[i].cls_field;
|
|
+ key->extract.from_hdr.field = priv->dist_fields[i].cls_field;
|
|
cls_cfg.num_extracts++;
|
|
|
|
- priv->rx_hash_fields |= hash_fields[i].rxnfc_field;
|
|
+ hash_fields |= priv->dist_fields[i].rxnfc_field;
|
|
}
|
|
|
|
- dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
|
|
- if (!dma_mem)
|
|
+ key_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
|
|
+ if (!key_mem)
|
|
return -ENOMEM;
|
|
|
|
- err = dpni_prepare_key_cfg(&cls_cfg, dma_mem);
|
|
+ err = dpni_prepare_key_cfg(&cls_cfg, key_mem);
|
|
if (err) {
|
|
dev_err(dev, "dpni_prepare_key_cfg error %d\n", err);
|
|
- goto err_prep_key;
|
|
+ goto free_key;
|
|
}
|
|
|
|
- memset(&dist_cfg, 0, sizeof(dist_cfg));
|
|
-
|
|
- /* Prepare for setting the rx dist */
|
|
- dist_cfg.key_cfg_iova = dma_map_single(dev, dma_mem,
|
|
- DPAA2_CLASSIFIER_DMA_SIZE,
|
|
- DMA_TO_DEVICE);
|
|
- if (dma_mapping_error(dev, dist_cfg.key_cfg_iova)) {
|
|
+ key_iova = dma_map_single(dev, key_mem, DPAA2_CLASSIFIER_DMA_SIZE,
|
|
+ DMA_TO_DEVICE);
|
|
+ if (dma_mapping_error(dev, key_iova)) {
|
|
dev_err(dev, "DMA mapping failed\n");
|
|
err = -ENOMEM;
|
|
- goto err_dma_map;
|
|
+ goto free_key;
|
|
}
|
|
|
|
- dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
|
|
- dist_cfg.dist_mode = DPNI_DIST_MODE_HASH;
|
|
+ switch (type) {
|
|
+ case DPAA2_ETH_RX_DIST_LEGACY:
|
|
+ err = legacy_config_dist_key(priv, key_iova);
|
|
+ break;
|
|
+ case DPAA2_ETH_RX_DIST_HASH:
|
|
+ err = config_hash_key(priv, key_iova);
|
|
+ break;
|
|
+ case DPAA2_ETH_RX_DIST_FS:
|
|
+ err = config_fs_key(priv, key_iova);
|
|
+ break;
|
|
+ default:
|
|
+ err = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
|
|
- err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg);
|
|
- dma_unmap_single(dev, dist_cfg.key_cfg_iova,
|
|
- DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE);
|
|
- if (err)
|
|
- dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err);
|
|
+ dma_unmap_single(dev, key_iova, DPAA2_CLASSIFIER_DMA_SIZE,
|
|
+ DMA_TO_DEVICE);
|
|
+ if (err) {
|
|
+ if (err != -EOPNOTSUPP)
|
|
+ dev_err(dev, "Distribution key config failed\n");
|
|
+ goto free_key;
|
|
+ }
|
|
|
|
-err_dma_map:
|
|
-err_prep_key:
|
|
- kfree(dma_mem);
|
|
+ if (type != DPAA2_ETH_RX_DIST_FS)
|
|
+ priv->rx_hash_fields = hash_fields;
|
|
+
|
|
+free_key:
|
|
+ kfree(key_mem);
|
|
return err;
|
|
}
|
|
|
|
@@ -2080,6 +2983,7 @@ static int bind_dpni(struct dpaa2_eth_pr
|
|
pools_params.num_dpbp = 1;
|
|
pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id;
|
|
pools_params.pools[0].backup_pool = 0;
|
|
+ pools_params.pools[0].priority_mask = 0xff;
|
|
pools_params.pools[0].buffer_size = DPAA2_ETH_RX_BUF_SIZE;
|
|
err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params);
|
|
if (err) {
|
|
@@ -2087,17 +2991,36 @@ static int bind_dpni(struct dpaa2_eth_pr
|
|
return err;
|
|
}
|
|
|
|
- /* have the interface implicitly distribute traffic based on supported
|
|
- * header fields
|
|
+ /* Verify classification options and disable hashing and/or
|
|
+ * flow steering support in case of invalid configuration values
|
|
*/
|
|
- err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_SUPPORTED);
|
|
- if (err)
|
|
- netdev_err(net_dev, "Failed to configure hashing\n");
|
|
+ priv->dist_fields = default_dist_fields;
|
|
+ priv->num_dist_fields = ARRAY_SIZE(default_dist_fields);
|
|
+ check_cls_support(priv);
|
|
+
|
|
+ /* have the interface implicitly distribute traffic based on
|
|
+ * a static hash key. Also configure flow steering key, if supported.
|
|
+ * Errors here are not blocking, so just let the called function
|
|
+ * print its error message and move along.
|
|
+ */
|
|
+ if (dpaa2_eth_has_legacy_dist(priv)) {
|
|
+ dpaa2_eth_set_dist_key(priv, DPAA2_ETH_RX_DIST_LEGACY,
|
|
+ DPAA2_ETH_DIST_ALL);
|
|
+ } else {
|
|
+ dpaa2_eth_set_dist_key(priv, DPAA2_ETH_RX_DIST_HASH,
|
|
+ DPAA2_ETH_DIST_DEFAULT_HASH);
|
|
+ dpaa2_eth_set_dist_key(priv, DPAA2_ETH_RX_DIST_FS,
|
|
+ DPAA2_ETH_DIST_ALL);
|
|
+ }
|
|
|
|
/* Configure handling of error frames */
|
|
err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
|
|
err_cfg.set_frame_annotation = 1;
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
+ err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE;
|
|
+#else
|
|
err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD;
|
|
+#endif
|
|
err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token,
|
|
&err_cfg);
|
|
if (err) {
|
|
@@ -2114,6 +3037,11 @@ static int bind_dpni(struct dpaa2_eth_pr
|
|
case DPAA2_TX_CONF_FQ:
|
|
err = setup_tx_flow(priv, &priv->fq[i]);
|
|
break;
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
|
|
+ case DPAA2_RX_ERR_FQ:
|
|
+ err = setup_rx_err_flow(priv, &priv->fq[i]);
|
|
+ break;
|
|
+#endif
|
|
default:
|
|
dev_err(dev, "Invalid FQ type %d\n", priv->fq[i].type);
|
|
return -EINVAL;
|
|
@@ -2237,11 +3165,14 @@ static int netdev_init(struct net_device
|
|
{
|
|
struct device *dev = net_dev->dev.parent;
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ u32 options = priv->dpni_attrs.options;
|
|
+ u64 supported = 0, not_supported = 0;
|
|
u8 bcast_addr[ETH_ALEN];
|
|
u8 num_queues;
|
|
int err;
|
|
|
|
net_dev->netdev_ops = &dpaa2_eth_ops;
|
|
+ net_dev->ethtool_ops = &dpaa2_ethtool_ops;
|
|
|
|
err = set_mac_addr(priv);
|
|
if (err)
|
|
@@ -2255,14 +3186,14 @@ static int netdev_init(struct net_device
|
|
return err;
|
|
}
|
|
|
|
- /* Reserve enough space to align buffer as per hardware requirement;
|
|
- * NOTE: priv->tx_data_offset MUST be initialized at this point.
|
|
- */
|
|
- net_dev->needed_headroom = DPAA2_ETH_NEEDED_HEADROOM(priv);
|
|
-
|
|
- /* Set MTU limits */
|
|
- net_dev->min_mtu = 68;
|
|
+ /* Set MTU upper limit; lower limit is default (68B) */
|
|
net_dev->max_mtu = DPAA2_ETH_MAX_MTU;
|
|
+ err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token,
|
|
+ (u16)DPAA2_ETH_MFL);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_max_frame_length() failed\n");
|
|
+ return err;
|
|
+ }
|
|
|
|
/* Set actual number of queues in the net device */
|
|
num_queues = dpaa2_eth_queue_count(priv);
|
|
@@ -2277,12 +3208,23 @@ static int netdev_init(struct net_device
|
|
return err;
|
|
}
|
|
|
|
- /* Our .ndo_init will be called herein */
|
|
- err = register_netdev(net_dev);
|
|
- if (err < 0) {
|
|
- dev_err(dev, "register_netdev() failed\n");
|
|
- return err;
|
|
- }
|
|
+ /* Capabilities listing */
|
|
+ supported |= IFF_LIVE_ADDR_CHANGE;
|
|
+
|
|
+ if (options & DPNI_OPT_NO_MAC_FILTER)
|
|
+ not_supported |= IFF_UNICAST_FLT;
|
|
+ else
|
|
+ supported |= IFF_UNICAST_FLT;
|
|
+
|
|
+ net_dev->priv_flags |= supported;
|
|
+ net_dev->priv_flags &= ~not_supported;
|
|
+
|
|
+ /* Features */
|
|
+ net_dev->features = NETIF_F_RXCSUM |
|
|
+ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
|
|
+ NETIF_F_SG | NETIF_F_HIGHDMA |
|
|
+ NETIF_F_LLTX;
|
|
+ net_dev->hw_features = net_dev->features;
|
|
|
|
return 0;
|
|
}
|
|
@@ -2303,14 +3245,9 @@ static int poll_link_state(void *arg)
|
|
return 0;
|
|
}
|
|
|
|
-static irqreturn_t dpni_irq0_handler(int irq_num, void *arg)
|
|
-{
|
|
- return IRQ_WAKE_THREAD;
|
|
-}
|
|
-
|
|
static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
|
|
{
|
|
- u32 status = 0, clear = 0;
|
|
+ u32 status = ~0;
|
|
struct device *dev = (struct device *)arg;
|
|
struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
|
|
struct net_device *net_dev = dev_get_drvdata(dev);
|
|
@@ -2320,18 +3257,12 @@ static irqreturn_t dpni_irq0_handler_thr
|
|
DPNI_IRQ_INDEX, &status);
|
|
if (unlikely(err)) {
|
|
netdev_err(net_dev, "Can't get irq status (err %d)\n", err);
|
|
- clear = 0xffffffff;
|
|
- goto out;
|
|
+ return IRQ_HANDLED;
|
|
}
|
|
|
|
- if (status & DPNI_IRQ_EVENT_LINK_CHANGED) {
|
|
- clear |= DPNI_IRQ_EVENT_LINK_CHANGED;
|
|
+ if (status & DPNI_IRQ_EVENT_LINK_CHANGED)
|
|
link_state_update(netdev_priv(net_dev));
|
|
- }
|
|
|
|
-out:
|
|
- dpni_clear_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
|
|
- DPNI_IRQ_INDEX, clear);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
@@ -2348,8 +3279,7 @@ static int setup_irqs(struct fsl_mc_devi
|
|
|
|
irq = ls_dev->irqs[0];
|
|
err = devm_request_threaded_irq(&ls_dev->dev, irq->msi_desc->irq,
|
|
- dpni_irq0_handler,
|
|
- dpni_irq0_handler_thread,
|
|
+ NULL, dpni_irq0_handler_thread,
|
|
IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
|
dev_name(&ls_dev->dev), &ls_dev->dev);
|
|
if (err < 0) {
|
|
@@ -2405,6 +3335,393 @@ static void del_ch_napi(struct dpaa2_eth
|
|
}
|
|
}
|
|
|
|
+/* SysFS support */
|
|
+static ssize_t dpaa2_eth_show_tx_shaping(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev));
|
|
+ /* No MC API for getting the shaping config. We're stateful. */
|
|
+ struct dpni_tx_shaping_cfg *scfg = &priv->shaping_cfg;
|
|
+
|
|
+ return sprintf(buf, "%u %hu\n", scfg->rate_limit, scfg->max_burst_size);
|
|
+}
|
|
+
|
|
+static ssize_t dpaa2_eth_write_tx_shaping(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int err, items;
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev));
|
|
+ struct dpni_tx_shaping_cfg scfg, ercfg = { 0 };
|
|
+
|
|
+ items = sscanf(buf, "%u %hu", &scfg.rate_limit, &scfg.max_burst_size);
|
|
+ if (items != 2) {
|
|
+ pr_err("Expected format: \"rate_limit(Mbps) max_burst_size(bytes)\"\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ /* Size restriction as per MC API documentation */
|
|
+ if (scfg.max_burst_size > DPAA2_ETH_MAX_BURST_SIZE) {
|
|
+ pr_err("max_burst_size must be <= %d\n",
|
|
+ DPAA2_ETH_MAX_BURST_SIZE);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, &scfg,
|
|
+ &ercfg, 0);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_tx_shaping() failed\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ /* If successful, save the current configuration for future inquiries */
|
|
+ priv->shaping_cfg = scfg;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static struct device_attribute dpaa2_eth_attrs[] = {
|
|
+ __ATTR(tx_shaping,
|
|
+ 0600,
|
|
+ dpaa2_eth_show_tx_shaping,
|
|
+ dpaa2_eth_write_tx_shaping),
|
|
+};
|
|
+
|
|
+static void dpaa2_eth_sysfs_init(struct device *dev)
|
|
+{
|
|
+ int i, err;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) {
|
|
+ err = device_create_file(dev, &dpaa2_eth_attrs[i]);
|
|
+ if (err) {
|
|
+ dev_err(dev, "ERROR creating sysfs file\n");
|
|
+ goto undo;
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+
|
|
+undo:
|
|
+ while (i > 0)
|
|
+ device_remove_file(dev, &dpaa2_eth_attrs[--i]);
|
|
+}
|
|
+
|
|
+static void dpaa2_eth_sysfs_remove(struct device *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++)
|
|
+ device_remove_file(dev, &dpaa2_eth_attrs[i]);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DCB
|
|
+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);
|
|
+ struct dpni_congestion_notification_cfg notification_cfg;
|
|
+ struct dpni_link_state state;
|
|
+ int err, i;
|
|
+
|
|
+ priv->pfc.pfc_cap = dpaa2_eth_tc_count(priv);
|
|
+
|
|
+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "ERROR %d getting link state", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (!(state.options & DPNI_LINK_OPT_PFC_PAUSE))
|
|
+ return 0;
|
|
+
|
|
+ priv->pfc.pfc_en = 0;
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ err = dpni_get_congestion_notification(priv->mc_io, 0,
|
|
+ priv->mc_token,
|
|
+ DPNI_QUEUE_RX,
|
|
+ i, ¬ification_cfg);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "Error %d getting congestion notif",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (notification_cfg.threshold_entry)
|
|
+ priv->pfc.pfc_en |= 1 << i;
|
|
+ }
|
|
+
|
|
+ memcpy(pfc, &priv->pfc, sizeof(priv->pfc));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Configure ingress classification based on VLAN PCP */
|
|
+static int set_vlan_qos(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+ struct dpkg_profile_cfg kg_cfg = {0};
|
|
+ struct dpni_qos_tbl_cfg qos_cfg = {0};
|
|
+ struct dpni_rule_cfg key_params;
|
|
+ u8 *params_iova, *key, *mask = NULL;
|
|
+ /* We only need the trailing 16 bits, without the TPID */
|
|
+ u8 key_size = VLAN_HLEN / 2;
|
|
+ int err = 0, i, j = 0;
|
|
+
|
|
+ if (priv->vlan_clsf_set)
|
|
+ return 0;
|
|
+
|
|
+ params_iova = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
|
|
+ if (!params_iova)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ kg_cfg.num_extracts = 1;
|
|
+ kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR;
|
|
+ kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_VLAN;
|
|
+ kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD;
|
|
+ kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_VLAN_TCI;
|
|
+
|
|
+ err = dpni_prepare_key_cfg(&kg_cfg, params_iova);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpkg_prepare_key_cfg failed: %d\n", err);
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ /* Set QoS table */
|
|
+ qos_cfg.default_tc = 0;
|
|
+ qos_cfg.discard_on_miss = 0;
|
|
+ qos_cfg.key_cfg_iova = dma_map_single(dev, params_iova,
|
|
+ DPAA2_CLASSIFIER_DMA_SIZE,
|
|
+ DMA_TO_DEVICE);
|
|
+ if (dma_mapping_error(dev, qos_cfg.key_cfg_iova)) {
|
|
+ dev_err(dev, "%s: DMA mapping failed\n", __func__);
|
|
+ err = -ENOMEM;
|
|
+ goto out_free;
|
|
+ }
|
|
+ err = dpni_set_qos_table(priv->mc_io, 0, priv->mc_token, &qos_cfg);
|
|
+ dma_unmap_single(dev, qos_cfg.key_cfg_iova,
|
|
+ DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE);
|
|
+
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_set_qos_table failed: %d\n", err);
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ key_params.key_size = key_size;
|
|
+
|
|
+ if (dpaa2_eth_fs_mask_enabled(priv)) {
|
|
+ mask = kzalloc(key_size, GFP_KERNEL);
|
|
+ if (!mask)
|
|
+ goto out_free;
|
|
+
|
|
+ *mask = cpu_to_be16(VLAN_PRIO_MASK);
|
|
+
|
|
+ key_params.mask_iova = dma_map_single(dev, mask, key_size,
|
|
+ DMA_TO_DEVICE);
|
|
+ if (dma_mapping_error(dev, key_params.mask_iova)) {
|
|
+ dev_err(dev, "DMA mapping failed %s\n", __func__);
|
|
+ err = -ENOMEM;
|
|
+ goto out_free_mask;
|
|
+ }
|
|
+ } else {
|
|
+ key_params.mask_iova = 0;
|
|
+ }
|
|
+
|
|
+ key = kzalloc(key_size, GFP_KERNEL);
|
|
+ if (!key)
|
|
+ goto out_cleanup_mask;
|
|
+
|
|
+ key_params.key_iova = dma_map_single(dev, key, key_size,
|
|
+ DMA_TO_DEVICE);
|
|
+ if (dma_mapping_error(dev, key_params.key_iova)) {
|
|
+ dev_err(dev, "%s: DMA mapping failed\n", __func__);
|
|
+ err = -ENOMEM;
|
|
+ goto out_free_key;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ *key = cpu_to_be16(i << VLAN_PRIO_SHIFT);
|
|
+
|
|
+ dma_sync_single_for_device(dev, key_params.key_iova,
|
|
+ key_size, DMA_TO_DEVICE);
|
|
+
|
|
+ err = dpni_add_qos_entry(priv->mc_io, 0, priv->mc_token,
|
|
+ &key_params, i, j++);
|
|
+ if (err) {
|
|
+ dev_err(dev, "dpni_add_qos_entry failed: %d\n", err);
|
|
+ goto out_remove;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ priv->vlan_clsf_set = true;
|
|
+ dev_dbg(dev, "Vlan PCP QoS classification set\n");
|
|
+ goto out_cleanup;
|
|
+
|
|
+out_remove:
|
|
+ for (j = 0; j < i; j++) {
|
|
+ *key = cpu_to_be16(j << VLAN_PRIO_SHIFT);
|
|
+
|
|
+ dma_sync_single_for_device(dev, key_params.key_iova, key_size,
|
|
+ DMA_TO_DEVICE);
|
|
+
|
|
+ err = dpni_remove_qos_entry(priv->mc_io, 0, priv->mc_token,
|
|
+ &key_params);
|
|
+ if (err)
|
|
+ dev_err(dev, "dpni_remove_qos_entry failed: %d\n", err);
|
|
+ }
|
|
+
|
|
+out_cleanup:
|
|
+ dma_unmap_single(dev, key_params.key_iova, key_size, DMA_TO_DEVICE);
|
|
+out_free_key:
|
|
+ kfree(key);
|
|
+out_cleanup_mask:
|
|
+ if (key_params.mask_iova)
|
|
+ dma_unmap_single(dev, key_params.mask_iova, key_size,
|
|
+ DMA_TO_DEVICE);
|
|
+out_free_mask:
|
|
+ kfree(mask);
|
|
+out_free:
|
|
+ kfree(params_iova);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+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_congestion_notification_cfg notification_cfg = {0};
|
|
+ struct dpni_link_state state = {0};
|
|
+ struct dpni_link_cfg cfg = {0};
|
|
+ struct ieee_pfc old_pfc;
|
|
+ int err = 0, i;
|
|
+
|
|
+ if (dpaa2_eth_tc_count(priv) == 1) {
|
|
+ netdev_dbg(net_dev, "DPNI has 1 TC, PFC configuration N/A\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Zero out pfc_enabled prios greater than tc_count */
|
|
+ pfc->pfc_en &= (1 << dpaa2_eth_tc_count(priv)) - 1;
|
|
+
|
|
+ if (priv->pfc.pfc_en == pfc->pfc_en)
|
|
+ /* Same enabled mask, nothing to be done */
|
|
+ return 0;
|
|
+
|
|
+ err = set_vlan_qos(priv);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "ERROR %d getting link state", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ cfg.rate = state.rate;
|
|
+ cfg.options = state.options;
|
|
+ if (pfc->pfc_en)
|
|
+ cfg.options |= DPNI_LINK_OPT_PFC_PAUSE;
|
|
+ else
|
|
+ cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE;
|
|
+
|
|
+ err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "ERROR %d setting link cfg", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ memcpy(&old_pfc, &priv->pfc, sizeof(priv->pfc));
|
|
+ memcpy(&priv->pfc, pfc, sizeof(priv->pfc));
|
|
+
|
|
+ err = set_rx_taildrop(priv);
|
|
+ if (err)
|
|
+ goto out_restore_config;
|
|
+
|
|
+ /* configure congestion notifications */
|
|
+ notification_cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL;
|
|
+ notification_cfg.units = DPNI_CONGESTION_UNIT_FRAMES;
|
|
+ notification_cfg.message_iova = 0ULL;
|
|
+ notification_cfg.message_ctx = 0ULL;
|
|
+
|
|
+ for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
|
|
+ if (dpaa2_eth_is_pfc_enabled(priv, i)) {
|
|
+ notification_cfg.threshold_entry = NAPI_POLL_WEIGHT;
|
|
+ notification_cfg.threshold_exit = NAPI_POLL_WEIGHT / 2;
|
|
+ } else {
|
|
+ notification_cfg.threshold_entry = 0;
|
|
+ notification_cfg.threshold_exit = 0;
|
|
+ }
|
|
+
|
|
+ err = dpni_set_congestion_notification(priv->mc_io, 0,
|
|
+ priv->mc_token,
|
|
+ DPNI_QUEUE_RX,
|
|
+ i, ¬ification_cfg);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "Error %d setting congestion notif",
|
|
+ err);
|
|
+ goto out_restore_config;
|
|
+ }
|
|
+
|
|
+ netdev_dbg(net_dev, "%s congestion notifications for tc %d\n",
|
|
+ (notification_cfg.threshold_entry ?
|
|
+ "Enabled" : "Disabled"), i);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_restore_config:
|
|
+ memcpy(&priv->pfc, &old_pfc, sizeof(priv->pfc));
|
|
+ return err;
|
|
+}
|
|
+
|
|
+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);
|
|
+
|
|
+ priv->dcbx_mode = mode;
|
|
+ return 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:
|
|
+ /* bitmap where each bit represents a number of traffic
|
|
+ * classes the device can be configured to use for Priority
|
|
+ * Flow Control
|
|
+ */
|
|
+ *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,
|
|
+};
|
|
+#endif
|
|
+
|
|
static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
|
|
{
|
|
struct device *dev;
|
|
@@ -2415,7 +3732,7 @@ static int dpaa2_eth_probe(struct fsl_mc
|
|
dev = &dpni_dev->dev;
|
|
|
|
/* Net device */
|
|
- net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_TX_QUEUES);
|
|
+ net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_NETDEV_QUEUES);
|
|
if (!net_dev) {
|
|
dev_err(dev, "alloc_etherdev_mq() failed\n");
|
|
return -ENOMEM;
|
|
@@ -2433,7 +3750,10 @@ static int dpaa2_eth_probe(struct fsl_mc
|
|
err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
|
|
&priv->mc_io);
|
|
if (err) {
|
|
- dev_err(dev, "MC portal allocation failed\n");
|
|
+ if (err == -ENXIO)
|
|
+ err = -EPROBE_DEFER;
|
|
+ else
|
|
+ dev_err(dev, "MC portal allocation failed\n");
|
|
goto err_portal_alloc;
|
|
}
|
|
|
|
@@ -2456,9 +3776,6 @@ static int dpaa2_eth_probe(struct fsl_mc
|
|
if (err)
|
|
goto err_bind;
|
|
|
|
- /* Add a NAPI context for each channel */
|
|
- add_ch_napi(priv);
|
|
-
|
|
/* Percpu statistics */
|
|
priv->percpu_stats = alloc_percpu(*priv->percpu_stats);
|
|
if (!priv->percpu_stats) {
|
|
@@ -2491,7 +3808,14 @@ static int dpaa2_eth_probe(struct fsl_mc
|
|
if (err)
|
|
goto err_alloc_rings;
|
|
|
|
- net_dev->ethtool_ops = &dpaa2_ethtool_ops;
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DCB
|
|
+ net_dev->dcbnl_ops = &dpaa2_eth_dcbnl_ops;
|
|
+ priv->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
|
|
+#endif
|
|
+
|
|
+ /* Add a NAPI context for each channel */
|
|
+ add_ch_napi(priv);
|
|
+ enable_ch_napi(priv);
|
|
|
|
err = setup_irqs(dpni_dev);
|
|
if (err) {
|
|
@@ -2499,25 +3823,41 @@ static int dpaa2_eth_probe(struct fsl_mc
|
|
priv->poll_thread = kthread_run(poll_link_state, priv,
|
|
"%s_poll_link", net_dev->name);
|
|
if (IS_ERR(priv->poll_thread)) {
|
|
- netdev_err(net_dev, "Error starting polling thread\n");
|
|
+ dev_err(dev, "Error starting polling thread\n");
|
|
goto err_poll_thread;
|
|
}
|
|
priv->do_link_poll = true;
|
|
}
|
|
|
|
+ err = register_netdev(net_dev);
|
|
+ if (err < 0) {
|
|
+ dev_err(dev, "register_netdev() failed\n");
|
|
+ goto err_netdev_reg;
|
|
+ }
|
|
+
|
|
+ dpaa2_eth_sysfs_init(&net_dev->dev);
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS
|
|
+ dpaa2_dbg_add(priv);
|
|
+#endif
|
|
+
|
|
dev_info(dev, "Probed interface %s\n", net_dev->name);
|
|
return 0;
|
|
|
|
+err_netdev_reg:
|
|
+ if (priv->do_link_poll)
|
|
+ kthread_stop(priv->poll_thread);
|
|
+ else
|
|
+ fsl_mc_free_irqs(dpni_dev);
|
|
err_poll_thread:
|
|
free_rings(priv);
|
|
err_alloc_rings:
|
|
err_csum:
|
|
- unregister_netdev(net_dev);
|
|
err_netdev_init:
|
|
free_percpu(priv->percpu_extras);
|
|
err_alloc_percpu_extras:
|
|
free_percpu(priv->percpu_stats);
|
|
err_alloc_percpu_stats:
|
|
+ disable_ch_napi(priv);
|
|
del_ch_napi(priv);
|
|
err_bind:
|
|
free_dpbp(priv);
|
|
@@ -2544,8 +3884,15 @@ static int dpaa2_eth_remove(struct fsl_m
|
|
net_dev = dev_get_drvdata(dev);
|
|
priv = netdev_priv(net_dev);
|
|
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS
|
|
+ dpaa2_dbg_remove(priv);
|
|
+#endif
|
|
+ dpaa2_eth_sysfs_remove(&net_dev->dev);
|
|
+
|
|
unregister_netdev(net_dev);
|
|
- dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
|
|
+
|
|
+ disable_ch_napi(priv);
|
|
+ del_ch_napi(priv);
|
|
|
|
if (priv->do_link_poll)
|
|
kthread_stop(priv->poll_thread);
|
|
@@ -2555,8 +3902,6 @@ static int dpaa2_eth_remove(struct fsl_m
|
|
free_rings(priv);
|
|
free_percpu(priv->percpu_stats);
|
|
free_percpu(priv->percpu_extras);
|
|
-
|
|
- del_ch_napi(priv);
|
|
free_dpbp(priv);
|
|
free_dpio(priv);
|
|
free_dpni(priv);
|
|
@@ -2566,6 +3911,8 @@ static int dpaa2_eth_remove(struct fsl_m
|
|
dev_set_drvdata(dev, NULL);
|
|
free_netdev(net_dev);
|
|
|
|
+ dev_dbg(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -2588,4 +3935,34 @@ static struct fsl_mc_driver dpaa2_eth_dr
|
|
.match_id_table = dpaa2_eth_match_id_table
|
|
};
|
|
|
|
-module_fsl_mc_driver(dpaa2_eth_driver);
|
|
+static int __init dpaa2_eth_driver_init(void)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ dpaa2_eth_dbg_init();
|
|
+ err = fsl_mc_driver_register(&dpaa2_eth_driver);
|
|
+ if (err)
|
|
+ goto out_debugfs_err;
|
|
+
|
|
+ err = dpaa2_ceetm_register();
|
|
+ if (err)
|
|
+ goto out_ceetm_err;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_ceetm_err:
|
|
+ fsl_mc_driver_unregister(&dpaa2_eth_driver);
|
|
+out_debugfs_err:
|
|
+ dpaa2_eth_dbg_exit();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void __exit dpaa2_eth_driver_exit(void)
|
|
+{
|
|
+ dpaa2_ceetm_unregister();
|
|
+ fsl_mc_driver_unregister(&dpaa2_eth_driver);
|
|
+ dpaa2_eth_dbg_exit();
|
|
+}
|
|
+
|
|
+module_init(dpaa2_eth_driver_init);
|
|
+module_exit(dpaa2_eth_driver_exit);
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
|
|
@@ -33,6 +33,7 @@
|
|
#ifndef __DPAA2_ETH_H
|
|
#define __DPAA2_ETH_H
|
|
|
|
+#include <linux/dcbnl.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/if_vlan.h>
|
|
|
|
@@ -44,9 +45,17 @@
|
|
#include "dpni-cmd.h"
|
|
|
|
#include "dpaa2-eth-trace.h"
|
|
+#include "dpaa2-eth-debugfs.h"
|
|
+
|
|
+#define DPAA2_WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0)
|
|
|
|
#define DPAA2_ETH_STORE_SIZE 16
|
|
|
|
+/* We set a max threshold for how many Tx confirmations we should process
|
|
+ * on a NAPI poll call, they take less processing time.
|
|
+ */
|
|
+#define TX_CONF_PER_NAPI_POLL 256
|
|
+
|
|
/* Maximum number of scatter-gather entries in an ingress frame,
|
|
* considering the maximum receive frame size is 64K
|
|
*/
|
|
@@ -60,6 +69,14 @@
|
|
/* Convert L3 MTU to L2 MFL */
|
|
#define DPAA2_ETH_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN)
|
|
|
|
+/* Maximum burst size value for Tx shaping */
|
|
+#define DPAA2_ETH_MAX_BURST_SIZE 0xF7FF
|
|
+
|
|
+/* Maximum number of buffers that can be acquired/released through a single
|
|
+ * QBMan command
|
|
+ */
|
|
+#define DPAA2_ETH_BUFS_PER_CMD 7
|
|
+
|
|
/* Set the taildrop threshold (in bytes) to allow the enqueue of several jumbo
|
|
* frames in the Rx queues (length of the current frame is not
|
|
* taken into account when making the taildrop decision)
|
|
@@ -72,31 +89,32 @@
|
|
* to accommodate the buffer refill delay.
|
|
*/
|
|
#define DPAA2_ETH_MAX_FRAMES_PER_QUEUE (DPAA2_ETH_TAILDROP_THRESH / 64)
|
|
-#define DPAA2_ETH_NUM_BUFS (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256)
|
|
-#define DPAA2_ETH_REFILL_THRESH DPAA2_ETH_MAX_FRAMES_PER_QUEUE
|
|
+#define DPAA2_ETH_NUM_BUFS_PER_CH (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256)
|
|
+#define DPAA2_ETH_REFILL_THRESH(priv) \
|
|
+ ((priv)->max_bufs_per_ch - DPAA2_ETH_BUFS_PER_CMD)
|
|
|
|
-/* Maximum number of buffers that can be acquired/released through a single
|
|
- * QBMan command
|
|
- */
|
|
-#define DPAA2_ETH_BUFS_PER_CMD 7
|
|
+/* Global buffer quota in case flow control is enabled */
|
|
+#define DPAA2_ETH_NUM_BUFS_FC 256
|
|
+
|
|
+/* Hardware requires alignment for ingress/egress buffer addresses */
|
|
+#define DPAA2_ETH_TX_BUF_ALIGN 64
|
|
|
|
-/* Hardware requires alignment for ingress/egress buffer addresses
|
|
- * and ingress buffer lengths.
|
|
+/* Due to a limitation in WRIOP 1.0.0, the RX buffer data must be aligned
|
|
+ * to 256B. For newer revisions, the requirement is only for 64B alignment
|
|
*/
|
|
+#define DPAA2_ETH_RX_BUF_ALIGN_REV1 256
|
|
+#define DPAA2_ETH_RX_BUF_ALIGN 64
|
|
+
|
|
#define DPAA2_ETH_RX_BUF_SIZE 2048
|
|
-#define DPAA2_ETH_TX_BUF_ALIGN 64
|
|
-#define DPAA2_ETH_RX_BUF_ALIGN 256
|
|
-#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \
|
|
- ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN)
|
|
-
|
|
-/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but we need to allocate ingress
|
|
- * buffers large enough to allow building an skb around them and also account
|
|
- * for alignment restrictions
|
|
- */
|
|
-#define DPAA2_ETH_BUF_RAW_SIZE \
|
|
- (DPAA2_ETH_RX_BUF_SIZE + \
|
|
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \
|
|
- DPAA2_ETH_RX_BUF_ALIGN)
|
|
+#define DPAA2_ETH_SKB_SIZE \
|
|
+ (DPAA2_ETH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
|
+
|
|
+/* PTP nominal frequency 1GHz */
|
|
+#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1
|
|
+
|
|
+/* Hardware annotation area in RX/TX buffers */
|
|
+#define DPAA2_ETH_RX_HWA_SIZE 64
|
|
+#define DPAA2_ETH_TX_HWA_SIZE 128
|
|
|
|
/* We are accommodating a skb backpointer and some S/G info
|
|
* in the frame's software annotation. The hardware
|
|
@@ -104,12 +122,32 @@
|
|
*/
|
|
#define DPAA2_ETH_SWA_SIZE 64
|
|
|
|
+/* We store different information in the software annotation area of a Tx frame
|
|
+ * based on what type of frame it is
|
|
+ */
|
|
+enum dpaa2_eth_swa_type {
|
|
+ DPAA2_ETH_SWA_SINGLE,
|
|
+ DPAA2_ETH_SWA_SG,
|
|
+ DPAA2_ETH_SWA_XDP,
|
|
+};
|
|
+
|
|
/* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */
|
|
struct dpaa2_eth_swa {
|
|
- struct sk_buff *skb;
|
|
- struct scatterlist *scl;
|
|
- int num_sg;
|
|
- int num_dma_bufs;
|
|
+ enum dpaa2_eth_swa_type type;
|
|
+ union {
|
|
+ struct {
|
|
+ struct sk_buff *skb;
|
|
+ } single;
|
|
+ struct {
|
|
+ struct sk_buff *skb;
|
|
+ struct scatterlist *scl;
|
|
+ int num_sg;
|
|
+ int sgt_size;
|
|
+ } sg;
|
|
+ struct {
|
|
+ int dma_size;
|
|
+ } xdp;
|
|
+ };
|
|
};
|
|
|
|
/* Annotation valid bits in FD FRC */
|
|
@@ -120,23 +158,14 @@ struct dpaa2_eth_swa {
|
|
#define DPAA2_FD_FRC_FASWOV 0x0800
|
|
#define DPAA2_FD_FRC_FAICFDV 0x0400
|
|
|
|
-/* Error bits in FD CTRL */
|
|
-#define DPAA2_FD_CTRL_UFD 0x00000004
|
|
-#define DPAA2_FD_CTRL_SBE 0x00000008
|
|
-#define DPAA2_FD_CTRL_FSE 0x00000020
|
|
-#define DPAA2_FD_CTRL_FAERR 0x00000040
|
|
-
|
|
-#define DPAA2_FD_RX_ERR_MASK (DPAA2_FD_CTRL_SBE | \
|
|
- DPAA2_FD_CTRL_FAERR)
|
|
-#define DPAA2_FD_TX_ERR_MASK (DPAA2_FD_CTRL_UFD | \
|
|
- DPAA2_FD_CTRL_SBE | \
|
|
- DPAA2_FD_CTRL_FSE | \
|
|
- DPAA2_FD_CTRL_FAERR)
|
|
+#define DPAA2_FD_RX_ERR_MASK (FD_CTRL_SBE | FD_CTRL_FAERR)
|
|
+#define DPAA2_FD_TX_ERR_MASK (FD_CTRL_UFD | \
|
|
+ FD_CTRL_SBE | \
|
|
+ FD_CTRL_FSE | \
|
|
+ FD_CTRL_FAERR)
|
|
|
|
/* Annotation bits in FD CTRL */
|
|
#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128 */
|
|
-#define DPAA2_FD_CTRL_PTA 0x00800000
|
|
-#define DPAA2_FD_CTRL_PTV1 0x00400000
|
|
|
|
/* Frame annotation status */
|
|
struct dpaa2_fas {
|
|
@@ -144,7 +173,7 @@ struct dpaa2_fas {
|
|
u8 ppid;
|
|
__le16 ifpid;
|
|
__le32 status;
|
|
-} __packed;
|
|
+};
|
|
|
|
/* Frame annotation status word is located in the first 8 bytes
|
|
* of the buffer's hardware annoatation area
|
|
@@ -152,11 +181,45 @@ struct dpaa2_fas {
|
|
#define DPAA2_FAS_OFFSET 0
|
|
#define DPAA2_FAS_SIZE (sizeof(struct dpaa2_fas))
|
|
|
|
+/* Timestamp is located in the next 8 bytes of the buffer's
|
|
+ * hardware annotation area
|
|
+ */
|
|
+#define DPAA2_TS_OFFSET 0x8
|
|
+
|
|
+/* Frame annotation egress action descriptor */
|
|
+#define DPAA2_FAEAD_OFFSET 0x58
|
|
+
|
|
+struct dpaa2_faead {
|
|
+ __le32 conf_fqid;
|
|
+ __le32 ctrl;
|
|
+};
|
|
+
|
|
+#define DPAA2_FAEAD_A2V 0x20000000
|
|
+#define DPAA2_FAEAD_A4V 0x08000000
|
|
+#define DPAA2_FAEAD_UPDV 0x00001000
|
|
+#define DPAA2_FAEAD_EBDDV 0x00002000
|
|
+#define DPAA2_FAEAD_UPD 0x00000010
|
|
+
|
|
/* Accessors for the hardware annotation fields that we use */
|
|
-#define dpaa2_get_hwa(buf_addr) \
|
|
- ((void *)(buf_addr) + DPAA2_ETH_SWA_SIZE)
|
|
-#define dpaa2_get_fas(buf_addr) \
|
|
- (struct dpaa2_fas *)(dpaa2_get_hwa(buf_addr) + DPAA2_FAS_OFFSET)
|
|
+static inline void *dpaa2_get_hwa(void *buf_addr, bool swa)
|
|
+{
|
|
+ return buf_addr + (swa ? DPAA2_ETH_SWA_SIZE : 0);
|
|
+}
|
|
+
|
|
+static inline struct dpaa2_fas *dpaa2_get_fas(void *buf_addr, bool swa)
|
|
+{
|
|
+ return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAS_OFFSET;
|
|
+}
|
|
+
|
|
+static inline u64 *dpaa2_get_ts(void *buf_addr, bool swa)
|
|
+{
|
|
+ return dpaa2_get_hwa(buf_addr, swa) + DPAA2_TS_OFFSET;
|
|
+}
|
|
+
|
|
+static inline struct dpaa2_faead *dpaa2_get_faead(void *buf_addr, bool swa)
|
|
+{
|
|
+ return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAEAD_OFFSET;
|
|
+}
|
|
|
|
/* Error and status bits in the frame annotation status word */
|
|
/* Debug frame, otherwise supposed to be discarded */
|
|
@@ -203,11 +266,6 @@ struct dpaa2_fas {
|
|
DPAA2_FAS_BLE | \
|
|
DPAA2_FAS_L3CE | \
|
|
DPAA2_FAS_L4CE)
|
|
-/* Tx errors */
|
|
-#define DPAA2_FAS_TX_ERR_MASK (DPAA2_FAS_KSE | \
|
|
- DPAA2_FAS_EOFHE | \
|
|
- DPAA2_FAS_MNLE | \
|
|
- DPAA2_FAS_TIDE)
|
|
|
|
/* Time in milliseconds between link state updates */
|
|
#define DPAA2_ETH_LINK_STATE_REFRESH 1000
|
|
@@ -218,6 +276,14 @@ struct dpaa2_fas {
|
|
*/
|
|
#define DPAA2_ETH_ENQUEUE_RETRIES 10
|
|
|
|
+/* Tx congestion entry & exit thresholds, in number of bytes.
|
|
+ * We allow a maximum of 512KB worth of frames pending processing on the Tx
|
|
+ * queues of an interface
|
|
+ */
|
|
+#define DPAA2_ETH_TX_CONG_ENTRY_THRESH (512 * 1024)
|
|
+#define DPAA2_ETH_TX_CONG_EXIT_THRESH \
|
|
+ (DPAA2_ETH_TX_CONG_ENTRY_THRESH * 9 / 10)
|
|
+
|
|
/* Driver statistics, other than those in struct rtnl_link_stats64.
|
|
* These are usually collected per-CPU and aggregated by ethtool.
|
|
*/
|
|
@@ -226,6 +292,7 @@ struct dpaa2_eth_drv_stats {
|
|
__u64 tx_conf_bytes;
|
|
__u64 tx_sg_frames;
|
|
__u64 tx_sg_bytes;
|
|
+ __u64 tx_reallocs;
|
|
__u64 rx_sg_frames;
|
|
__u64 rx_sg_bytes;
|
|
/* Enqueues retried due to portal busy */
|
|
@@ -236,6 +303,8 @@ struct dpaa2_eth_drv_stats {
|
|
struct dpaa2_eth_fq_stats {
|
|
/* Number of frames received on this queue */
|
|
__u64 frames;
|
|
+ /* Number of times this queue entered congestion */
|
|
+ __u64 congestion_entry;
|
|
};
|
|
|
|
/* Per-channel statistics */
|
|
@@ -250,17 +319,23 @@ struct dpaa2_eth_ch_stats {
|
|
__u64 pull_err;
|
|
};
|
|
|
|
+#define DPAA2_ETH_MAX_TCS 8
|
|
+
|
|
/* Maximum number of queues associated with a DPNI */
|
|
-#define DPAA2_ETH_MAX_RX_QUEUES 16
|
|
-#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS
|
|
+#define DPAA2_ETH_MAX_RX_QUEUES (DPNI_MAX_DIST_SIZE * DPAA2_ETH_MAX_TCS)
|
|
+#define DPAA2_ETH_MAX_TX_QUEUES DPNI_MAX_SENDERS
|
|
+#define DPAA2_ETH_MAX_RX_ERR_QUEUES 1
|
|
#define DPAA2_ETH_MAX_QUEUES (DPAA2_ETH_MAX_RX_QUEUES + \
|
|
- DPAA2_ETH_MAX_TX_QUEUES)
|
|
+ DPAA2_ETH_MAX_TX_QUEUES + \
|
|
+ DPAA2_ETH_MAX_RX_ERR_QUEUES)
|
|
+#define DPAA2_ETH_MAX_NETDEV_QUEUES (DPNI_MAX_DIST_SIZE * DPAA2_ETH_MAX_TCS)
|
|
|
|
-#define DPAA2_ETH_MAX_DPCONS NR_CPUS
|
|
+#define DPAA2_ETH_MAX_DPCONS 16
|
|
|
|
enum dpaa2_eth_fq_type {
|
|
DPAA2_RX_FQ = 0,
|
|
DPAA2_TX_CONF_FQ,
|
|
+ DPAA2_RX_ERR_FQ
|
|
};
|
|
|
|
struct dpaa2_eth_priv;
|
|
@@ -269,6 +344,7 @@ struct dpaa2_eth_fq {
|
|
u32 fqid;
|
|
u32 tx_qdbin;
|
|
u16 flowid;
|
|
+ u8 tc;
|
|
int target_cpu;
|
|
struct dpaa2_eth_channel *channel;
|
|
enum dpaa2_eth_fq_type type;
|
|
@@ -276,7 +352,8 @@ struct dpaa2_eth_fq {
|
|
void (*consume)(struct dpaa2_eth_priv *,
|
|
struct dpaa2_eth_channel *,
|
|
const struct dpaa2_fd *,
|
|
- struct napi_struct *);
|
|
+ struct napi_struct *,
|
|
+ u16 queue_id);
|
|
struct dpaa2_eth_fq_stats stats;
|
|
};
|
|
|
|
@@ -285,24 +362,53 @@ struct dpaa2_eth_channel {
|
|
struct fsl_mc_device *dpcon;
|
|
int dpcon_id;
|
|
int ch_id;
|
|
- int dpio_id;
|
|
struct napi_struct napi;
|
|
+ struct dpaa2_io *dpio;
|
|
struct dpaa2_io_store *store;
|
|
struct dpaa2_eth_priv *priv;
|
|
int buf_count;
|
|
struct dpaa2_eth_ch_stats stats;
|
|
+ struct bpf_prog *xdp_prog;
|
|
+ u64 rel_buf_array[DPAA2_ETH_BUFS_PER_CMD];
|
|
+ u8 rel_buf_cnt;
|
|
+ bool flush;
|
|
+};
|
|
+
|
|
+struct dpaa2_eth_cls_rule {
|
|
+ struct ethtool_rx_flow_spec fs;
|
|
+ bool in_use;
|
|
};
|
|
|
|
-struct dpaa2_eth_hash_fields {
|
|
+struct dpaa2_eth_dist_fields {
|
|
u64 rxnfc_field;
|
|
enum net_prot cls_prot;
|
|
int cls_field;
|
|
+ int offset;
|
|
int size;
|
|
+ u32 id;
|
|
};
|
|
|
|
/* Driver private data */
|
|
struct dpaa2_eth_priv {
|
|
struct net_device *net_dev;
|
|
+ /* Standard statistics */
|
|
+ struct rtnl_link_stats64 __percpu *percpu_stats;
|
|
+ /* Extra stats, in addition to the ones known by the kernel */
|
|
+ struct dpaa2_eth_drv_stats __percpu *percpu_extras;
|
|
+ bool ts_tx_en; /* Tx timestamping enabled */
|
|
+ bool ts_rx_en; /* Rx timestamping enabled */
|
|
+ u16 tx_data_offset;
|
|
+ u16 bpid;
|
|
+ u16 tx_qdid;
|
|
+ u16 rx_buf_align;
|
|
+ struct iommu_domain *iommu_domain;
|
|
+ int max_bufs_per_ch;
|
|
+ int refill_thresh;
|
|
+ bool has_xdp_prog;
|
|
+
|
|
+ void *cscn_mem; /* Tx congestion notifications are written here */
|
|
+ void *cscn_unaligned;
|
|
+ dma_addr_t cscn_dma;
|
|
|
|
u8 num_fqs;
|
|
struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES];
|
|
@@ -311,51 +417,193 @@ struct dpaa2_eth_priv {
|
|
struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS];
|
|
|
|
struct dpni_attr dpni_attrs;
|
|
- u16 tx_data_offset;
|
|
-
|
|
+ u16 dpni_ver_major;
|
|
+ u16 dpni_ver_minor;
|
|
struct fsl_mc_device *dpbp_dev;
|
|
- u16 bpid;
|
|
- struct iommu_domain *iommu_domain;
|
|
|
|
- u16 tx_qdid;
|
|
struct fsl_mc_io *mc_io;
|
|
/* Cores which have an affine DPIO/DPCON.
|
|
* This is the cpu set on which Rx and Tx conf frames are processed
|
|
*/
|
|
struct cpumask dpio_cpumask;
|
|
|
|
- /* Standard statistics */
|
|
- struct rtnl_link_stats64 __percpu *percpu_stats;
|
|
- /* Extra stats, in addition to the ones known by the kernel */
|
|
- struct dpaa2_eth_drv_stats __percpu *percpu_extras;
|
|
-
|
|
u16 mc_token;
|
|
|
|
struct dpni_link_state link_state;
|
|
bool do_link_poll;
|
|
struct task_struct *poll_thread;
|
|
|
|
+ /* Rx distribution (hash and flow steering) header fields
|
|
+ * supported by the driver
|
|
+ */
|
|
+ struct dpaa2_eth_dist_fields *dist_fields;
|
|
+ u8 num_dist_fields;
|
|
/* enabled ethtool hashing bits */
|
|
u64 rx_hash_fields;
|
|
+#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS
|
|
+ struct dpaa2_debugfs dbg;
|
|
+#endif
|
|
+ /* array of classification rules */
|
|
+ struct dpaa2_eth_cls_rule *cls_rule;
|
|
+ struct dpni_tx_shaping_cfg shaping_cfg;
|
|
+
|
|
+ u8 dcbx_mode;
|
|
+ struct ieee_pfc pfc;
|
|
+ bool vlan_clsf_set;
|
|
+ bool tx_pause_frames;
|
|
+
|
|
+ bool ceetm_en;
|
|
+};
|
|
+
|
|
+enum dpaa2_eth_rx_dist {
|
|
+ DPAA2_ETH_RX_DIST_HASH,
|
|
+ DPAA2_ETH_RX_DIST_FS,
|
|
+ DPAA2_ETH_RX_DIST_LEGACY
|
|
};
|
|
|
|
-/* default Rx hash options, set during probing */
|
|
-#define DPAA2_RXH_SUPPORTED (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \
|
|
- | RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 \
|
|
- | RXH_L4_B_2_3)
|
|
+/* Supported Rx distribution field ids */
|
|
+#define DPAA2_ETH_DIST_ETHSRC BIT(0)
|
|
+#define DPAA2_ETH_DIST_ETHDST BIT(1)
|
|
+#define DPAA2_ETH_DIST_ETHTYPE BIT(2)
|
|
+#define DPAA2_ETH_DIST_VLAN BIT(3)
|
|
+#define DPAA2_ETH_DIST_IPSRC BIT(4)
|
|
+#define DPAA2_ETH_DIST_IPDST BIT(5)
|
|
+#define DPAA2_ETH_DIST_IPPROTO BIT(6)
|
|
+#define DPAA2_ETH_DIST_L4SRC BIT(7)
|
|
+#define DPAA2_ETH_DIST_L4DST BIT(8)
|
|
+#define DPAA2_ETH_DIST_ALL (~0U)
|
|
+
|
|
+/* Default Rx hash key */
|
|
+#define DPAA2_ETH_DIST_DEFAULT_HASH \
|
|
+ (DPAA2_ETH_DIST_IPPROTO | \
|
|
+ DPAA2_ETH_DIST_IPSRC | DPAA2_ETH_DIST_IPDST | \
|
|
+ DPAA2_ETH_DIST_L4SRC | DPAA2_ETH_DIST_L4DST)
|
|
|
|
#define dpaa2_eth_hash_enabled(priv) \
|
|
((priv)->dpni_attrs.num_queues > 1)
|
|
|
|
+#define dpaa2_eth_fs_enabled(priv) \
|
|
+ (!((priv)->dpni_attrs.options & DPNI_OPT_NO_FS))
|
|
+
|
|
+#define dpaa2_eth_fs_mask_enabled(priv) \
|
|
+ ((priv)->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING)
|
|
+
|
|
+#define dpaa2_eth_fs_count(priv) \
|
|
+ ((priv)->dpni_attrs.fs_entries)
|
|
+
|
|
/* Required by struct dpni_rx_tc_dist_cfg::key_cfg_iova */
|
|
#define DPAA2_CLASSIFIER_DMA_SIZE 256
|
|
|
|
extern const struct ethtool_ops dpaa2_ethtool_ops;
|
|
extern const char dpaa2_eth_drv_version[];
|
|
|
|
-static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv)
|
|
+static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
|
|
+ u16 ver_major, u16 ver_minor)
|
|
+{
|
|
+ if (priv->dpni_ver_major == ver_major)
|
|
+ return priv->dpni_ver_minor - ver_minor;
|
|
+ return priv->dpni_ver_major - ver_major;
|
|
+}
|
|
+
|
|
+#define DPNI_DIST_KEY_VER_MAJOR 7
|
|
+#define DPNI_DIST_KEY_VER_MINOR 5
|
|
+
|
|
+static inline bool dpaa2_eth_has_legacy_dist(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return (dpaa2_eth_cmp_dpni_ver(priv, DPNI_DIST_KEY_VER_MAJOR,
|
|
+ DPNI_DIST_KEY_VER_MINOR) < 0);
|
|
+}
|
|
+
|
|
+/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around
|
|
+ * the buffer also needs space for its shared info struct, and we need
|
|
+ * to allocate enough to accommodate hardware alignment restrictions
|
|
+ */
|
|
+static inline unsigned int dpaa2_eth_buf_raw_size(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return DPAA2_ETH_SKB_SIZE + priv->rx_buf_align;
|
|
+}
|
|
+
|
|
+/* Total headroom needed by the hardware in Tx frame buffers */
|
|
+static inline unsigned int
|
|
+dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, struct sk_buff *skb)
|
|
+{
|
|
+ unsigned int headroom = DPAA2_ETH_SWA_SIZE;
|
|
+
|
|
+ /* If we don't have an skb (e.g. XDP buffer), we only need space for
|
|
+ * the software annotation area
|
|
+ */
|
|
+ if (!skb)
|
|
+ return headroom;
|
|
+
|
|
+ /* For non-linear skbs we have no headroom requirement, as we build a
|
|
+ * SG frame with a newly allocated SGT buffer
|
|
+ */
|
|
+ if (skb_is_nonlinear(skb))
|
|
+ return 0;
|
|
+
|
|
+ /* If we have Tx timestamping, need 128B hardware annotation */
|
|
+ if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
|
|
+ headroom += DPAA2_ETH_TX_HWA_SIZE;
|
|
+
|
|
+ return headroom;
|
|
+}
|
|
+
|
|
+/* Extra headroom space requested to hardware, in order to make sure there's
|
|
+ * no realloc'ing in forwarding scenarios. We need to reserve enough space
|
|
+ * such that we can accommodate the maximum required Tx offset and alignment
|
|
+ * in the ingress frame buffer
|
|
+ */
|
|
+static inline unsigned int dpaa2_eth_rx_headroom(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return priv->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN -
|
|
+ DPAA2_ETH_RX_HWA_SIZE;
|
|
+}
|
|
+
|
|
+static inline int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv)
|
|
{
|
|
return priv->dpni_attrs.num_queues;
|
|
}
|
|
|
|
+static inline int dpaa2_eth_tc_count(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return priv->dpni_attrs.num_tcs;
|
|
+}
|
|
+
|
|
+static inline bool dpaa2_eth_is_pfc_enabled(struct dpaa2_eth_priv *priv,
|
|
+ int traffic_class)
|
|
+{
|
|
+ return priv->pfc.pfc_en & (1 << traffic_class);
|
|
+}
|
|
+
|
|
+enum dpaa2_eth_td_cfg {
|
|
+ DPAA2_ETH_TD_NONE,
|
|
+ DPAA2_ETH_TD_QUEUE,
|
|
+ DPAA2_ETH_TD_GROUP
|
|
+};
|
|
+
|
|
+static inline enum dpaa2_eth_td_cfg
|
|
+dpaa2_eth_get_td_type(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ bool pfc_enabled = !!(priv->pfc.pfc_en);
|
|
+
|
|
+ if (pfc_enabled)
|
|
+ return DPAA2_ETH_TD_GROUP;
|
|
+ else if (priv->tx_pause_frames)
|
|
+ return DPAA2_ETH_TD_NONE;
|
|
+ else
|
|
+ return DPAA2_ETH_TD_QUEUE;
|
|
+}
|
|
+
|
|
+static inline int dpaa2_eth_ch_count(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+void check_cls_support(struct dpaa2_eth_priv *priv);
|
|
+
|
|
+int set_rx_taildrop(struct dpaa2_eth_priv *priv);
|
|
+
|
|
+int dpaa2_eth_set_dist_key(struct dpaa2_eth_priv *priv,
|
|
+ enum dpaa2_eth_rx_dist type, u32 key_fields);
|
|
+
|
|
#endif /* __DPAA2_H */
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
|
|
@@ -1,5 +1,5 @@
|
|
/* Copyright 2014-2016 Freescale Semiconductor Inc.
|
|
- * Copyright 2016 NXP
|
|
+ * Copyright 2016-2017 NXP
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
@@ -62,6 +62,7 @@ static char dpaa2_ethtool_extras[][ETH_G
|
|
"[drv] tx conf bytes",
|
|
"[drv] tx sg frames",
|
|
"[drv] tx sg bytes",
|
|
+ "[drv] tx realloc frames",
|
|
"[drv] rx sg frames",
|
|
"[drv] rx sg bytes",
|
|
"[drv] enqueue portal busy",
|
|
@@ -69,6 +70,15 @@ static char dpaa2_ethtool_extras[][ETH_G
|
|
"[drv] dequeue portal busy",
|
|
"[drv] channel pull errors",
|
|
"[drv] cdan",
|
|
+ "[drv] tx congestion state",
|
|
+#ifdef CONFIG_FSL_QBMAN_DEBUG
|
|
+ /* FQ stats */
|
|
+ "rx pending frames",
|
|
+ "rx pending bytes",
|
|
+ "tx conf pending frames",
|
|
+ "tx conf pending bytes",
|
|
+ "buffer count"
|
|
+#endif
|
|
};
|
|
|
|
#define DPAA2_ETH_NUM_EXTRA_STATS ARRAY_SIZE(dpaa2_ethtool_extras)
|
|
@@ -76,10 +86,15 @@ static char dpaa2_ethtool_extras[][ETH_G
|
|
static void dpaa2_eth_get_drvinfo(struct net_device *net_dev,
|
|
struct ethtool_drvinfo *drvinfo)
|
|
{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+
|
|
strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
|
|
strlcpy(drvinfo->version, dpaa2_eth_drv_version,
|
|
sizeof(drvinfo->version));
|
|
- strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
|
|
+
|
|
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
|
|
+ "%u.%u", priv->dpni_ver_major, priv->dpni_ver_minor);
|
|
+
|
|
strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent),
|
|
sizeof(drvinfo->bus_info));
|
|
}
|
|
@@ -113,25 +128,37 @@ out:
|
|
return err;
|
|
}
|
|
|
|
+#define DPNI_DYNAMIC_LINK_SET_VER_MAJOR 7
|
|
+#define DPNI_DYNAMIC_LINK_SET_VER_MINOR 1
|
|
static int
|
|
dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
|
|
const struct ethtool_link_ksettings *link_settings)
|
|
{
|
|
- struct dpni_link_cfg cfg = {0};
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct dpni_link_state state = {0};
|
|
+ struct dpni_link_cfg cfg = {0};
|
|
int err = 0;
|
|
|
|
- netdev_dbg(net_dev, "Setting link parameters...");
|
|
+ /* If using an older MC version, the DPNI must be down
|
|
+ * in order to be able to change link settings. Taking steps to let
|
|
+ * the user know that.
|
|
+ */
|
|
+ if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_DYNAMIC_LINK_SET_VER_MAJOR,
|
|
+ DPNI_DYNAMIC_LINK_SET_VER_MINOR) < 0) {
|
|
+ if (netif_running(net_dev)) {
|
|
+ netdev_info(net_dev, "Interface must be brought down first.\n");
|
|
+ return -EACCES;
|
|
+ }
|
|
+ }
|
|
|
|
- /* Due to a temporary MC limitation, the DPNI must be down
|
|
- * in order to be able to change link settings. Taking steps to let
|
|
- * the user know that.
|
|
- */
|
|
- if (netif_running(net_dev)) {
|
|
- netdev_info(net_dev, "Sorry, interface must be brought down first.\n");
|
|
- return -EACCES;
|
|
+ /* Need to interrogate link state to get flow control params */
|
|
+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
|
|
+ if (err) {
|
|
+ netdev_err(net_dev, "Error getting link state\n");
|
|
+ goto out;
|
|
}
|
|
|
|
+ cfg.options = state.options;
|
|
cfg.rate = link_settings->base.speed;
|
|
if (link_settings->base.autoneg == AUTONEG_ENABLE)
|
|
cfg.options |= DPNI_LINK_OPT_AUTONEG;
|
|
@@ -149,6 +176,81 @@ dpaa2_eth_set_link_ksettings(struct net_
|
|
*/
|
|
netdev_dbg(net_dev, "ERROR %d setting link cfg\n", err);
|
|
|
|
+out:
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct dpni_link_state state = {0};
|
|
+ int err;
|
|
+
|
|
+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
|
|
+ if (err)
|
|
+ netdev_dbg(net_dev, "Error getting link state\n");
|
|
+
|
|
+ /* Report general port autonegotiation status */
|
|
+ pause->autoneg = !!(state.options & DPNI_LINK_OPT_AUTONEG);
|
|
+ pause->rx_pause = !!(state.options & DPNI_LINK_OPT_PAUSE);
|
|
+ pause->tx_pause = pause->rx_pause ^
|
|
+ !!(state.options & DPNI_LINK_OPT_ASYM_PAUSE);
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_set_pauseparam(struct net_device *net_dev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct dpni_link_state state = {0};
|
|
+ struct dpni_link_cfg cfg = {0};
|
|
+ u32 current_tx_pause;
|
|
+ int err = 0;
|
|
+
|
|
+ err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
|
|
+ if (err) {
|
|
+ netdev_dbg(net_dev, "Error getting link state\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ cfg.rate = state.rate;
|
|
+ cfg.options = state.options;
|
|
+ current_tx_pause = !!(cfg.options & DPNI_LINK_OPT_PAUSE) ^
|
|
+ !!(cfg.options & DPNI_LINK_OPT_ASYM_PAUSE);
|
|
+
|
|
+ /* We don't support changing pause frame autonegotiation separately
|
|
+ * from general port autoneg
|
|
+ */
|
|
+ if (pause->autoneg != !!(state.options & DPNI_LINK_OPT_AUTONEG))
|
|
+ netdev_warn(net_dev,
|
|
+ "Cannot change pause frame autoneg separately\n");
|
|
+
|
|
+ if (pause->rx_pause)
|
|
+ cfg.options |= DPNI_LINK_OPT_PAUSE;
|
|
+ else
|
|
+ cfg.options &= ~DPNI_LINK_OPT_PAUSE;
|
|
+
|
|
+ if (pause->rx_pause ^ pause->tx_pause)
|
|
+ cfg.options |= DPNI_LINK_OPT_ASYM_PAUSE;
|
|
+ else
|
|
+ cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE;
|
|
+
|
|
+ err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg);
|
|
+ if (err) {
|
|
+ netdev_dbg(net_dev, "Error setting link\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Enable/disable Rx FQ taildrop if Tx pause frames have changed */
|
|
+ if (current_tx_pause == pause->tx_pause)
|
|
+ goto out;
|
|
+
|
|
+ priv->tx_pause_frames = pause->tx_pause;
|
|
+ err = set_rx_taildrop(priv);
|
|
+ if (err)
|
|
+ netdev_dbg(net_dev, "Error configuring taildrop\n");
|
|
+
|
|
+out:
|
|
return err;
|
|
}
|
|
|
|
@@ -192,6 +294,13 @@ static void dpaa2_eth_get_ethtool_stats(
|
|
int j, k, err;
|
|
int num_cnt;
|
|
union dpni_statistics dpni_stats;
|
|
+
|
|
+#ifdef CONFIG_FSL_QBMAN_DEBUG
|
|
+ u32 fcnt, bcnt;
|
|
+ u32 fcnt_rx_total = 0, fcnt_tx_total = 0;
|
|
+ u32 bcnt_rx_total = 0, bcnt_tx_total = 0;
|
|
+ u32 buf_cnt;
|
|
+#endif
|
|
u64 cdan = 0;
|
|
u64 portal_busy = 0, pull_err = 0;
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
@@ -204,7 +313,7 @@ static void dpaa2_eth_get_ethtool_stats(
|
|
/* Print standard counters, from DPNI statistics */
|
|
for (j = 0; j <= 2; j++) {
|
|
err = dpni_get_statistics(priv->mc_io, 0, priv->mc_token,
|
|
- j, &dpni_stats);
|
|
+ j, 0, &dpni_stats);
|
|
if (err != 0)
|
|
netdev_warn(net_dev, "dpni_get_stats(%d) failed\n", j);
|
|
switch (j) {
|
|
@@ -240,12 +349,474 @@ static void dpaa2_eth_get_ethtool_stats(
|
|
*(data + i++) = portal_busy;
|
|
*(data + i++) = pull_err;
|
|
*(data + i++) = cdan;
|
|
+
|
|
+ *(data + i++) = dpaa2_cscn_state_congested(priv->cscn_mem);
|
|
+
|
|
+#ifdef CONFIG_FSL_QBMAN_DEBUG
|
|
+ for (j = 0; j < priv->num_fqs; j++) {
|
|
+ /* Print FQ instantaneous counts */
|
|
+ err = dpaa2_io_query_fq_count(NULL, priv->fq[j].fqid,
|
|
+ &fcnt, &bcnt);
|
|
+ if (err) {
|
|
+ netdev_warn(net_dev, "FQ query error %d", err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (priv->fq[j].type == DPAA2_TX_CONF_FQ) {
|
|
+ fcnt_tx_total += fcnt;
|
|
+ bcnt_tx_total += bcnt;
|
|
+ } else {
|
|
+ fcnt_rx_total += fcnt;
|
|
+ bcnt_rx_total += bcnt;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *(data + i++) = fcnt_rx_total;
|
|
+ *(data + i++) = bcnt_rx_total;
|
|
+ *(data + i++) = fcnt_tx_total;
|
|
+ *(data + i++) = bcnt_tx_total;
|
|
+
|
|
+ err = dpaa2_io_query_bp_count(NULL, priv->bpid, &buf_cnt);
|
|
+ if (err) {
|
|
+ netdev_warn(net_dev, "Buffer count query error %d\n", err);
|
|
+ return;
|
|
+ }
|
|
+ *(data + i++) = buf_cnt;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static int cls_key_off(struct dpaa2_eth_priv *priv, int prot, int field)
|
|
+{
|
|
+ int i, off = 0;
|
|
+
|
|
+ for (i = 0; i < priv->num_dist_fields; i++) {
|
|
+ if (priv->dist_fields[i].cls_prot == prot &&
|
|
+ priv->dist_fields[i].cls_field == field)
|
|
+ return off;
|
|
+ off += priv->dist_fields[i].size;
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static u8 cls_key_size(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ u8 i, size = 0;
|
|
+
|
|
+ for (i = 0; i < priv->num_dist_fields; i++)
|
|
+ size += priv->dist_fields[i].size;
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+void check_cls_support(struct dpaa2_eth_priv *priv)
|
|
+{
|
|
+ u8 key_size = cls_key_size(priv);
|
|
+ struct device *dev = priv->net_dev->dev.parent;
|
|
+
|
|
+ if (dpaa2_eth_hash_enabled(priv)) {
|
|
+ if (priv->dpni_attrs.fs_key_size < key_size) {
|
|
+ dev_info(dev, "max_dist_key_size = %d, expected %d. Hashing and steering are disabled\n",
|
|
+ priv->dpni_attrs.fs_key_size,
|
|
+ key_size);
|
|
+ goto disable_fs;
|
|
+ }
|
|
+ if (priv->num_dist_fields > DPKG_MAX_NUM_OF_EXTRACTS) {
|
|
+ dev_info(dev, "Too many key fields (max = %d). Hashing and steering are disabled\n",
|
|
+ DPKG_MAX_NUM_OF_EXTRACTS);
|
|
+ goto disable_fs;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (dpaa2_eth_fs_enabled(priv)) {
|
|
+ if (!dpaa2_eth_hash_enabled(priv)) {
|
|
+ dev_info(dev, "Insufficient queues. Steering is disabled\n");
|
|
+ goto disable_fs;
|
|
+ }
|
|
+
|
|
+ if (!dpaa2_eth_fs_mask_enabled(priv)) {
|
|
+ dev_info(dev, "Key masks not supported. Steering is disabled\n");
|
|
+ goto disable_fs;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return;
|
|
+
|
|
+disable_fs:
|
|
+ priv->dpni_attrs.options |= DPNI_OPT_NO_FS;
|
|
+ priv->dpni_attrs.options &= ~DPNI_OPT_HAS_KEY_MASKING;
|
|
+}
|
|
+
|
|
+static int prep_l4_rule(struct dpaa2_eth_priv *priv,
|
|
+ struct ethtool_tcpip4_spec *l4_value,
|
|
+ struct ethtool_tcpip4_spec *l4_mask,
|
|
+ void *key, void *mask, u8 l4_proto)
|
|
+{
|
|
+ int offset;
|
|
+
|
|
+ if (l4_mask->tos) {
|
|
+ netdev_err(priv->net_dev, "ToS is not supported for IPv4 L4\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ if (l4_mask->ip4src) {
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC);
|
|
+ *(u32 *)(key + offset) = l4_value->ip4src;
|
|
+ *(u32 *)(mask + offset) = l4_mask->ip4src;
|
|
+ }
|
|
+
|
|
+ if (l4_mask->ip4dst) {
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST);
|
|
+ *(u32 *)(key + offset) = l4_value->ip4dst;
|
|
+ *(u32 *)(mask + offset) = l4_mask->ip4dst;
|
|
+ }
|
|
+
|
|
+ if (l4_mask->psrc) {
|
|
+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
|
|
+ *(u32 *)(key + offset) = l4_value->psrc;
|
|
+ *(u32 *)(mask + offset) = l4_mask->psrc;
|
|
+ }
|
|
+
|
|
+ if (l4_mask->pdst) {
|
|
+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
|
|
+ *(u32 *)(key + offset) = l4_value->pdst;
|
|
+ *(u32 *)(mask + offset) = l4_mask->pdst;
|
|
+ }
|
|
+
|
|
+ /* Only apply the rule for the user-specified L4 protocol
|
|
+ * and if ethertype matches IPv4
|
|
+ */
|
|
+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE);
|
|
+ *(u16 *)(key + offset) = htons(ETH_P_IP);
|
|
+ *(u16 *)(mask + offset) = 0xFFFF;
|
|
+
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO);
|
|
+ *(u8 *)(key + offset) = l4_proto;
|
|
+ *(u8 *)(mask + offset) = 0xFF;
|
|
+
|
|
+ /* TODO: check IP version */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int prep_eth_rule(struct dpaa2_eth_priv *priv,
|
|
+ struct ethhdr *eth_value, struct ethhdr *eth_mask,
|
|
+ void *key, void *mask)
|
|
+{
|
|
+ int offset;
|
|
+
|
|
+ if (eth_mask->h_proto) {
|
|
+ netdev_err(priv->net_dev, "Ethertype is not supported!\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ if (!is_zero_ether_addr(eth_mask->h_source)) {
|
|
+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_SA);
|
|
+ ether_addr_copy(key + offset, eth_value->h_source);
|
|
+ ether_addr_copy(mask + offset, eth_mask->h_source);
|
|
+ }
|
|
+
|
|
+ if (!is_zero_ether_addr(eth_mask->h_dest)) {
|
|
+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA);
|
|
+ ether_addr_copy(key + offset, eth_value->h_dest);
|
|
+ ether_addr_copy(mask + offset, eth_mask->h_dest);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int prep_user_ip_rule(struct dpaa2_eth_priv *priv,
|
|
+ struct ethtool_usrip4_spec *uip_value,
|
|
+ struct ethtool_usrip4_spec *uip_mask,
|
|
+ void *key, void *mask)
|
|
+{
|
|
+ int offset;
|
|
+
|
|
+ if (uip_mask->tos)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (uip_mask->ip4src) {
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_SRC);
|
|
+ *(u32 *)(key + offset) = uip_value->ip4src;
|
|
+ *(u32 *)(mask + offset) = uip_mask->ip4src;
|
|
+ }
|
|
+
|
|
+ if (uip_mask->ip4dst) {
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_DST);
|
|
+ *(u32 *)(key + offset) = uip_value->ip4dst;
|
|
+ *(u32 *)(mask + offset) = uip_mask->ip4dst;
|
|
+ }
|
|
+
|
|
+ if (uip_mask->proto) {
|
|
+ offset = cls_key_off(priv, NET_PROT_IP, NH_FLD_IP_PROTO);
|
|
+ *(u32 *)(key + offset) = uip_value->proto;
|
|
+ *(u32 *)(mask + offset) = uip_mask->proto;
|
|
+ }
|
|
+ if (uip_mask->l4_4_bytes) {
|
|
+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
|
|
+ *(u16 *)(key + offset) = uip_value->l4_4_bytes << 16;
|
|
+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes << 16;
|
|
+
|
|
+ offset = cls_key_off(priv, NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
|
|
+ *(u16 *)(key + offset) = uip_value->l4_4_bytes & 0xFFFF;
|
|
+ *(u16 *)(mask + offset) = uip_mask->l4_4_bytes & 0xFFFF;
|
|
+ }
|
|
+
|
|
+ /* Ethertype must be IP */
|
|
+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_TYPE);
|
|
+ *(u16 *)(key + offset) = htons(ETH_P_IP);
|
|
+ *(u16 *)(mask + offset) = 0xFFFF;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int prep_ext_rule(struct dpaa2_eth_priv *priv,
|
|
+ struct ethtool_flow_ext *ext_value,
|
|
+ struct ethtool_flow_ext *ext_mask,
|
|
+ void *key, void *mask)
|
|
+{
|
|
+ int offset;
|
|
+
|
|
+ if (ext_mask->vlan_etype)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (ext_mask->vlan_tci) {
|
|
+ offset = cls_key_off(priv, NET_PROT_VLAN, NH_FLD_VLAN_TCI);
|
|
+ *(u16 *)(key + offset) = ext_value->vlan_tci;
|
|
+ *(u16 *)(mask + offset) = ext_mask->vlan_tci;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int prep_mac_ext_rule(struct dpaa2_eth_priv *priv,
|
|
+ struct ethtool_flow_ext *ext_value,
|
|
+ struct ethtool_flow_ext *ext_mask,
|
|
+ void *key, void *mask)
|
|
+{
|
|
+ int offset;
|
|
+
|
|
+ if (!is_zero_ether_addr(ext_mask->h_dest)) {
|
|
+ offset = cls_key_off(priv, NET_PROT_ETH, NH_FLD_ETH_DA);
|
|
+ ether_addr_copy(key + offset, ext_value->h_dest);
|
|
+ ether_addr_copy(mask + offset, ext_mask->h_dest);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int prep_cls_rule(struct net_device *net_dev,
|
|
+ struct ethtool_rx_flow_spec *fs,
|
|
+ void *key)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ const u8 key_size = cls_key_size(priv);
|
|
+ void *msk = key + key_size;
|
|
+ int err;
|
|
+
|
|
+ memset(key, 0, key_size * 2);
|
|
+
|
|
+ switch (fs->flow_type & 0xff) {
|
|
+ case TCP_V4_FLOW:
|
|
+ err = prep_l4_rule(priv, &fs->h_u.tcp_ip4_spec,
|
|
+ &fs->m_u.tcp_ip4_spec, key, msk,
|
|
+ IPPROTO_TCP);
|
|
+ break;
|
|
+ case UDP_V4_FLOW:
|
|
+ err = prep_l4_rule(priv, &fs->h_u.udp_ip4_spec,
|
|
+ &fs->m_u.udp_ip4_spec, key, msk,
|
|
+ IPPROTO_UDP);
|
|
+ break;
|
|
+ case SCTP_V4_FLOW:
|
|
+ err = prep_l4_rule(priv, &fs->h_u.sctp_ip4_spec,
|
|
+ &fs->m_u.sctp_ip4_spec, key, msk,
|
|
+ IPPROTO_SCTP);
|
|
+ break;
|
|
+ case ETHER_FLOW:
|
|
+ err = prep_eth_rule(priv, &fs->h_u.ether_spec,
|
|
+ &fs->m_u.ether_spec, key, msk);
|
|
+ break;
|
|
+ case IP_USER_FLOW:
|
|
+ err = prep_user_ip_rule(priv, &fs->h_u.usr_ip4_spec,
|
|
+ &fs->m_u.usr_ip4_spec, key, msk);
|
|
+ break;
|
|
+ default:
|
|
+ /* TODO: AH, ESP */
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (fs->flow_type & FLOW_EXT) {
|
|
+ err = prep_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (fs->flow_type & FLOW_MAC_EXT) {
|
|
+ err = prep_mac_ext_rule(priv, &fs->h_ext, &fs->m_ext, key, msk);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int del_cls(struct net_device *net_dev, int location);
|
|
+
|
|
+static int do_cls(struct net_device *net_dev,
|
|
+ struct ethtool_rx_flow_spec *fs,
|
|
+ bool add)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ struct device *dev = net_dev->dev.parent;
|
|
+ const int rule_cnt = dpaa2_eth_fs_count(priv);
|
|
+ struct dpni_rule_cfg rule_cfg;
|
|
+ struct dpni_fs_action_cfg fs_act = { 0 };
|
|
+ void *dma_mem;
|
|
+ int err = 0, tc;
|
|
+
|
|
+ if (!dpaa2_eth_fs_enabled(priv)) {
|
|
+ netdev_err(net_dev, "dev does not support steering!\n");
|
|
+ /* dev doesn't support steering */
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ if ((fs->ring_cookie != RX_CLS_FLOW_DISC &&
|
|
+ fs->ring_cookie >= dpaa2_eth_queue_count(priv)) ||
|
|
+ fs->location >= rule_cnt)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* When adding a new rule, check if location if available
|
|
+ * and if not, free the existing table entry before inserting
|
|
+ * the new one
|
|
+ */
|
|
+ if (add && (priv->cls_rule[fs->location].in_use == true))
|
|
+ del_cls(net_dev, fs->location);
|
|
+
|
|
+ memset(&rule_cfg, 0, sizeof(rule_cfg));
|
|
+ rule_cfg.key_size = cls_key_size(priv);
|
|
+
|
|
+ /* allocate twice the key size, for the actual key and for mask */
|
|
+ dma_mem = kzalloc(rule_cfg.key_size * 2, GFP_DMA | GFP_KERNEL);
|
|
+ if (!dma_mem)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ err = prep_cls_rule(net_dev, fs, dma_mem);
|
|
+ if (err)
|
|
+ goto err_free_mem;
|
|
+
|
|
+ rule_cfg.key_iova = dma_map_single(dev, dma_mem,
|
|
+ rule_cfg.key_size * 2,
|
|
+ DMA_TO_DEVICE);
|
|
+
|
|
+ rule_cfg.mask_iova = rule_cfg.key_iova + rule_cfg.key_size;
|
|
+
|
|
+ if (fs->ring_cookie == RX_CLS_FLOW_DISC)
|
|
+ fs_act.options |= DPNI_FS_OPT_DISCARD;
|
|
+ else
|
|
+ fs_act.flow_id = fs->ring_cookie;
|
|
+
|
|
+ for (tc = 0; tc < dpaa2_eth_tc_count(priv); tc++) {
|
|
+ if (add)
|
|
+ err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token,
|
|
+ tc, fs->location, &rule_cfg,
|
|
+ &fs_act);
|
|
+ else
|
|
+ err = dpni_remove_fs_entry(priv->mc_io, 0,
|
|
+ priv->mc_token, tc,
|
|
+ &rule_cfg);
|
|
+
|
|
+ if (err)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dma_unmap_single(dev, rule_cfg.key_iova,
|
|
+ rule_cfg.key_size * 2, DMA_TO_DEVICE);
|
|
+
|
|
+ if (err)
|
|
+ netdev_err(net_dev, "dpaa2_add/remove_cls() error %d\n", err);
|
|
+
|
|
+err_free_mem:
|
|
+ kfree(dma_mem);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int add_cls(struct net_device *net_dev,
|
|
+ struct ethtool_rx_flow_spec *fs)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ int err;
|
|
+
|
|
+ err = do_cls(net_dev, fs, true);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ priv->cls_rule[fs->location].in_use = true;
|
|
+ priv->cls_rule[fs->location].fs = *fs;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int del_cls(struct net_device *net_dev, int location)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ int err;
|
|
+
|
|
+ err = do_cls(net_dev, &priv->cls_rule[location].fs, false);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ priv->cls_rule[location].in_use = false;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int set_hash(struct net_device *net_dev, u64 data)
|
|
+{
|
|
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ u32 key = 0;
|
|
+ int i;
|
|
+
|
|
+ if (data & RXH_DISCARD)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ for (i = 0; i < priv->num_dist_fields; i++)
|
|
+ if (priv->dist_fields[i].rxnfc_field & data)
|
|
+ key |= priv->dist_fields[i].id;
|
|
+
|
|
+ return dpaa2_eth_set_dist_key(priv, DPAA2_ETH_RX_DIST_HASH, key);
|
|
+}
|
|
+
|
|
+static int dpaa2_eth_set_rxnfc(struct net_device *net_dev,
|
|
+ struct ethtool_rxnfc *rxnfc)
|
|
+{
|
|
+ int err = 0;
|
|
+
|
|
+ switch (rxnfc->cmd) {
|
|
+ case ETHTOOL_SRXCLSRLINS:
|
|
+ err = add_cls(net_dev, &rxnfc->fs);
|
|
+ break;
|
|
+ case ETHTOOL_SRXCLSRLDEL:
|
|
+ err = del_cls(net_dev, rxnfc->fs.location);
|
|
+ break;
|
|
+ case ETHTOOL_SRXFH:
|
|
+ err = set_hash(net_dev, rxnfc->data);
|
|
+ break;
|
|
+ default:
|
|
+ err = -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
}
|
|
|
|
static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
|
|
struct ethtool_rxnfc *rxnfc, u32 *rule_locs)
|
|
{
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
+ const int rule_cnt = dpaa2_eth_fs_count(priv);
|
|
+ int i, j;
|
|
|
|
switch (rxnfc->cmd) {
|
|
case ETHTOOL_GRXFH:
|
|
@@ -258,6 +829,33 @@ static int dpaa2_eth_get_rxnfc(struct ne
|
|
case ETHTOOL_GRXRINGS:
|
|
rxnfc->data = dpaa2_eth_queue_count(priv);
|
|
break;
|
|
+
|
|
+ case ETHTOOL_GRXCLSRLCNT:
|
|
+ for (i = 0, rxnfc->rule_cnt = 0; i < rule_cnt; i++)
|
|
+ if (priv->cls_rule[i].in_use)
|
|
+ rxnfc->rule_cnt++;
|
|
+ rxnfc->data = rule_cnt;
|
|
+ break;
|
|
+
|
|
+ case ETHTOOL_GRXCLSRULE:
|
|
+ if (!priv->cls_rule[rxnfc->fs.location].in_use)
|
|
+ return -EINVAL;
|
|
+
|
|
+ rxnfc->fs = priv->cls_rule[rxnfc->fs.location].fs;
|
|
+ break;
|
|
+
|
|
+ case ETHTOOL_GRXCLSRLALL:
|
|
+ for (i = 0, j = 0; i < rule_cnt; i++) {
|
|
+ if (!priv->cls_rule[i].in_use)
|
|
+ continue;
|
|
+ if (j == rxnfc->rule_cnt)
|
|
+ return -EMSGSIZE;
|
|
+ rule_locs[j++] = i;
|
|
+ }
|
|
+ rxnfc->rule_cnt = j;
|
|
+ rxnfc->data = rule_cnt;
|
|
+ break;
|
|
+
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
@@ -270,8 +868,11 @@ const struct ethtool_ops dpaa2_ethtool_o
|
|
.get_link = ethtool_op_get_link,
|
|
.get_link_ksettings = dpaa2_eth_get_link_ksettings,
|
|
.set_link_ksettings = dpaa2_eth_set_link_ksettings,
|
|
+ .get_pauseparam = dpaa2_eth_get_pauseparam,
|
|
+ .set_pauseparam = dpaa2_eth_set_pauseparam,
|
|
.get_sset_count = dpaa2_eth_get_sset_count,
|
|
.get_ethtool_stats = dpaa2_eth_get_ethtool_stats,
|
|
.get_strings = dpaa2_eth_get_strings,
|
|
.get_rxnfc = dpaa2_eth_get_rxnfc,
|
|
+ .set_rxnfc = dpaa2_eth_set_rxnfc,
|
|
};
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
|
|
@@ -39,9 +39,11 @@
|
|
#define DPNI_VER_MAJOR 7
|
|
#define DPNI_VER_MINOR 0
|
|
#define DPNI_CMD_BASE_VERSION 1
|
|
+#define DPNI_CMD_2ND_VERSION 2
|
|
#define DPNI_CMD_ID_OFFSET 4
|
|
|
|
#define DPNI_CMD(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_BASE_VERSION)
|
|
+#define DPNI_CMD_V2(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_2ND_VERSION)
|
|
|
|
#define DPNI_CMDID_OPEN DPNI_CMD(0x801)
|
|
#define DPNI_CMDID_CLOSE DPNI_CMD(0x800)
|
|
@@ -64,7 +66,7 @@
|
|
#define DPNI_CMDID_GET_IRQ_STATUS DPNI_CMD(0x016)
|
|
#define DPNI_CMDID_CLEAR_IRQ_STATUS DPNI_CMD(0x017)
|
|
|
|
-#define DPNI_CMDID_SET_POOLS DPNI_CMD(0x200)
|
|
+#define DPNI_CMDID_SET_POOLS DPNI_CMD_V2(0x200)
|
|
#define DPNI_CMDID_SET_ERRORS_BEHAVIOR DPNI_CMD(0x20B)
|
|
|
|
#define DPNI_CMDID_GET_QDID DPNI_CMD(0x210)
|
|
@@ -73,7 +75,7 @@
|
|
#define DPNI_CMDID_SET_MAX_FRAME_LENGTH DPNI_CMD(0x216)
|
|
#define DPNI_CMDID_GET_MAX_FRAME_LENGTH DPNI_CMD(0x217)
|
|
#define DPNI_CMDID_SET_LINK_CFG DPNI_CMD(0x21A)
|
|
-#define DPNI_CMDID_SET_TX_SHAPING DPNI_CMD(0x21B)
|
|
+#define DPNI_CMDID_SET_TX_SHAPING DPNI_CMD_V2(0x21B)
|
|
|
|
#define DPNI_CMDID_SET_MCAST_PROMISC DPNI_CMD(0x220)
|
|
#define DPNI_CMDID_GET_MCAST_PROMISC DPNI_CMD(0x221)
|
|
@@ -87,11 +89,16 @@
|
|
|
|
#define DPNI_CMDID_SET_RX_TC_DIST DPNI_CMD(0x235)
|
|
|
|
+#define DPNI_CMDID_SET_QOS_TBL DPNI_CMD(0x240)
|
|
+#define DPNI_CMDID_ADD_QOS_ENT DPNI_CMD(0x241)
|
|
+#define DPNI_CMDID_REMOVE_QOS_ENT DPNI_CMD(0x242)
|
|
#define DPNI_CMDID_ADD_FS_ENT DPNI_CMD(0x244)
|
|
#define DPNI_CMDID_REMOVE_FS_ENT DPNI_CMD(0x245)
|
|
#define DPNI_CMDID_CLR_FS_ENT DPNI_CMD(0x246)
|
|
|
|
-#define DPNI_CMDID_GET_STATISTICS DPNI_CMD(0x25D)
|
|
+#define DPNI_CMDID_SET_TX_PRIORITIES DPNI_CMD_V2(0x250)
|
|
+#define DPNI_CMDID_GET_STATISTICS DPNI_CMD_V2(0x25D)
|
|
+#define DPNI_CMDID_RESET_STATISTICS DPNI_CMD(0x25E)
|
|
#define DPNI_CMDID_GET_QUEUE DPNI_CMD(0x25F)
|
|
#define DPNI_CMDID_SET_QUEUE DPNI_CMD(0x260)
|
|
#define DPNI_CMDID_GET_TAILDROP DPNI_CMD(0x261)
|
|
@@ -110,6 +117,9 @@
|
|
#define DPNI_CMDID_GET_OFFLOAD DPNI_CMD(0x26B)
|
|
#define DPNI_CMDID_SET_OFFLOAD DPNI_CMD(0x26C)
|
|
|
|
+#define DPNI_CMDID_SET_RX_FS_DIST DPNI_CMD(0x273)
|
|
+#define DPNI_CMDID_SET_RX_HASH_DIST DPNI_CMD(0x274)
|
|
+
|
|
/* Macros for accessing command fields smaller than 1byte */
|
|
#define DPNI_MASK(field) \
|
|
GENMASK(DPNI_##field##_SHIFT + DPNI_##field##_SIZE - 1, \
|
|
@@ -126,13 +136,14 @@ struct dpni_cmd_open {
|
|
|
|
#define DPNI_BACKUP_POOL(val, order) (((val) & 0x1) << (order))
|
|
struct dpni_cmd_set_pools {
|
|
- /* cmd word 0 */
|
|
u8 num_dpbp;
|
|
u8 backup_pool_mask;
|
|
__le16 pad;
|
|
- /* cmd word 0..4 */
|
|
- __le32 dpbp_id[DPNI_MAX_DPBP];
|
|
- /* cmd word 4..6 */
|
|
+ struct {
|
|
+ __le16 dpbp_id;
|
|
+ u8 priority_mask;
|
|
+ u8 pad;
|
|
+ } pool[DPNI_MAX_DPBP];
|
|
__le16 buffer_size[DPNI_MAX_DPBP];
|
|
};
|
|
|
|
@@ -303,6 +314,7 @@ struct dpni_rsp_get_tx_data_offset {
|
|
|
|
struct dpni_cmd_get_statistics {
|
|
u8 page_number;
|
|
+ u8 param;
|
|
};
|
|
|
|
struct dpni_rsp_get_statistics {
|
|
@@ -335,6 +347,22 @@ struct dpni_rsp_get_link_state {
|
|
__le64 options;
|
|
};
|
|
|
|
+#define DPNI_COUPLED_SHIFT 0
|
|
+#define DPNI_COUPLED_SIZE 1
|
|
+
|
|
+struct dpni_cmd_set_tx_shaping {
|
|
+ /* cmd word 0 */
|
|
+ __le16 tx_cr_max_burst_size;
|
|
+ __le16 tx_er_max_burst_size;
|
|
+ __le32 pad;
|
|
+ /* cmd word 1 */
|
|
+ __le32 tx_cr_rate_limit;
|
|
+ __le32 tx_er_rate_limit;
|
|
+ /* cmd word 2 */
|
|
+ /* from LSB: coupled:1 */
|
|
+ u8 coupled;
|
|
+};
|
|
+
|
|
struct dpni_cmd_set_max_frame_length {
|
|
__le16 max_frame_length;
|
|
};
|
|
@@ -394,6 +422,24 @@ struct dpni_cmd_clear_mac_filters {
|
|
u8 flags;
|
|
};
|
|
|
|
+#define DPNI_SEPARATE_GRP_SHIFT 0
|
|
+#define DPNI_SEPARATE_GRP_SIZE 1
|
|
+#define DPNI_MODE_1_SHIFT 0
|
|
+#define DPNI_MODE_1_SIZE 4
|
|
+#define DPNI_MODE_2_SHIFT 4
|
|
+#define DPNI_MODE_2_SIZE 4
|
|
+
|
|
+struct dpni_cmd_set_tx_priorities {
|
|
+ __le16 flags;
|
|
+ u8 prio_group_A;
|
|
+ u8 prio_group_B;
|
|
+ __le32 pad0;
|
|
+ u8 modes[4];
|
|
+ __le32 pad1;
|
|
+ __le64 pad2;
|
|
+ __le16 delta_bandwidth[8];
|
|
+};
|
|
+
|
|
#define DPNI_DIST_MODE_SHIFT 0
|
|
#define DPNI_DIST_MODE_SIZE 4
|
|
#define DPNI_MISS_ACTION_SHIFT 4
|
|
@@ -503,6 +549,63 @@ struct dpni_cmd_set_queue {
|
|
__le64 user_context;
|
|
};
|
|
|
|
+#define DPNI_DISCARD_ON_MISS_SHIFT 0
|
|
+#define DPNI_DISCARD_ON_MISS_SIZE 1
|
|
+
|
|
+struct dpni_cmd_set_qos_table {
|
|
+ __le32 pad;
|
|
+ u8 default_tc;
|
|
+ /* only the LSB */
|
|
+ u8 discard_on_miss;
|
|
+ __le16 pad1[21];
|
|
+ __le64 key_cfg_iova;
|
|
+};
|
|
+
|
|
+struct dpni_cmd_add_qos_entry {
|
|
+ __le16 pad;
|
|
+ u8 tc_id;
|
|
+ u8 key_size;
|
|
+ __le16 index;
|
|
+ __le16 pad2;
|
|
+ __le64 key_iova;
|
|
+ __le64 mask_iova;
|
|
+};
|
|
+
|
|
+struct dpni_cmd_remove_qos_entry {
|
|
+ u8 pad1[3];
|
|
+ u8 key_size;
|
|
+ __le32 pad2;
|
|
+ __le64 key_iova;
|
|
+ __le64 mask_iova;
|
|
+};
|
|
+
|
|
+struct dpni_cmd_add_fs_entry {
|
|
+ /* cmd word 0 */
|
|
+ __le16 options;
|
|
+ u8 tc_id;
|
|
+ u8 key_size;
|
|
+ __le16 index;
|
|
+ __le16 flow_id;
|
|
+ /* cmd word 1 */
|
|
+ __le64 key_iova;
|
|
+ /* cmd word 2 */
|
|
+ __le64 mask_iova;
|
|
+ /* cmd word 3 */
|
|
+ __le64 flc;
|
|
+};
|
|
+
|
|
+struct dpni_cmd_remove_fs_entry {
|
|
+ /* cmd word 0 */
|
|
+ __le16 pad0;
|
|
+ u8 tc_id;
|
|
+ u8 key_size;
|
|
+ __le32 pad1;
|
|
+ /* cmd word 1 */
|
|
+ __le64 key_iova;
|
|
+ /* cmd word 2 */
|
|
+ __le64 mask_iova;
|
|
+};
|
|
+
|
|
struct dpni_cmd_set_taildrop {
|
|
/* cmd word 0 */
|
|
u8 congestion_point;
|
|
@@ -538,4 +641,79 @@ struct dpni_rsp_get_taildrop {
|
|
__le32 threshold;
|
|
};
|
|
|
|
+struct dpni_rsp_get_api_version {
|
|
+ u16 major;
|
|
+ u16 minor;
|
|
+};
|
|
+
|
|
+#define DPNI_DEST_TYPE_SHIFT 0
|
|
+#define DPNI_DEST_TYPE_SIZE 4
|
|
+#define DPNI_CONG_UNITS_SHIFT 4
|
|
+#define DPNI_CONG_UNITS_SIZE 2
|
|
+
|
|
+struct dpni_cmd_set_congestion_notification {
|
|
+ /* cmd word 0 */
|
|
+ u8 qtype;
|
|
+ u8 tc;
|
|
+ u8 pad[6];
|
|
+ /* cmd word 1 */
|
|
+ __le32 dest_id;
|
|
+ __le16 notification_mode;
|
|
+ u8 dest_priority;
|
|
+ /* from LSB: dest_type: 4 units:2 */
|
|
+ u8 type_units;
|
|
+ /* cmd word 2 */
|
|
+ __le64 message_iova;
|
|
+ /* cmd word 3 */
|
|
+ __le64 message_ctx;
|
|
+ /* cmd word 4 */
|
|
+ __le32 threshold_entry;
|
|
+ __le32 threshold_exit;
|
|
+};
|
|
+
|
|
+struct dpni_cmd_get_congestion_notification {
|
|
+ /* cmd word 0 */
|
|
+ u8 qtype;
|
|
+ u8 tc;
|
|
+};
|
|
+
|
|
+struct dpni_rsp_get_congestion_notification {
|
|
+ /* cmd word 0 */
|
|
+ __le64 pad;
|
|
+ /* cmd word 1 */
|
|
+ __le32 dest_id;
|
|
+ __le16 notification_mode;
|
|
+ u8 dest_priority;
|
|
+ /* from LSB: dest_type: 4 units:2 */
|
|
+ u8 type_units;
|
|
+ /* cmd word 2 */
|
|
+ __le64 message_iova;
|
|
+ /* cmd word 3 */
|
|
+ __le64 message_ctx;
|
|
+ /* cmd word 4 */
|
|
+ __le32 threshold_entry;
|
|
+ __le32 threshold_exit;
|
|
+};
|
|
+
|
|
+#define DPNI_RX_FS_DIST_ENABLE_SHIFT 0
|
|
+#define DPNI_RX_FS_DIST_ENABLE_SIZE 1
|
|
+struct dpni_cmd_set_rx_fs_dist {
|
|
+ __le16 dist_size;
|
|
+ u8 enable;
|
|
+ u8 tc;
|
|
+ __le16 miss_flow_id;
|
|
+ __le16 pad;
|
|
+ __le64 key_cfg_iova;
|
|
+};
|
|
+
|
|
+#define DPNI_RX_HASH_DIST_ENABLE_SHIFT 0
|
|
+#define DPNI_RX_HASH_DIST_ENABLE_SIZE 1
|
|
+struct dpni_cmd_set_rx_hash_dist {
|
|
+ __le16 dist_size;
|
|
+ u8 enable;
|
|
+ u8 tc;
|
|
+ __le32 pad;
|
|
+ __le64 key_cfg_iova;
|
|
+};
|
|
+
|
|
#endif /* _FSL_DPNI_CMD_H */
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpni.c
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c
|
|
@@ -122,7 +122,7 @@ int dpni_open(struct fsl_mc_io *mc_io,
|
|
int dpni_id,
|
|
u16 *token)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_open *cmd_params;
|
|
|
|
int err;
|
|
@@ -160,7 +160,7 @@ int dpni_close(struct fsl_mc_io *mc_io,
|
|
u32 cmd_flags,
|
|
u16 token)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
|
|
/* prepare command */
|
|
cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE,
|
|
@@ -188,7 +188,7 @@ int dpni_set_pools(struct fsl_mc_io *mc_
|
|
u16 token,
|
|
const struct dpni_pools_cfg *cfg)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_pools *cmd_params;
|
|
int i;
|
|
|
|
@@ -199,7 +199,10 @@ int dpni_set_pools(struct fsl_mc_io *mc_
|
|
cmd_params = (struct dpni_cmd_set_pools *)cmd.params;
|
|
cmd_params->num_dpbp = cfg->num_dpbp;
|
|
for (i = 0; i < DPNI_MAX_DPBP; i++) {
|
|
- cmd_params->dpbp_id[i] = cpu_to_le32(cfg->pools[i].dpbp_id);
|
|
+ cmd_params->pool[i].dpbp_id =
|
|
+ cpu_to_le16(cfg->pools[i].dpbp_id);
|
|
+ cmd_params->pool[i].priority_mask =
|
|
+ cfg->pools[i].priority_mask;
|
|
cmd_params->buffer_size[i] =
|
|
cpu_to_le16(cfg->pools[i].buffer_size);
|
|
cmd_params->backup_pool_mask |=
|
|
@@ -222,7 +225,7 @@ int dpni_enable(struct fsl_mc_io *mc_io,
|
|
u32 cmd_flags,
|
|
u16 token)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
|
|
/* prepare command */
|
|
cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE,
|
|
@@ -245,7 +248,7 @@ int dpni_disable(struct fsl_mc_io *mc_io
|
|
u32 cmd_flags,
|
|
u16 token)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
|
|
/* prepare command */
|
|
cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE,
|
|
@@ -270,7 +273,7 @@ int dpni_is_enabled(struct fsl_mc_io *mc
|
|
u16 token,
|
|
int *en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_is_enabled *rsp_params;
|
|
int err;
|
|
|
|
@@ -303,7 +306,7 @@ int dpni_reset(struct fsl_mc_io *mc_io,
|
|
u32 cmd_flags,
|
|
u16 token)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
|
|
/* prepare command */
|
|
cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET,
|
|
@@ -335,7 +338,7 @@ int dpni_set_irq_enable(struct fsl_mc_io
|
|
u8 irq_index,
|
|
u8 en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_irq_enable *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -366,7 +369,7 @@ int dpni_get_irq_enable(struct fsl_mc_io
|
|
u8 irq_index,
|
|
u8 *en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_irq_enable *cmd_params;
|
|
struct dpni_rsp_get_irq_enable *rsp_params;
|
|
|
|
@@ -413,7 +416,7 @@ int dpni_set_irq_mask(struct fsl_mc_io *
|
|
u8 irq_index,
|
|
u32 mask)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_irq_mask *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -447,7 +450,7 @@ int dpni_get_irq_mask(struct fsl_mc_io *
|
|
u8 irq_index,
|
|
u32 *mask)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_irq_mask *cmd_params;
|
|
struct dpni_rsp_get_irq_mask *rsp_params;
|
|
int err;
|
|
@@ -489,7 +492,7 @@ int dpni_get_irq_status(struct fsl_mc_io
|
|
u8 irq_index,
|
|
u32 *status)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_irq_status *cmd_params;
|
|
struct dpni_rsp_get_irq_status *rsp_params;
|
|
int err;
|
|
@@ -532,7 +535,7 @@ int dpni_clear_irq_status(struct fsl_mc_
|
|
u8 irq_index,
|
|
u32 status)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_clear_irq_status *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -561,7 +564,7 @@ int dpni_get_attributes(struct fsl_mc_io
|
|
u16 token,
|
|
struct dpni_attr *attr)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_attr *rsp_params;
|
|
|
|
int err;
|
|
@@ -609,7 +612,7 @@ int dpni_set_errors_behavior(struct fsl_
|
|
u16 token,
|
|
struct dpni_error_cfg *cfg)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_errors_behavior *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -641,7 +644,7 @@ int dpni_get_buffer_layout(struct fsl_mc
|
|
enum dpni_queue_type qtype,
|
|
struct dpni_buffer_layout *layout)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_buffer_layout *cmd_params;
|
|
struct dpni_rsp_get_buffer_layout *rsp_params;
|
|
int err;
|
|
@@ -689,7 +692,7 @@ int dpni_set_buffer_layout(struct fsl_mc
|
|
enum dpni_queue_type qtype,
|
|
const struct dpni_buffer_layout *layout)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_buffer_layout *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -731,7 +734,7 @@ int dpni_set_offload(struct fsl_mc_io *m
|
|
enum dpni_offload type,
|
|
u32 config)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_offload *cmd_params;
|
|
|
|
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_OFFLOAD,
|
|
@@ -750,7 +753,7 @@ int dpni_get_offload(struct fsl_mc_io *m
|
|
enum dpni_offload type,
|
|
u32 *config)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_offload *cmd_params;
|
|
struct dpni_rsp_get_offload *rsp_params;
|
|
int err;
|
|
@@ -792,7 +795,7 @@ int dpni_get_qdid(struct fsl_mc_io *mc_i
|
|
enum dpni_queue_type qtype,
|
|
u16 *qdid)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_qdid *cmd_params;
|
|
struct dpni_rsp_get_qdid *rsp_params;
|
|
int err;
|
|
@@ -830,7 +833,7 @@ int dpni_get_tx_data_offset(struct fsl_m
|
|
u16 token,
|
|
u16 *data_offset)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_tx_data_offset *rsp_params;
|
|
int err;
|
|
|
|
@@ -865,7 +868,7 @@ int dpni_set_link_cfg(struct fsl_mc_io *
|
|
u16 token,
|
|
const struct dpni_link_cfg *cfg)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_link_cfg *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -894,7 +897,7 @@ int dpni_get_link_state(struct fsl_mc_io
|
|
u16 token,
|
|
struct dpni_link_state *state)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_link_state *rsp_params;
|
|
int err;
|
|
|
|
@@ -918,6 +921,44 @@ int dpni_get_link_state(struct fsl_mc_io
|
|
}
|
|
|
|
/**
|
|
+ * dpni_set_tx_shaping() - Set the transmit shaping
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @tx_cr_shaper: TX committed rate shaping configuration
|
|
+ * @tx_er_shaper: TX excess rate shaping configuration
|
|
+ * @coupled: Committed and excess rate shapers are coupled
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_tx_shaping_cfg *tx_cr_shaper,
|
|
+ const struct dpni_tx_shaping_cfg *tx_er_shaper,
|
|
+ int coupled)
|
|
+{
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+ struct dpni_cmd_set_tx_shaping *cmd_params;
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SHAPING,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_tx_shaping *)cmd.params;
|
|
+ cmd_params->tx_cr_max_burst_size =
|
|
+ cpu_to_le16(tx_cr_shaper->max_burst_size);
|
|
+ cmd_params->tx_er_max_burst_size =
|
|
+ cpu_to_le16(tx_er_shaper->max_burst_size);
|
|
+ cmd_params->tx_cr_rate_limit = cpu_to_le32(tx_cr_shaper->rate_limit);
|
|
+ cmd_params->tx_er_rate_limit = cpu_to_le32(tx_er_shaper->rate_limit);
|
|
+ dpni_set_field(cmd_params->coupled, COUPLED, coupled);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
* dpni_set_max_frame_length() - Set the maximum received frame length.
|
|
* @mc_io: Pointer to MC portal's I/O object
|
|
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
@@ -933,7 +974,7 @@ int dpni_set_max_frame_length(struct fsl
|
|
u16 token,
|
|
u16 max_frame_length)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_max_frame_length *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -963,7 +1004,7 @@ int dpni_get_max_frame_length(struct fsl
|
|
u16 token,
|
|
u16 *max_frame_length)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_max_frame_length *rsp_params;
|
|
int err;
|
|
|
|
@@ -998,7 +1039,7 @@ int dpni_set_multicast_promisc(struct fs
|
|
u16 token,
|
|
int en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_multicast_promisc *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1026,7 +1067,7 @@ int dpni_get_multicast_promisc(struct fs
|
|
u16 token,
|
|
int *en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_multicast_promisc *rsp_params;
|
|
int err;
|
|
|
|
@@ -1061,7 +1102,7 @@ int dpni_set_unicast_promisc(struct fsl_
|
|
u16 token,
|
|
int en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_unicast_promisc *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1089,7 +1130,7 @@ int dpni_get_unicast_promisc(struct fsl_
|
|
u16 token,
|
|
int *en)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_unicast_promisc *rsp_params;
|
|
int err;
|
|
|
|
@@ -1124,7 +1165,7 @@ int dpni_set_primary_mac_addr(struct fsl
|
|
u16 token,
|
|
const u8 mac_addr[6])
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_primary_mac_addr *cmd_params;
|
|
int i;
|
|
|
|
@@ -1154,7 +1195,7 @@ int dpni_get_primary_mac_addr(struct fsl
|
|
u16 token,
|
|
u8 mac_addr[6])
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_primary_mac_addr *rsp_params;
|
|
int i, err;
|
|
|
|
@@ -1193,7 +1234,7 @@ int dpni_get_port_mac_addr(struct fsl_mc
|
|
u16 token,
|
|
u8 mac_addr[6])
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_rsp_get_port_mac_addr *rsp_params;
|
|
int i, err;
|
|
|
|
@@ -1229,7 +1270,7 @@ int dpni_add_mac_addr(struct fsl_mc_io *
|
|
u16 token,
|
|
const u8 mac_addr[6])
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_add_mac_addr *cmd_params;
|
|
int i;
|
|
|
|
@@ -1259,7 +1300,7 @@ int dpni_remove_mac_addr(struct fsl_mc_i
|
|
u16 token,
|
|
const u8 mac_addr[6])
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_remove_mac_addr *cmd_params;
|
|
int i;
|
|
|
|
@@ -1293,7 +1334,7 @@ int dpni_clear_mac_filters(struct fsl_mc
|
|
int unicast,
|
|
int multicast)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_clear_mac_filters *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1309,6 +1350,55 @@ int dpni_clear_mac_filters(struct fsl_mc
|
|
}
|
|
|
|
/**
|
|
+ * dpni_set_tx_priorities() - Set transmission TC priority configuration
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: Transmission selection configuration
|
|
+ *
|
|
+ * warning: Allowed only when DPNI is disabled
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_set_tx_priorities(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_tx_priorities_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_set_tx_priorities *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+ int i;
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_PRIORITIES,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_tx_priorities *)cmd.params;
|
|
+ dpni_set_field(cmd_params->flags,
|
|
+ SEPARATE_GRP,
|
|
+ cfg->separate_groups);
|
|
+ cmd_params->prio_group_A = cfg->prio_group_A;
|
|
+ cmd_params->prio_group_B = cfg->prio_group_B;
|
|
+
|
|
+ for (i = 0; i + 1 < DPNI_MAX_TC; i += 2) {
|
|
+ dpni_set_field(cmd_params->modes[i / 2],
|
|
+ MODE_1,
|
|
+ cfg->tc_sched[i].mode);
|
|
+ dpni_set_field(cmd_params->modes[i / 2],
|
|
+ MODE_2,
|
|
+ cfg->tc_sched[i + 1].mode);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < DPNI_MAX_TC; i++) {
|
|
+ cmd_params->delta_bandwidth[i] =
|
|
+ cpu_to_le16(cfg->tc_sched[i].delta_bandwidth);
|
|
+ }
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
* dpni_set_rx_tc_dist() - Set Rx traffic class distribution configuration
|
|
* @mc_io: Pointer to MC portal's I/O object
|
|
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
@@ -1327,7 +1417,7 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io
|
|
u8 tc_id,
|
|
const struct dpni_rx_tc_dist_cfg *cfg)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_rx_tc_dist *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1346,6 +1436,293 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io
|
|
return mc_send_command(mc_io, &cmd);
|
|
}
|
|
|
|
+/*
|
|
+ * dpni_set_qos_table() - Set QoS mapping table
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: QoS table configuration
|
|
+ *
|
|
+ * This function and all QoS-related functions require that
|
|
+ *'max_tcs > 1' was set at DPNI creation.
|
|
+ *
|
|
+ * warning: Before calling this function, call dpkg_prepare_key_cfg() to
|
|
+ * prepare the key_cfg_iova parameter
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_set_qos_table(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_qos_tbl_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_set_qos_table *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_qos_table *)cmd.params;
|
|
+ cmd_params->default_tc = cfg->default_tc;
|
|
+ cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova);
|
|
+ dpni_set_field(cmd_params->discard_on_miss,
|
|
+ ENABLE,
|
|
+ cfg->discard_on_miss);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class)
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: QoS rule to add
|
|
+ * @tc_id: Traffic class selection (0-7)
|
|
+ * @index: Location in the QoS table where to insert the entry.
|
|
+ * Only relevant if MASKING is enabled for QoS classification on
|
|
+ * this DPNI, it is ignored for exact match.
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_add_qos_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rule_cfg *cfg,
|
|
+ u8 tc_id,
|
|
+ u16 index)
|
|
+{
|
|
+ struct dpni_cmd_add_qos_entry *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_add_qos_entry *)cmd.params;
|
|
+ cmd_params->tc_id = tc_id;
|
|
+ cmd_params->key_size = cfg->key_size;
|
|
+ cmd_params->index = cpu_to_le16(index);
|
|
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
|
|
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_remove_qos_entry() - Remove QoS mapping entry
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: QoS rule to remove
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rule_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_remove_qos_entry *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_QOS_ENT,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_remove_qos_entry *)cmd.params;
|
|
+ cmd_params->key_size = cfg->key_size;
|
|
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
|
|
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class
|
|
+ * (to select a flow ID)
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @tc_id: Traffic class selection (0-7)
|
|
+ * @index: Location in the QoS table where to insert the entry.
|
|
+ * Only relevant if MASKING is enabled for QoS
|
|
+ * classification on this DPNI, it is ignored for exact match.
|
|
+ * @cfg: Flow steering rule to add
|
|
+ * @action: Action to be taken as result of a classification hit
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_add_fs_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ u8 tc_id,
|
|
+ u16 index,
|
|
+ const struct dpni_rule_cfg *cfg,
|
|
+ const struct dpni_fs_action_cfg *action)
|
|
+{
|
|
+ struct dpni_cmd_add_fs_entry *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_FS_ENT,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_add_fs_entry *)cmd.params;
|
|
+ cmd_params->tc_id = tc_id;
|
|
+ cmd_params->key_size = cfg->key_size;
|
|
+ cmd_params->index = cpu_to_le16(index);
|
|
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
|
|
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
|
|
+ cmd_params->options = cpu_to_le16(action->options);
|
|
+ cmd_params->flow_id = cpu_to_le16(action->flow_id);
|
|
+ cmd_params->flc = cpu_to_le64(action->flc);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_remove_fs_entry() - Remove Flow Steering entry from a specific
|
|
+ * traffic class
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @tc_id: Traffic class selection (0-7)
|
|
+ * @cfg: Flow steering rule to remove
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ u8 tc_id,
|
|
+ const struct dpni_rule_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_remove_fs_entry *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_FS_ENT,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_remove_fs_entry *)cmd.params;
|
|
+ cmd_params->tc_id = tc_id;
|
|
+ cmd_params->key_size = cfg->key_size;
|
|
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
|
|
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_set_congestion_notification() - Set traffic class congestion
|
|
+ * notification configuration
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @qtype: Type of queue - Rx, Tx and Tx confirm types are supported
|
|
+ * @tc_id: Traffic class selection (0-7)
|
|
+ * @cfg: Congestion notification configuration
|
|
+ *
|
|
+ * Return: '0' on Success; error code otherwise.
|
|
+ */
|
|
+int dpni_set_congestion_notification(
|
|
+ struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ enum dpni_queue_type qtype,
|
|
+ u8 tc_id,
|
|
+ const struct dpni_congestion_notification_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_set_congestion_notification *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(
|
|
+ DPNI_CMDID_SET_CONGESTION_NOTIFICATION,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_congestion_notification *)cmd.params;
|
|
+ cmd_params->qtype = qtype;
|
|
+ cmd_params->tc = tc_id;
|
|
+ cmd_params->dest_id = cpu_to_le32(cfg->dest_cfg.dest_id);
|
|
+ cmd_params->notification_mode = cpu_to_le16(cfg->notification_mode);
|
|
+ cmd_params->dest_priority = cfg->dest_cfg.priority;
|
|
+ dpni_set_field(cmd_params->type_units, DEST_TYPE,
|
|
+ cfg->dest_cfg.dest_type);
|
|
+ dpni_set_field(cmd_params->type_units, CONG_UNITS, cfg->units);
|
|
+ cmd_params->message_iova = cpu_to_le64(cfg->message_iova);
|
|
+ cmd_params->message_ctx = cpu_to_le64(cfg->message_ctx);
|
|
+ cmd_params->threshold_entry = cpu_to_le32(cfg->threshold_entry);
|
|
+ cmd_params->threshold_exit = cpu_to_le32(cfg->threshold_exit);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_get_congestion_notification() - Get traffic class congestion
|
|
+ * notification configuration
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @qtype: Type of queue - Rx, Tx and Tx confirm types are supported
|
|
+ * @tc_id: bits 7-4 contain ceetm channel index (valid only for TX);
|
|
+ * bits 3-0 contain traffic class.
|
|
+ * Use macro DPNI_BUILD_CH_TC() to build correct value for
|
|
+ * tc_id parameter.
|
|
+ * @cfg: congestion notification configuration
|
|
+ *
|
|
+ * Return: '0' on Success; error code otherwise.
|
|
+ */
|
|
+int dpni_get_congestion_notification(
|
|
+ struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ enum dpni_queue_type qtype,
|
|
+ u8 tc_id,
|
|
+ struct dpni_congestion_notification_cfg *cfg)
|
|
+{
|
|
+ struct dpni_rsp_get_congestion_notification *rsp_params;
|
|
+ struct dpni_cmd_get_congestion_notification *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+ int err;
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(
|
|
+ DPNI_CMDID_GET_CONGESTION_NOTIFICATION,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_get_congestion_notification *)cmd.params;
|
|
+ cmd_params->qtype = qtype;
|
|
+ cmd_params->tc = tc_id;
|
|
+
|
|
+ /* send command to mc*/
|
|
+ err = mc_send_command(mc_io, &cmd);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ rsp_params = (struct dpni_rsp_get_congestion_notification *)cmd.params;
|
|
+ cfg->units = dpni_get_field(rsp_params->type_units, CONG_UNITS);
|
|
+ cfg->threshold_entry = le32_to_cpu(rsp_params->threshold_entry);
|
|
+ cfg->threshold_exit = le32_to_cpu(rsp_params->threshold_exit);
|
|
+ cfg->message_ctx = le64_to_cpu(rsp_params->message_ctx);
|
|
+ cfg->message_iova = le64_to_cpu(rsp_params->message_iova);
|
|
+ cfg->notification_mode = le16_to_cpu(rsp_params->notification_mode);
|
|
+ cfg->dest_cfg.dest_id = le32_to_cpu(rsp_params->dest_id);
|
|
+ cfg->dest_cfg.priority = rsp_params->dest_priority;
|
|
+ cfg->dest_cfg.dest_type = dpni_get_field(rsp_params->type_units,
|
|
+ DEST_TYPE);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* dpni_set_queue() - Set queue parameters
|
|
* @mc_io: Pointer to MC portal's I/O object
|
|
@@ -1371,7 +1748,7 @@ int dpni_set_queue(struct fsl_mc_io *mc_
|
|
u8 options,
|
|
const struct dpni_queue *queue)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_queue *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1419,7 +1796,7 @@ int dpni_get_queue(struct fsl_mc_io *mc_
|
|
struct dpni_queue *queue,
|
|
struct dpni_queue_id *qid)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_queue *cmd_params;
|
|
struct dpni_rsp_get_queue *rsp_params;
|
|
int err;
|
|
@@ -1463,6 +1840,8 @@ int dpni_get_queue(struct fsl_mc_io *mc_
|
|
* @token: Token of DPNI object
|
|
* @page: Selects the statistics page to retrieve, see
|
|
* DPNI_GET_STATISTICS output. Pages are numbered 0 to 2.
|
|
+ * @param: Custom parameter for some pages used to select a certain
|
|
+ * statistic source, for example the TC.
|
|
* @stat: Structure containing the statistics
|
|
*
|
|
* Return: '0' on Success; Error code otherwise.
|
|
@@ -1471,9 +1850,10 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
u32 cmd_flags,
|
|
u16 token,
|
|
u8 page,
|
|
+ u8 param,
|
|
union dpni_statistics *stat)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_statistics *cmd_params;
|
|
struct dpni_rsp_get_statistics *rsp_params;
|
|
int i, err;
|
|
@@ -1484,6 +1864,7 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
token);
|
|
cmd_params = (struct dpni_cmd_get_statistics *)cmd.params;
|
|
cmd_params->page_number = page;
|
|
+ cmd_params->param = param;
|
|
|
|
/* send command to mc */
|
|
err = mc_send_command(mc_io, &cmd);
|
|
@@ -1499,6 +1880,29 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
}
|
|
|
|
/**
|
|
+ * dpni_reset_statistics() - Clears DPNI statistics
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_reset_statistics(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token)
|
|
+{
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET_STATISTICS,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
* dpni_set_taildrop() - Set taildrop per queue or TC
|
|
* @mc_io: Pointer to MC portal's I/O object
|
|
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
@@ -1506,7 +1910,10 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
* @cg_point: Congestion point
|
|
* @q_type: Queue type on which the taildrop is configured.
|
|
* Only Rx queues are supported for now
|
|
- * @tc: Traffic class to apply this taildrop to
|
|
+ * @tc: bits 7-4 contain ceetm channel index (valid only for TX);
|
|
+ * bits 3-0 contain traffic class.
|
|
+ * Use macro DPNI_BUILD_CH_TC() to build correct value for
|
|
+ * tc parameter.
|
|
* @q_index: Index of the queue if the DPNI supports multiple queues for
|
|
* traffic distribution. Ignored if CONGESTION_POINT is not 0.
|
|
* @taildrop: Taildrop structure
|
|
@@ -1522,7 +1929,7 @@ int dpni_set_taildrop(struct fsl_mc_io *
|
|
u8 index,
|
|
struct dpni_taildrop *taildrop)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_set_taildrop *cmd_params;
|
|
|
|
/* prepare command */
|
|
@@ -1550,7 +1957,10 @@ int dpni_set_taildrop(struct fsl_mc_io *
|
|
* @cg_point: Congestion point
|
|
* @q_type: Queue type on which the taildrop is configured.
|
|
* Only Rx queues are supported for now
|
|
- * @tc: Traffic class to apply this taildrop to
|
|
+ * @tc: bits 7-4 contain ceetm channel index (valid only for TX);
|
|
+ * bits 3-0 contain traffic class.
|
|
+ * Use macro DPNI_BUILD_CH_TC() to build correct value for
|
|
+ * tc parameter.
|
|
* @q_index: Index of the queue if the DPNI supports multiple queues for
|
|
* traffic distribution. Ignored if CONGESTION_POINT is not 0.
|
|
* @taildrop: Taildrop structure
|
|
@@ -1566,7 +1976,7 @@ int dpni_get_taildrop(struct fsl_mc_io *
|
|
u8 index,
|
|
struct dpni_taildrop *taildrop)
|
|
{
|
|
- struct mc_command cmd = { 0 };
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
struct dpni_cmd_get_taildrop *cmd_params;
|
|
struct dpni_rsp_get_taildrop *rsp_params;
|
|
int err;
|
|
@@ -1594,3 +2004,109 @@ int dpni_get_taildrop(struct fsl_mc_io *
|
|
|
|
return 0;
|
|
}
|
|
+
|
|
+/**
|
|
+ * dpni_get_api_version() - Get Data Path Network Interface API version
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @major_ver: Major version of data path network interface API
|
|
+ * @minor_ver: Minor version of data path network interface API
|
|
+ *
|
|
+ * Return: '0' on Success; Error code otherwise.
|
|
+ */
|
|
+int dpni_get_api_version(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 *major_ver,
|
|
+ u16 *minor_ver)
|
|
+{
|
|
+ struct dpni_rsp_get_api_version *rsp_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+ int err;
|
|
+
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_API_VERSION,
|
|
+ cmd_flags, 0);
|
|
+
|
|
+ err = mc_send_command(mc_io, &cmd);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ rsp_params = (struct dpni_rsp_get_api_version *)cmd.params;
|
|
+ *major_ver = le16_to_cpu(rsp_params->major);
|
|
+ *minor_ver = le16_to_cpu(rsp_params->minor);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_set_rx_fs_dist() - Set Rx traffic class FS distribution
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: Distribution configuration
|
|
+ * If the FS is already enabled with a previous call the classification
|
|
+ * key will be changed but all the table rules are kept. If the
|
|
+ * existing rules do not match the key the results will not be
|
|
+ * predictable. It is the user responsibility to keep key integrity.
|
|
+ * If cfg.enable is set to 1 the command will create a flow steering table
|
|
+ * and will classify packets according to this table. The packets that
|
|
+ * miss all the table rules will be classified according to settings
|
|
+ * made in dpni_set_rx_hash_dist()
|
|
+ * If cfg.enable is set to 0 the command will clear flow steering table.
|
|
+ * The packets will be classified according to settings made in
|
|
+ * dpni_set_rx_hash_dist()
|
|
+ */
|
|
+int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rx_dist_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_set_rx_fs_dist *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FS_DIST,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_rx_fs_dist *)cmd.params;
|
|
+ cmd_params->dist_size = le16_to_cpu(cfg->dist_size);
|
|
+ dpni_set_field(cmd_params->enable, RX_FS_DIST_ENABLE, cfg->enable);
|
|
+ cmd_params->tc = cfg->tc;
|
|
+ cmd_params->miss_flow_id = le16_to_cpu(cfg->fs_miss_flow_id);
|
|
+ cmd_params->key_cfg_iova = le64_to_cpu(cfg->key_cfg_iova);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dpni_set_rx_hash_dist() - Set Rx traffic class HASH distribution
|
|
+ * @mc_io: Pointer to MC portal's I/O object
|
|
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
|
+ * @token: Token of DPNI object
|
|
+ * @cfg: Distribution configuration
|
|
+ * If cfg.enable is set to 1 the packets will be classified using a hash
|
|
+ * function based on the key received in cfg.key_cfg_iova parameter.
|
|
+ * If cfg.enable is set to 0 the packets will be sent to the queue configured
|
|
+ * in dpni_set_rx_dist_default_queue() call
|
|
+ */
|
|
+int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rx_dist_cfg *cfg)
|
|
+{
|
|
+ struct dpni_cmd_set_rx_hash_dist *cmd_params;
|
|
+ struct fsl_mc_command cmd = { 0 };
|
|
+
|
|
+ /* prepare command */
|
|
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_HASH_DIST,
|
|
+ cmd_flags,
|
|
+ token);
|
|
+ cmd_params = (struct dpni_cmd_set_rx_hash_dist *)cmd.params;
|
|
+ cmd_params->dist_size = le16_to_cpu(cfg->dist_size);
|
|
+ dpni_set_field(cmd_params->enable, RX_FS_DIST_ENABLE, cfg->enable);
|
|
+ cmd_params->tc = cfg->tc;
|
|
+ cmd_params->key_cfg_iova = le64_to_cpu(cfg->key_cfg_iova);
|
|
+
|
|
+ /* send command to mc*/
|
|
+ return mc_send_command(mc_io, &cmd);
|
|
+}
|
|
--- a/drivers/staging/fsl-dpaa2/ethernet/dpni.h
|
|
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h
|
|
@@ -52,6 +52,14 @@ struct fsl_mc_io;
|
|
* Maximum number of buffer pools per DPNI
|
|
*/
|
|
#define DPNI_MAX_DPBP 8
|
|
+/**
|
|
+ * Maximum number of senders
|
|
+ */
|
|
+#define DPNI_MAX_SENDERS 16
|
|
+/**
|
|
+ * Maximum distribution size
|
|
+ */
|
|
+#define DPNI_MAX_DIST_SIZE 16
|
|
|
|
/**
|
|
* All traffic classes considered; see dpni_set_queue()
|
|
@@ -123,13 +131,15 @@ struct dpni_pools_cfg {
|
|
/**
|
|
* struct pools - Buffer pools parameters
|
|
* @dpbp_id: DPBP object ID
|
|
+ * @priority_mask: priorities served by DPBP
|
|
* @buffer_size: Buffer size
|
|
* @backup_pool: Backup pool
|
|
*/
|
|
struct {
|
|
- int dpbp_id;
|
|
+ u16 dpbp_id;
|
|
+ u8 priority_mask;
|
|
u16 buffer_size;
|
|
- int backup_pool;
|
|
+ u8 backup_pool;
|
|
} pools[DPNI_MAX_DPBP];
|
|
};
|
|
|
|
@@ -476,6 +486,24 @@ union dpni_statistics {
|
|
u64 egress_confirmed_frames;
|
|
} page_2;
|
|
/**
|
|
+ * struct page_3 - Page_3 statistics structure with values for the
|
|
+ * selected TC
|
|
+ * @ceetm_dequeue_bytes: Cumulative count of the number of bytes
|
|
+ * dequeued
|
|
+ * @ceetm_dequeue_frames: Cumulative count of the number of frames
|
|
+ * dequeued
|
|
+ * @ceetm_reject_bytes: Cumulative count of the number of bytes in all
|
|
+ * frames whose enqueue was rejected
|
|
+ * @ceetm_reject_frames: Cumulative count of all frame enqueues
|
|
+ * rejected
|
|
+ */
|
|
+ struct {
|
|
+ u64 ceetm_dequeue_bytes;
|
|
+ u64 ceetm_dequeue_frames;
|
|
+ u64 ceetm_reject_bytes;
|
|
+ u64 ceetm_reject_frames;
|
|
+ } page_3;
|
|
+ /**
|
|
* struct raw - raw statistics structure
|
|
*/
|
|
struct {
|
|
@@ -487,8 +515,13 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
u32 cmd_flags,
|
|
u16 token,
|
|
u8 page,
|
|
+ u8 param,
|
|
union dpni_statistics *stat);
|
|
|
|
+int dpni_reset_statistics(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token);
|
|
+
|
|
/**
|
|
* Enable auto-negotiation
|
|
*/
|
|
@@ -505,6 +538,10 @@ int dpni_get_statistics(struct fsl_mc_io
|
|
* Enable a-symmetric pause frames
|
|
*/
|
|
#define DPNI_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL
|
|
+/**
|
|
+ * Enable priority flow control pause frames
|
|
+ */
|
|
+#define DPNI_LINK_OPT_PFC_PAUSE 0x0000000000000010ULL
|
|
|
|
/**
|
|
* struct - Structure representing DPNI link configuration
|
|
@@ -538,6 +575,23 @@ int dpni_get_link_state(struct fsl_mc_io
|
|
u16 token,
|
|
struct dpni_link_state *state);
|
|
|
|
+/**
|
|
+ * struct dpni_tx_shaping - Structure representing DPNI tx shaping configuration
|
|
+ * @rate_limit: rate in Mbps
|
|
+ * @max_burst_size: burst size in bytes (up to 64KB)
|
|
+ */
|
|
+struct dpni_tx_shaping_cfg {
|
|
+ u32 rate_limit;
|
|
+ u16 max_burst_size;
|
|
+};
|
|
+
|
|
+int dpni_set_tx_shaping(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_tx_shaping_cfg *tx_cr_shaper,
|
|
+ const struct dpni_tx_shaping_cfg *tx_er_shaper,
|
|
+ int coupled);
|
|
+
|
|
int dpni_set_max_frame_length(struct fsl_mc_io *mc_io,
|
|
u32 cmd_flags,
|
|
u16 token,
|
|
@@ -639,6 +693,70 @@ int dpni_prepare_key_cfg(const struct dp
|
|
u8 *key_cfg_buf);
|
|
|
|
/**
|
|
+ * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration
|
|
+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with
|
|
+ * key extractions to be used as the QoS criteria by calling
|
|
+ * dpkg_prepare_key_cfg()
|
|
+ * @discard_on_miss: Set to '1' to discard frames in case of no match (miss);
|
|
+ * '0' to use the 'default_tc' in such cases
|
|
+ * @default_tc: Used in case of no-match and 'discard_on_miss'= 0
|
|
+ */
|
|
+struct dpni_qos_tbl_cfg {
|
|
+ u64 key_cfg_iova;
|
|
+ int discard_on_miss;
|
|
+ u8 default_tc;
|
|
+};
|
|
+
|
|
+int dpni_set_qos_table(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_qos_tbl_cfg *cfg);
|
|
+
|
|
+/**
|
|
+ * enum dpni_tx_schedule_mode - DPNI Tx scheduling mode
|
|
+ * @DPNI_TX_SCHED_STRICT_PRIORITY: strict priority
|
|
+ * @DPNI_TX_SCHED_WEIGHTED_A: weighted based scheduling in group A
|
|
+ * @DPNI_TX_SCHED_WEIGHTED_B: weighted based scheduling in group B
|
|
+ */
|
|
+enum dpni_tx_schedule_mode {
|
|
+ DPNI_TX_SCHED_STRICT_PRIORITY = 0,
|
|
+ DPNI_TX_SCHED_WEIGHTED_A,
|
|
+ DPNI_TX_SCHED_WEIGHTED_B,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct dpni_tx_schedule_cfg - Structure representing Tx scheduling conf
|
|
+ * @mode: Scheduling mode
|
|
+ * @delta_bandwidth: Bandwidth represented in weights from 100 to 10000;
|
|
+ * not applicable for 'strict-priority' mode;
|
|
+ */
|
|
+struct dpni_tx_schedule_cfg {
|
|
+ enum dpni_tx_schedule_mode mode;
|
|
+ u16 delta_bandwidth;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct dpni_tx_priorities_cfg - Structure representing transmission
|
|
+ * priorities for DPNI TCs
|
|
+ * @tc_sched: An array of traffic-classes
|
|
+ * @prio_group_A: Priority of group A
|
|
+ * @prio_group_B: Priority of group B
|
|
+ * @separate_groups: Treat A and B groups as separate
|
|
+ * @ceetm_ch_idx: ceetm channel index to apply the changes
|
|
+ */
|
|
+struct dpni_tx_priorities_cfg {
|
|
+ struct dpni_tx_schedule_cfg tc_sched[DPNI_MAX_TC];
|
|
+ u8 prio_group_A;
|
|
+ u8 prio_group_B;
|
|
+ u8 separate_groups;
|
|
+};
|
|
+
|
|
+int dpni_set_tx_priorities(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_tx_priorities_cfg *cfg);
|
|
+
|
|
+/**
|
|
* struct dpni_rx_tc_dist_cfg - Rx traffic class distribution configuration
|
|
* @dist_size: Set the distribution size;
|
|
* supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96,
|
|
@@ -784,6 +902,108 @@ enum dpni_congestion_point {
|
|
};
|
|
|
|
/**
|
|
+ * struct dpni_dest_cfg - Structure representing DPNI destination parameters
|
|
+ * @dest_type: Destination type
|
|
+ * @dest_id: Either DPIO ID or DPCON ID, depending on the destination type
|
|
+ * @priority: Priority selection within the DPIO or DPCON channel; valid
|
|
+ * values are 0-1 or 0-7, depending on the number of priorities
|
|
+ * in that channel; not relevant for 'DPNI_DEST_NONE' option
|
|
+ */
|
|
+struct dpni_dest_cfg {
|
|
+ enum dpni_dest dest_type;
|
|
+ int dest_id;
|
|
+ u8 priority;
|
|
+};
|
|
+
|
|
+/* DPNI congestion options */
|
|
+
|
|
+/**
|
|
+ * CSCN message is written to message_iova once entering a
|
|
+ * congestion state (see 'threshold_entry')
|
|
+ */
|
|
+#define DPNI_CONG_OPT_WRITE_MEM_ON_ENTER 0x00000001
|
|
+/**
|
|
+ * CSCN message is written to message_iova once exiting a
|
|
+ * congestion state (see 'threshold_exit')
|
|
+ */
|
|
+#define DPNI_CONG_OPT_WRITE_MEM_ON_EXIT 0x00000002
|
|
+/**
|
|
+ * CSCN write will attempt to allocate into a cache (coherent write);
|
|
+ * valid only if 'DPNI_CONG_OPT_WRITE_MEM_<X>' is selected
|
|
+ */
|
|
+#define DPNI_CONG_OPT_COHERENT_WRITE 0x00000004
|
|
+/**
|
|
+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to
|
|
+ * DPIO/DPCON's WQ channel once entering a congestion state
|
|
+ * (see 'threshold_entry')
|
|
+ */
|
|
+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_ENTER 0x00000008
|
|
+/**
|
|
+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to
|
|
+ * DPIO/DPCON's WQ channel once exiting a congestion state
|
|
+ * (see 'threshold_exit')
|
|
+ */
|
|
+#define DPNI_CONG_OPT_NOTIFY_DEST_ON_EXIT 0x00000010
|
|
+/**
|
|
+ * if 'dest_cfg.dest_type != DPNI_DEST_NONE' when the CSCN is written to the
|
|
+ * sw-portal's DQRR, the DQRI interrupt is asserted immediately (if enabled)
|
|
+ */
|
|
+#define DPNI_CONG_OPT_INTR_COALESCING_DISABLED 0x00000020
|
|
+/**
|
|
+ * This congestion will trigger flow control or priority flow control.
|
|
+ * This will have effect only if flow control is enabled with
|
|
+ * dpni_set_link_cfg().
|
|
+ */
|
|
+#define DPNI_CONG_OPT_FLOW_CONTROL 0x00000040
|
|
+
|
|
+/**
|
|
+ * struct dpni_congestion_notification_cfg - congestion notification
|
|
+ * configuration
|
|
+ * @units: Units type
|
|
+ * @threshold_entry: Above this threshold we enter a congestion state.
|
|
+ * set it to '0' to disable it
|
|
+ * @threshold_exit: Below this threshold we exit the congestion state.
|
|
+ * @message_ctx: The context that will be part of the CSCN message
|
|
+ * @message_iova: I/O virtual address (must be in DMA-able memory),
|
|
+ * must be 16B aligned; valid only if 'DPNI_CONG_OPT_WRITE_MEM_<X>'
|
|
+ * is contained in 'options'
|
|
+ * @dest_cfg: CSCN can be send to either DPIO or DPCON WQ channel
|
|
+ * @notification_mode: Mask of available options; use 'DPNI_CONG_OPT_<X>' values
|
|
+ */
|
|
+
|
|
+struct dpni_congestion_notification_cfg {
|
|
+ enum dpni_congestion_unit units;
|
|
+ u32 threshold_entry;
|
|
+ u32 threshold_exit;
|
|
+ u64 message_ctx;
|
|
+ u64 message_iova;
|
|
+ struct dpni_dest_cfg dest_cfg;
|
|
+ u16 notification_mode;
|
|
+};
|
|
+
|
|
+/** Compose TC parameter for function dpni_set_congestion_notification()
|
|
+ * and dpni_get_congestion_notification().
|
|
+ */
|
|
+#define DPNI_BUILD_CH_TC(ceetm_ch_idx, tc) \
|
|
+ ((((ceetm_ch_idx) & 0x0F) << 4) | ((tc) & 0x0F))
|
|
+
|
|
+int dpni_set_congestion_notification(
|
|
+ struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ enum dpni_queue_type qtype,
|
|
+ u8 tc_id,
|
|
+ const struct dpni_congestion_notification_cfg *cfg);
|
|
+
|
|
+int dpni_get_congestion_notification(
|
|
+ struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ enum dpni_queue_type qtype,
|
|
+ u8 tc_id,
|
|
+ struct dpni_congestion_notification_cfg *cfg);
|
|
+
|
|
+/**
|
|
* struct dpni_taildrop - Structure representing the taildrop
|
|
* @enable: Indicates whether the taildrop is active or not.
|
|
* @units: Indicates the unit of THRESHOLD. Queue taildrop only supports
|
|
@@ -829,4 +1049,124 @@ struct dpni_rule_cfg {
|
|
u8 key_size;
|
|
};
|
|
|
|
+int dpni_get_api_version(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 *major_ver,
|
|
+ u16 *minor_ver);
|
|
+
|
|
+int dpni_add_qos_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rule_cfg *cfg,
|
|
+ u8 tc_id,
|
|
+ u16 index);
|
|
+
|
|
+int dpni_remove_qos_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rule_cfg *cfg);
|
|
+
|
|
+int dpni_clear_qos_table(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token);
|
|
+
|
|
+/**
|
|
+ * Discard matching traffic. If set, this takes precedence over any other
|
|
+ * configuration and matching traffic is always discarded.
|
|
+ */
|
|
+ #define DPNI_FS_OPT_DISCARD 0x1
|
|
+
|
|
+/**
|
|
+ * Set FLC value. If set, flc member of struct dpni_fs_action_cfg is used to
|
|
+ * override the FLC value set per queue.
|
|
+ * For more details check the Frame Descriptor section in the hardware
|
|
+ * documentation.
|
|
+ */
|
|
+#define DPNI_FS_OPT_SET_FLC 0x2
|
|
+
|
|
+/*
|
|
+ * Indicates whether the 6 lowest significant bits of FLC are used for stash
|
|
+ * control. If set, the 6 least significant bits in value are interpreted as
|
|
+ * follows:
|
|
+ * - bits 0-1: indicates the number of 64 byte units of context that are
|
|
+ * stashed. FLC value is interpreted as a memory address in this case,
|
|
+ * excluding the 6 LS bits.
|
|
+ * - bits 2-3: indicates the number of 64 byte units of frame annotation
|
|
+ * to be stashed. Annotation is placed at FD[ADDR].
|
|
+ * - bits 4-5: indicates the number of 64 byte units of frame data to be
|
|
+ * stashed. Frame data is placed at FD[ADDR] + FD[OFFSET].
|
|
+ * This flag is ignored if DPNI_FS_OPT_SET_FLC is not specified.
|
|
+ */
|
|
+#define DPNI_FS_OPT_SET_STASH_CONTROL 0x4
|
|
+
|
|
+/**
|
|
+ * struct dpni_fs_action_cfg - Action configuration for table look-up
|
|
+ * @flc: FLC value for traffic matching this rule. Please check the
|
|
+ * Frame Descriptor section in the hardware documentation for
|
|
+ * more information.
|
|
+ * @flow_id: Identifies the Rx queue used for matching traffic. Supported
|
|
+ * values are in range 0 to num_queue-1.
|
|
+ * @options: Any combination of DPNI_FS_OPT_ values.
|
|
+ */
|
|
+struct dpni_fs_action_cfg {
|
|
+ u64 flc;
|
|
+ u16 flow_id;
|
|
+ u16 options;
|
|
+};
|
|
+
|
|
+int dpni_add_fs_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ u8 tc_id,
|
|
+ u16 index,
|
|
+ const struct dpni_rule_cfg *cfg,
|
|
+ const struct dpni_fs_action_cfg *action);
|
|
+
|
|
+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ u8 tc_id,
|
|
+ const struct dpni_rule_cfg *cfg);
|
|
+
|
|
+/**
|
|
+ * When used for queue_idx in function dpni_set_rx_dist_default_queue
|
|
+ * will signal to dpni to drop all unclassified frames
|
|
+ */
|
|
+#define DPNI_FS_MISS_DROP ((uint16_t)-1)
|
|
+
|
|
+/**
|
|
+ * struct dpni_rx_dist_cfg - distribution configuration
|
|
+ * @dist_size: distribution size; supported values: 1,2,3,4,6,7,8,
|
|
+ * 12,14,16,24,28,32,48,56,64,96,112,128,192,224,256,384,448,
|
|
+ * 512,768,896,1024
|
|
+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with
|
|
+ * the extractions to be used for the distribution key by calling
|
|
+ * dpkg_prepare_key_cfg() relevant only when enable!=0 otherwise
|
|
+ * it can be '0'
|
|
+ * @enable: enable/disable the distribution.
|
|
+ * @tc: TC id for which distribution is set
|
|
+ * @fs_miss_flow_id: when packet misses all rules from flow steering table and
|
|
+ * hash is disabled it will be put into this queue id; use
|
|
+ * DPNI_FS_MISS_DROP to drop frames. The value of this field is
|
|
+ * used only when flow steering distribution is enabled and hash
|
|
+ * distribution is disabled
|
|
+ */
|
|
+struct dpni_rx_dist_cfg {
|
|
+ u16 dist_size;
|
|
+ u64 key_cfg_iova;
|
|
+ u8 enable;
|
|
+ u8 tc;
|
|
+ u16 fs_miss_flow_id;
|
|
+};
|
|
+
|
|
+int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rx_dist_cfg *cfg);
|
|
+
|
|
+int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
|
|
+ u32 cmd_flags,
|
|
+ u16 token,
|
|
+ const struct dpni_rx_dist_cfg *cfg);
|
|
+
|
|
#endif /* __FSL_DPNI_H */
|
|
|