From f3d63720649413ac60f4723f117280391acd5015 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:21:45 -0800 Subject: [PATCH] nfp: abm: add GRED offload Add support for GRED offload. It behaves much like RED, but can apply different parameters to different bands. GRED operates pretty much exactly like our HW/FW with a single FIFO and different RED state instances. Signed-off-by: Jakub Kicinski Reviewed-by: John Hurley Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/abm/main.c | 2 + drivers/net/ethernet/netronome/nfp/abm/main.h | 12 +- drivers/net/ethernet/netronome/nfp/abm/qdisc.c | 154 +++++++++++++++++++++++-- 3 files changed, 158 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.c b/drivers/net/ethernet/netronome/nfp/abm/main.c index b21250b..4f95f7b 100644 --- a/drivers/net/ethernet/netronome/nfp/abm/main.c +++ b/drivers/net/ethernet/netronome/nfp/abm/main.c @@ -44,6 +44,8 @@ nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev, return nfp_abm_setup_tc_mq(netdev, repr->app_priv, type_data); case TC_SETUP_QDISC_RED: return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data); + case TC_SETUP_QDISC_GRED: + return nfp_abm_setup_tc_gred(netdev, repr->app_priv, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.h b/drivers/net/ethernet/netronome/nfp/abm/main.h index 4788828..6bb4e60 100644 --- a/drivers/net/ethernet/netronome/nfp/abm/main.h +++ b/drivers/net/ethernet/netronome/nfp/abm/main.h @@ -8,6 +8,7 @@ #include #include #include +#include /* Dump of 64 PRIOs and 256 REDs seems to take 850us on Xeon v4 @ 2.20GHz; * 2.5ms / 400Hz seems more than sufficient for stats resolution. @@ -89,6 +90,7 @@ enum nfp_qdisc_type { NFP_QDISC_NONE = 0, NFP_QDISC_MQ, NFP_QDISC_RED, + NFP_QDISC_GRED, }; #define NFP_QDISC_UNTRACKED ((struct nfp_qdisc *)1UL) @@ -139,7 +141,7 @@ struct nfp_qdisc { struct nfp_alink_stats stats; struct nfp_alink_stats prev_stats; } mq; - /* TC_SETUP_QDISC_RED */ + /* TC_SETUP_QDISC_RED, TC_SETUP_QDISC_GRED */ struct { unsigned int num_bands; @@ -149,7 +151,7 @@ struct nfp_qdisc { struct nfp_alink_stats prev_stats; struct nfp_alink_xstats xstats; struct nfp_alink_xstats prev_xstats; - } band[1]; + } band[MAX_DPs]; } red; }; }; @@ -164,6 +166,8 @@ struct nfp_qdisc { * * @last_stats_update: ktime of last stats update * + * @def_band: default band to use + * * @root_qdisc: pointer to the current root of the Qdisc hierarchy * @qdiscs: all qdiscs recorded by major part of the handle */ @@ -176,6 +180,8 @@ struct nfp_abm_link { u64 last_stats_update; + u8 def_band; + struct nfp_qdisc *root_qdisc; struct radix_tree_root qdiscs; }; @@ -192,6 +198,8 @@ int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt); int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt); +int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink, + struct tc_gred_qopt_offload *opt); void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink); int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm); diff --git a/drivers/net/ethernet/netronome/nfp/abm/qdisc.c b/drivers/net/ethernet/netronome/nfp/abm/qdisc.c index b65b317..e80a3d4 100644 --- a/drivers/net/ethernet/netronome/nfp/abm/qdisc.c +++ b/drivers/net/ethernet/netronome/nfp/abm/qdisc.c @@ -15,7 +15,7 @@ static bool nfp_abm_qdisc_is_red(struct nfp_qdisc *qdisc) { - return qdisc->type == NFP_QDISC_RED; + return qdisc->type == NFP_QDISC_RED || qdisc->type == NFP_QDISC_GRED; } static bool nfp_abm_qdisc_child_valid(struct nfp_qdisc *qdisc, unsigned int id) @@ -191,12 +191,17 @@ static void nfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, unsigned int queue) { + bool good_red, good_gred; unsigned int i; - qdisc->offload_mark = qdisc->type == NFP_QDISC_RED && - qdisc->params_ok && - qdisc->use_cnt == 1 && - !qdisc->children[0]; + good_red = qdisc->type == NFP_QDISC_RED && + qdisc->params_ok && + qdisc->use_cnt == 1 && + !qdisc->children[0]; + good_gred = qdisc->type == NFP_QDISC_GRED && + qdisc->params_ok && + qdisc->use_cnt == 1; + qdisc->offload_mark = good_red || good_gred; /* If we are starting offload init prev_stats */ if (qdisc->offload_mark && !qdisc->offloaded) @@ -336,9 +341,11 @@ nfp_abm_qdisc_alloc(struct net_device *netdev, struct nfp_abm_link *alink, if (!qdisc) return NULL; - qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL); - if (!qdisc->children) - goto err_free_qdisc; + if (children) { + qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL); + if (!qdisc->children) + goto err_free_qdisc; + } qdisc->netdev = netdev; qdisc->type = type; @@ -465,6 +472,137 @@ nfp_abm_stats_red_calculate(struct nfp_alink_xstats *new, } static int +nfp_abm_gred_stats(struct nfp_abm_link *alink, u32 handle, + struct tc_gred_qopt_offload_stats *stats) +{ + struct nfp_qdisc *qdisc; + unsigned int i; + + nfp_abm_stats_update(alink); + + qdisc = nfp_abm_qdisc_find(alink, handle); + if (!qdisc) + return -EOPNOTSUPP; + /* If the qdisc offload has stopped we may need to adjust the backlog + * counters back so carry on even if qdisc is not currently offloaded. + */ + + for (i = 0; i < qdisc->red.num_bands; i++) { + if (!stats->xstats[i]) + continue; + + nfp_abm_stats_calculate(&qdisc->red.band[i].stats, + &qdisc->red.band[i].prev_stats, + &stats->bstats[i], &stats->qstats[i]); + qdisc->red.band[i].prev_stats = qdisc->red.band[i].stats; + + nfp_abm_stats_red_calculate(&qdisc->red.band[i].xstats, + &qdisc->red.band[i].prev_xstats, + stats->xstats[i]); + qdisc->red.band[i].prev_xstats = qdisc->red.band[i].xstats; + } + + return qdisc->offloaded ? 0 : -EOPNOTSUPP; +} + +static bool +nfp_abm_gred_check_params(struct nfp_abm_link *alink, + struct tc_gred_qopt_offload *opt) +{ + struct nfp_cpp *cpp = alink->abm->app->cpp; + struct nfp_abm *abm = alink->abm; + unsigned int i; + + if (opt->set.grio_on || opt->set.wred_on) { + nfp_warn(cpp, "GRED offload failed - GRIO and WRED not supported (p:%08x h:%08x)\n", + opt->parent, opt->handle); + return false; + } + if (opt->set.dp_def != alink->def_band) { + nfp_warn(cpp, "GRED offload failed - default band must be %d (p:%08x h:%08x)\n", + alink->def_band, opt->parent, opt->handle); + return false; + } + if (opt->set.dp_cnt != abm->num_bands) { + nfp_warn(cpp, "GRED offload failed - band count must be %d (p:%08x h:%08x)\n", + abm->num_bands, opt->parent, opt->handle); + return false; + } + + for (i = 0; i < abm->num_bands; i++) { + struct tc_gred_vq_qopt_offload_params *band = &opt->set.tab[i]; + + if (!band->present) + return false; + if (!band->is_ecn) { + nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n", + opt->parent, opt->handle, i); + return false; + } + if (band->is_harddrop) { + nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n", + opt->parent, opt->handle, i); + return false; + } + if (band->min != band->max) { + nfp_warn(cpp, "GRED offload failed - threshold mismatch (p:%08x h:%08x vq:%d)\n", + opt->parent, opt->handle, i); + return false; + } + if (band->min > S32_MAX) { + nfp_warn(cpp, "GRED offload failed - threshold too large %d > %d (p:%08x h:%08x vq:%d)\n", + band->min, S32_MAX, opt->parent, opt->handle, + i); + return false; + } + } + + return true; +} + +static int +nfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink, + struct tc_gred_qopt_offload *opt) +{ + struct nfp_qdisc *qdisc; + unsigned int i; + int ret; + + ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_GRED, opt->parent, + opt->handle, 0, &qdisc); + if (ret < 0) + return ret; + + qdisc->params_ok = nfp_abm_gred_check_params(alink, opt); + if (qdisc->params_ok) { + qdisc->red.num_bands = opt->set.dp_cnt; + for (i = 0; i < qdisc->red.num_bands; i++) + qdisc->red.band[i].threshold = opt->set.tab[i].min; + } + + if (qdisc->use_cnt) + nfp_abm_qdisc_offload_update(alink); + + return 0; +} + +int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink, + struct tc_gred_qopt_offload *opt) +{ + switch (opt->command) { + case TC_GRED_REPLACE: + return nfp_abm_gred_replace(netdev, alink, opt); + case TC_GRED_DESTROY: + nfp_abm_qdisc_destroy(netdev, alink, opt->handle); + return 0; + case TC_GRED_STATS: + return nfp_abm_gred_stats(alink, opt->handle, &opt->stats); + default: + return -EOPNOTSUPP; + } +} + +static int nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) { struct nfp_qdisc *qdisc; -- 2.7.4