162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2018 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/rtnetlink.h> 562306a36Sopenharmony_ci#include <net/pkt_cls.h> 662306a36Sopenharmony_ci#include <net/pkt_sched.h> 762306a36Sopenharmony_ci#include <net/red.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "../nfpcore/nfp_cpp.h" 1062306a36Sopenharmony_ci#include "../nfp_app.h" 1162306a36Sopenharmony_ci#include "../nfp_main.h" 1262306a36Sopenharmony_ci#include "../nfp_net.h" 1362306a36Sopenharmony_ci#include "../nfp_port.h" 1462306a36Sopenharmony_ci#include "main.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic bool nfp_abm_qdisc_is_red(struct nfp_qdisc *qdisc) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return qdisc->type == NFP_QDISC_RED || qdisc->type == NFP_QDISC_GRED; 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool nfp_abm_qdisc_child_valid(struct nfp_qdisc *qdisc, unsigned int id) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return qdisc->children[id] && 2462306a36Sopenharmony_ci qdisc->children[id] != NFP_QDISC_UNTRACKED; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void *nfp_abm_qdisc_tree_deref_slot(void __rcu **slot) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return rtnl_dereference(*slot); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void 3362306a36Sopenharmony_cinfp_abm_stats_propagate(struct nfp_alink_stats *parent, 3462306a36Sopenharmony_ci struct nfp_alink_stats *child) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci parent->tx_pkts += child->tx_pkts; 3762306a36Sopenharmony_ci parent->tx_bytes += child->tx_bytes; 3862306a36Sopenharmony_ci parent->backlog_pkts += child->backlog_pkts; 3962306a36Sopenharmony_ci parent->backlog_bytes += child->backlog_bytes; 4062306a36Sopenharmony_ci parent->overlimits += child->overlimits; 4162306a36Sopenharmony_ci parent->drops += child->drops; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void 4562306a36Sopenharmony_cinfp_abm_stats_update_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 4662306a36Sopenharmony_ci unsigned int queue) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 4962306a36Sopenharmony_ci unsigned int i; 5062306a36Sopenharmony_ci int err; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!qdisc->offloaded) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 5662306a36Sopenharmony_ci err = nfp_abm_ctrl_read_q_stats(alink, i, queue, 5762306a36Sopenharmony_ci &qdisc->red.band[i].stats); 5862306a36Sopenharmony_ci if (err) 5962306a36Sopenharmony_ci nfp_err(cpp, "RED stats (%d, %d) read failed with error %d\n", 6062306a36Sopenharmony_ci i, queue, err); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci err = nfp_abm_ctrl_read_q_xstats(alink, i, queue, 6362306a36Sopenharmony_ci &qdisc->red.band[i].xstats); 6462306a36Sopenharmony_ci if (err) 6562306a36Sopenharmony_ci nfp_err(cpp, "RED xstats (%d, %d) read failed with error %d\n", 6662306a36Sopenharmony_ci i, queue, err); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void 7162306a36Sopenharmony_cinfp_abm_stats_update_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned int i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (qdisc->type != NFP_QDISC_MQ) 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (i = 0; i < alink->total_queues; i++) 7962306a36Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, i)) 8062306a36Sopenharmony_ci nfp_abm_stats_update_red(alink, qdisc->children[i], i); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void __nfp_abm_stats_update(struct nfp_abm_link *alink, u64 time_now) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci alink->last_stats_update = time_now; 8662306a36Sopenharmony_ci if (alink->root_qdisc) 8762306a36Sopenharmony_ci nfp_abm_stats_update_mq(alink, alink->root_qdisc); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void nfp_abm_stats_update(struct nfp_abm_link *alink) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u64 now; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Limit the frequency of updates - stats of non-leaf qdiscs are a sum 9562306a36Sopenharmony_ci * of all their leafs, so we would read the same stat multiple times 9662306a36Sopenharmony_ci * for every dump. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci now = ktime_get(); 9962306a36Sopenharmony_ci if (now - alink->last_stats_update < NFP_ABM_STATS_REFRESH_IVAL) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci __nfp_abm_stats_update(alink, now); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void 10662306a36Sopenharmony_cinfp_abm_qdisc_unlink_children(struct nfp_qdisc *qdisc, 10762306a36Sopenharmony_ci unsigned int start, unsigned int end) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned int i; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for (i = start; i < end; i++) 11262306a36Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, i)) { 11362306a36Sopenharmony_ci qdisc->children[i]->use_cnt--; 11462306a36Sopenharmony_ci qdisc->children[i] = NULL; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void 11962306a36Sopenharmony_cinfp_abm_qdisc_offload_stop(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned int i; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Don't complain when qdisc is getting unlinked */ 12462306a36Sopenharmony_ci if (qdisc->use_cnt) 12562306a36Sopenharmony_ci nfp_warn(alink->abm->app->cpp, "Offload of '%08x' stopped\n", 12662306a36Sopenharmony_ci qdisc->handle); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!nfp_abm_qdisc_is_red(qdisc)) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 13262306a36Sopenharmony_ci qdisc->red.band[i].stats.backlog_pkts = 0; 13362306a36Sopenharmony_ci qdisc->red.band[i].stats.backlog_bytes = 0; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int 13862306a36Sopenharmony_ci__nfp_abm_stats_init(struct nfp_abm_link *alink, unsigned int band, 13962306a36Sopenharmony_ci unsigned int queue, struct nfp_alink_stats *prev_stats, 14062306a36Sopenharmony_ci struct nfp_alink_xstats *prev_xstats) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u64 backlog_pkts, backlog_bytes; 14362306a36Sopenharmony_ci int err; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Don't touch the backlog, backlog can only be reset after it has 14662306a36Sopenharmony_ci * been reported back to the tc qdisc stats. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci backlog_pkts = prev_stats->backlog_pkts; 14962306a36Sopenharmony_ci backlog_bytes = prev_stats->backlog_bytes; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci err = nfp_abm_ctrl_read_q_stats(alink, band, queue, prev_stats); 15262306a36Sopenharmony_ci if (err) { 15362306a36Sopenharmony_ci nfp_err(alink->abm->app->cpp, 15462306a36Sopenharmony_ci "RED stats init (%d, %d) failed with error %d\n", 15562306a36Sopenharmony_ci band, queue, err); 15662306a36Sopenharmony_ci return err; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci err = nfp_abm_ctrl_read_q_xstats(alink, band, queue, prev_xstats); 16062306a36Sopenharmony_ci if (err) { 16162306a36Sopenharmony_ci nfp_err(alink->abm->app->cpp, 16262306a36Sopenharmony_ci "RED xstats init (%d, %d) failed with error %d\n", 16362306a36Sopenharmony_ci band, queue, err); 16462306a36Sopenharmony_ci return err; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci prev_stats->backlog_pkts = backlog_pkts; 16862306a36Sopenharmony_ci prev_stats->backlog_bytes = backlog_bytes; 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int 17362306a36Sopenharmony_cinfp_abm_stats_init(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 17462306a36Sopenharmony_ci unsigned int queue) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci unsigned int i; 17762306a36Sopenharmony_ci int err; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 18062306a36Sopenharmony_ci err = __nfp_abm_stats_init(alink, i, queue, 18162306a36Sopenharmony_ci &qdisc->red.band[i].prev_stats, 18262306a36Sopenharmony_ci &qdisc->red.band[i].prev_xstats); 18362306a36Sopenharmony_ci if (err) 18462306a36Sopenharmony_ci return err; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void 19162306a36Sopenharmony_cinfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 19262306a36Sopenharmony_ci unsigned int queue) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci bool good_red, good_gred; 19562306a36Sopenharmony_ci unsigned int i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci good_red = qdisc->type == NFP_QDISC_RED && 19862306a36Sopenharmony_ci qdisc->params_ok && 19962306a36Sopenharmony_ci qdisc->use_cnt == 1 && 20062306a36Sopenharmony_ci !alink->has_prio && 20162306a36Sopenharmony_ci !qdisc->children[0]; 20262306a36Sopenharmony_ci good_gred = qdisc->type == NFP_QDISC_GRED && 20362306a36Sopenharmony_ci qdisc->params_ok && 20462306a36Sopenharmony_ci qdisc->use_cnt == 1; 20562306a36Sopenharmony_ci qdisc->offload_mark = good_red || good_gred; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* If we are starting offload init prev_stats */ 20862306a36Sopenharmony_ci if (qdisc->offload_mark && !qdisc->offloaded) 20962306a36Sopenharmony_ci if (nfp_abm_stats_init(alink, qdisc, queue)) 21062306a36Sopenharmony_ci qdisc->offload_mark = false; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!qdisc->offload_mark) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < alink->abm->num_bands; i++) { 21662306a36Sopenharmony_ci enum nfp_abm_q_action act; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci nfp_abm_ctrl_set_q_lvl(alink, i, queue, 21962306a36Sopenharmony_ci qdisc->red.band[i].threshold); 22062306a36Sopenharmony_ci act = qdisc->red.band[i].ecn ? 22162306a36Sopenharmony_ci NFP_ABM_ACT_MARK_DROP : NFP_ABM_ACT_DROP; 22262306a36Sopenharmony_ci nfp_abm_ctrl_set_q_act(alink, i, queue, act); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void 22762306a36Sopenharmony_cinfp_abm_offload_compile_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci unsigned int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci qdisc->offload_mark = qdisc->type == NFP_QDISC_MQ; 23262306a36Sopenharmony_ci if (!qdisc->offload_mark) 23362306a36Sopenharmony_ci return; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (i = 0; i < alink->total_queues; i++) { 23662306a36Sopenharmony_ci struct nfp_qdisc *child = qdisc->children[i]; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!nfp_abm_qdisc_child_valid(qdisc, i)) 23962306a36Sopenharmony_ci continue; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci nfp_abm_offload_compile_red(alink, child, i); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct nfp_abm *abm = alink->abm; 24862306a36Sopenharmony_ci struct radix_tree_iter iter; 24962306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 25062306a36Sopenharmony_ci void __rcu **slot; 25162306a36Sopenharmony_ci size_t i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Mark all thresholds as unconfigured */ 25462306a36Sopenharmony_ci for (i = 0; i < abm->num_bands; i++) 25562306a36Sopenharmony_ci __bitmap_set(abm->threshold_undef, 25662306a36Sopenharmony_ci i * NFP_NET_MAX_RX_RINGS + alink->queue_base, 25762306a36Sopenharmony_ci alink->total_queues); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Clear offload marks */ 26062306a36Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 26162306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_tree_deref_slot(slot); 26262306a36Sopenharmony_ci qdisc->offload_mark = false; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (alink->root_qdisc) 26662306a36Sopenharmony_ci nfp_abm_offload_compile_mq(alink, alink->root_qdisc); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Refresh offload status */ 26962306a36Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 27062306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_tree_deref_slot(slot); 27162306a36Sopenharmony_ci if (!qdisc->offload_mark && qdisc->offloaded) 27262306a36Sopenharmony_ci nfp_abm_qdisc_offload_stop(alink, qdisc); 27362306a36Sopenharmony_ci qdisc->offloaded = qdisc->offload_mark; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Reset the unconfigured thresholds */ 27762306a36Sopenharmony_ci for (i = 0; i < abm->num_thresholds; i++) 27862306a36Sopenharmony_ci if (test_bit(i, abm->threshold_undef)) 27962306a36Sopenharmony_ci __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci __nfp_abm_stats_update(alink, ktime_get()); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void 28562306a36Sopenharmony_cinfp_abm_qdisc_clear_mq(struct net_device *netdev, struct nfp_abm_link *alink, 28662306a36Sopenharmony_ci struct nfp_qdisc *qdisc) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct radix_tree_iter iter; 28962306a36Sopenharmony_ci unsigned int mq_refs = 0; 29062306a36Sopenharmony_ci void __rcu **slot; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!qdisc->use_cnt) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci /* MQ doesn't notify well on destruction, we need special handling of 29562306a36Sopenharmony_ci * MQ's children. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (qdisc->type == NFP_QDISC_MQ && 29862306a36Sopenharmony_ci qdisc == alink->root_qdisc && 29962306a36Sopenharmony_ci netdev->reg_state == NETREG_UNREGISTERING) 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Count refs held by MQ instances and clear pointers */ 30362306a36Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 30462306a36Sopenharmony_ci struct nfp_qdisc *mq = nfp_abm_qdisc_tree_deref_slot(slot); 30562306a36Sopenharmony_ci unsigned int i; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (mq->type != NFP_QDISC_MQ || mq->netdev != netdev) 30862306a36Sopenharmony_ci continue; 30962306a36Sopenharmony_ci for (i = 0; i < mq->num_children; i++) 31062306a36Sopenharmony_ci if (mq->children[i] == qdisc) { 31162306a36Sopenharmony_ci mq->children[i] = NULL; 31262306a36Sopenharmony_ci mq_refs++; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci WARN(qdisc->use_cnt != mq_refs, "non-zero qdisc use count: %d (- %d)\n", 31762306a36Sopenharmony_ci qdisc->use_cnt, mq_refs); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void 32162306a36Sopenharmony_cinfp_abm_qdisc_free(struct net_device *netdev, struct nfp_abm_link *alink, 32262306a36Sopenharmony_ci struct nfp_qdisc *qdisc) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct nfp_port *port = nfp_port_from_netdev(netdev); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!qdisc) 32762306a36Sopenharmony_ci return; 32862306a36Sopenharmony_ci nfp_abm_qdisc_clear_mq(netdev, alink, qdisc); 32962306a36Sopenharmony_ci WARN_ON(radix_tree_delete(&alink->qdiscs, 33062306a36Sopenharmony_ci TC_H_MAJ(qdisc->handle)) != qdisc); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci kfree(qdisc->children); 33362306a36Sopenharmony_ci kfree(qdisc); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci port->tc_offload_cnt--; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic struct nfp_qdisc * 33962306a36Sopenharmony_cinfp_abm_qdisc_alloc(struct net_device *netdev, struct nfp_abm_link *alink, 34062306a36Sopenharmony_ci enum nfp_qdisc_type type, u32 parent_handle, u32 handle, 34162306a36Sopenharmony_ci unsigned int children) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct nfp_port *port = nfp_port_from_netdev(netdev); 34462306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 34562306a36Sopenharmony_ci int err; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci qdisc = kzalloc(sizeof(*qdisc), GFP_KERNEL); 34862306a36Sopenharmony_ci if (!qdisc) 34962306a36Sopenharmony_ci return NULL; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (children) { 35262306a36Sopenharmony_ci qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL); 35362306a36Sopenharmony_ci if (!qdisc->children) 35462306a36Sopenharmony_ci goto err_free_qdisc; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci qdisc->netdev = netdev; 35862306a36Sopenharmony_ci qdisc->type = type; 35962306a36Sopenharmony_ci qdisc->parent_handle = parent_handle; 36062306a36Sopenharmony_ci qdisc->handle = handle; 36162306a36Sopenharmony_ci qdisc->num_children = children; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci err = radix_tree_insert(&alink->qdiscs, TC_H_MAJ(qdisc->handle), qdisc); 36462306a36Sopenharmony_ci if (err) { 36562306a36Sopenharmony_ci nfp_err(alink->abm->app->cpp, 36662306a36Sopenharmony_ci "Qdisc insertion into radix tree failed: %d\n", err); 36762306a36Sopenharmony_ci goto err_free_child_tbl; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci port->tc_offload_cnt++; 37162306a36Sopenharmony_ci return qdisc; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cierr_free_child_tbl: 37462306a36Sopenharmony_ci kfree(qdisc->children); 37562306a36Sopenharmony_cierr_free_qdisc: 37662306a36Sopenharmony_ci kfree(qdisc); 37762306a36Sopenharmony_ci return NULL; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic struct nfp_qdisc * 38162306a36Sopenharmony_cinfp_abm_qdisc_find(struct nfp_abm_link *alink, u32 handle) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci return radix_tree_lookup(&alink->qdiscs, TC_H_MAJ(handle)); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int 38762306a36Sopenharmony_cinfp_abm_qdisc_replace(struct net_device *netdev, struct nfp_abm_link *alink, 38862306a36Sopenharmony_ci enum nfp_qdisc_type type, u32 parent_handle, u32 handle, 38962306a36Sopenharmony_ci unsigned int children, struct nfp_qdisc **qdisc) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci *qdisc = nfp_abm_qdisc_find(alink, handle); 39262306a36Sopenharmony_ci if (*qdisc) { 39362306a36Sopenharmony_ci if (WARN_ON((*qdisc)->type != type)) 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci return 1; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci *qdisc = nfp_abm_qdisc_alloc(netdev, alink, type, parent_handle, handle, 39962306a36Sopenharmony_ci children); 40062306a36Sopenharmony_ci return *qdisc ? 0 : -ENOMEM; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic void 40462306a36Sopenharmony_cinfp_abm_qdisc_destroy(struct net_device *netdev, struct nfp_abm_link *alink, 40562306a36Sopenharmony_ci u32 handle) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 41062306a36Sopenharmony_ci if (!qdisc) 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* We don't get TC_SETUP_ROOT_QDISC w/ MQ when netdev is unregistered */ 41462306a36Sopenharmony_ci if (alink->root_qdisc == qdisc) 41562306a36Sopenharmony_ci qdisc->use_cnt--; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci nfp_abm_qdisc_unlink_children(qdisc, 0, qdisc->num_children); 41862306a36Sopenharmony_ci nfp_abm_qdisc_free(netdev, alink, qdisc); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (alink->root_qdisc == qdisc) { 42162306a36Sopenharmony_ci alink->root_qdisc = NULL; 42262306a36Sopenharmony_ci /* Only root change matters, other changes are acted upon on 42362306a36Sopenharmony_ci * the graft notification. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int 43062306a36Sopenharmony_cinfp_abm_qdisc_graft(struct nfp_abm_link *alink, u32 handle, u32 child_handle, 43162306a36Sopenharmony_ci unsigned int id) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct nfp_qdisc *parent, *child; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci parent = nfp_abm_qdisc_find(alink, handle); 43662306a36Sopenharmony_ci if (!parent) 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (WARN(id >= parent->num_children, 44062306a36Sopenharmony_ci "graft child out of bound %d >= %d\n", 44162306a36Sopenharmony_ci id, parent->num_children)) 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci nfp_abm_qdisc_unlink_children(parent, id, id + 1); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci child = nfp_abm_qdisc_find(alink, child_handle); 44762306a36Sopenharmony_ci if (child) 44862306a36Sopenharmony_ci child->use_cnt++; 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci child = NFP_QDISC_UNTRACKED; 45162306a36Sopenharmony_ci parent->children[id] = child; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void 45962306a36Sopenharmony_cinfp_abm_stats_calculate(struct nfp_alink_stats *new, 46062306a36Sopenharmony_ci struct nfp_alink_stats *old, 46162306a36Sopenharmony_ci struct gnet_stats_basic_sync *bstats, 46262306a36Sopenharmony_ci struct gnet_stats_queue *qstats) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci _bstats_update(bstats, new->tx_bytes - old->tx_bytes, 46562306a36Sopenharmony_ci new->tx_pkts - old->tx_pkts); 46662306a36Sopenharmony_ci qstats->qlen += new->backlog_pkts - old->backlog_pkts; 46762306a36Sopenharmony_ci qstats->backlog += new->backlog_bytes - old->backlog_bytes; 46862306a36Sopenharmony_ci qstats->overlimits += new->overlimits - old->overlimits; 46962306a36Sopenharmony_ci qstats->drops += new->drops - old->drops; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void 47362306a36Sopenharmony_cinfp_abm_stats_red_calculate(struct nfp_alink_xstats *new, 47462306a36Sopenharmony_ci struct nfp_alink_xstats *old, 47562306a36Sopenharmony_ci struct red_stats *stats) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci stats->forced_mark += new->ecn_marked - old->ecn_marked; 47862306a36Sopenharmony_ci stats->pdrop += new->pdrop - old->pdrop; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int 48262306a36Sopenharmony_cinfp_abm_gred_stats(struct nfp_abm_link *alink, u32 handle, 48362306a36Sopenharmony_ci struct tc_gred_qopt_offload_stats *stats) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 48662306a36Sopenharmony_ci unsigned int i; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci nfp_abm_stats_update(alink); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 49162306a36Sopenharmony_ci if (!qdisc) 49262306a36Sopenharmony_ci return -EOPNOTSUPP; 49362306a36Sopenharmony_ci /* If the qdisc offload has stopped we may need to adjust the backlog 49462306a36Sopenharmony_ci * counters back so carry on even if qdisc is not currently offloaded. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 49862306a36Sopenharmony_ci if (!stats->xstats[i]) 49962306a36Sopenharmony_ci continue; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->red.band[i].stats, 50262306a36Sopenharmony_ci &qdisc->red.band[i].prev_stats, 50362306a36Sopenharmony_ci &stats->bstats[i], &stats->qstats[i]); 50462306a36Sopenharmony_ci qdisc->red.band[i].prev_stats = qdisc->red.band[i].stats; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci nfp_abm_stats_red_calculate(&qdisc->red.band[i].xstats, 50762306a36Sopenharmony_ci &qdisc->red.band[i].prev_xstats, 50862306a36Sopenharmony_ci stats->xstats[i]); 50962306a36Sopenharmony_ci qdisc->red.band[i].prev_xstats = qdisc->red.band[i].xstats; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic bool 51662306a36Sopenharmony_cinfp_abm_gred_check_params(struct nfp_abm_link *alink, 51762306a36Sopenharmony_ci struct tc_gred_qopt_offload *opt) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 52062306a36Sopenharmony_ci struct nfp_abm *abm = alink->abm; 52162306a36Sopenharmony_ci unsigned int i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (opt->set.grio_on || opt->set.wred_on) { 52462306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - GRIO and WRED not supported (p:%08x h:%08x)\n", 52562306a36Sopenharmony_ci opt->parent, opt->handle); 52662306a36Sopenharmony_ci return false; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci if (opt->set.dp_def != alink->def_band) { 52962306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - default band must be %d (p:%08x h:%08x)\n", 53062306a36Sopenharmony_ci alink->def_band, opt->parent, opt->handle); 53162306a36Sopenharmony_ci return false; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci if (opt->set.dp_cnt != abm->num_bands) { 53462306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - band count must be %d (p:%08x h:%08x)\n", 53562306a36Sopenharmony_ci abm->num_bands, opt->parent, opt->handle); 53662306a36Sopenharmony_ci return false; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci for (i = 0; i < abm->num_bands; i++) { 54062306a36Sopenharmony_ci struct tc_gred_vq_qopt_offload_params *band = &opt->set.tab[i]; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (!band->present) 54362306a36Sopenharmony_ci return false; 54462306a36Sopenharmony_ci if (!band->is_ecn && !nfp_abm_has_drop(abm)) { 54562306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n", 54662306a36Sopenharmony_ci opt->parent, opt->handle, i); 54762306a36Sopenharmony_ci return false; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci if (band->is_ecn && !nfp_abm_has_mark(abm)) { 55062306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - ECN marking not supported (p:%08x h:%08x vq:%d)\n", 55162306a36Sopenharmony_ci opt->parent, opt->handle, i); 55262306a36Sopenharmony_ci return false; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci if (band->is_harddrop) { 55562306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n", 55662306a36Sopenharmony_ci opt->parent, opt->handle, i); 55762306a36Sopenharmony_ci return false; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (band->min != band->max) { 56062306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - threshold mismatch (p:%08x h:%08x vq:%d)\n", 56162306a36Sopenharmony_ci opt->parent, opt->handle, i); 56262306a36Sopenharmony_ci return false; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci if (band->min > S32_MAX) { 56562306a36Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - threshold too large %d > %d (p:%08x h:%08x vq:%d)\n", 56662306a36Sopenharmony_ci band->min, S32_MAX, opt->parent, opt->handle, 56762306a36Sopenharmony_ci i); 56862306a36Sopenharmony_ci return false; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return true; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic int 57662306a36Sopenharmony_cinfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink, 57762306a36Sopenharmony_ci struct tc_gred_qopt_offload *opt) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 58062306a36Sopenharmony_ci unsigned int i; 58162306a36Sopenharmony_ci int ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_GRED, opt->parent, 58462306a36Sopenharmony_ci opt->handle, 0, &qdisc); 58562306a36Sopenharmony_ci if (ret < 0) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci qdisc->params_ok = nfp_abm_gred_check_params(alink, opt); 58962306a36Sopenharmony_ci if (qdisc->params_ok) { 59062306a36Sopenharmony_ci qdisc->red.num_bands = opt->set.dp_cnt; 59162306a36Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 59262306a36Sopenharmony_ci qdisc->red.band[i].ecn = opt->set.tab[i].is_ecn; 59362306a36Sopenharmony_ci qdisc->red.band[i].threshold = opt->set.tab[i].min; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (qdisc->use_cnt) 59862306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ciint nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink, 60462306a36Sopenharmony_ci struct tc_gred_qopt_offload *opt) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci switch (opt->command) { 60762306a36Sopenharmony_ci case TC_GRED_REPLACE: 60862306a36Sopenharmony_ci return nfp_abm_gred_replace(netdev, alink, opt); 60962306a36Sopenharmony_ci case TC_GRED_DESTROY: 61062306a36Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci case TC_GRED_STATS: 61362306a36Sopenharmony_ci return nfp_abm_gred_stats(alink, opt->handle, &opt->stats); 61462306a36Sopenharmony_ci default: 61562306a36Sopenharmony_ci return -EOPNOTSUPP; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int 62062306a36Sopenharmony_cinfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci nfp_abm_stats_update(alink); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, opt->handle); 62762306a36Sopenharmony_ci if (!qdisc || !qdisc->offloaded) 62862306a36Sopenharmony_ci return -EOPNOTSUPP; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci nfp_abm_stats_red_calculate(&qdisc->red.band[0].xstats, 63162306a36Sopenharmony_ci &qdisc->red.band[0].prev_xstats, 63262306a36Sopenharmony_ci opt->xstats); 63362306a36Sopenharmony_ci qdisc->red.band[0].prev_xstats = qdisc->red.band[0].xstats; 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int 63862306a36Sopenharmony_cinfp_abm_red_stats(struct nfp_abm_link *alink, u32 handle, 63962306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci nfp_abm_stats_update(alink); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 64662306a36Sopenharmony_ci if (!qdisc) 64762306a36Sopenharmony_ci return -EOPNOTSUPP; 64862306a36Sopenharmony_ci /* If the qdisc offload has stopped we may need to adjust the backlog 64962306a36Sopenharmony_ci * counters back so carry on even if qdisc is not currently offloaded. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->red.band[0].stats, 65362306a36Sopenharmony_ci &qdisc->red.band[0].prev_stats, 65462306a36Sopenharmony_ci stats->bstats, stats->qstats); 65562306a36Sopenharmony_ci qdisc->red.band[0].prev_stats = qdisc->red.band[0].stats; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic bool 66162306a36Sopenharmony_cinfp_abm_red_check_params(struct nfp_abm_link *alink, 66262306a36Sopenharmony_ci struct tc_red_qopt_offload *opt) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 66562306a36Sopenharmony_ci struct nfp_abm *abm = alink->abm; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (!opt->set.is_ecn && !nfp_abm_has_drop(abm)) { 66862306a36Sopenharmony_ci nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n", 66962306a36Sopenharmony_ci opt->parent, opt->handle); 67062306a36Sopenharmony_ci return false; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci if (opt->set.is_ecn && !nfp_abm_has_mark(abm)) { 67362306a36Sopenharmony_ci nfp_warn(cpp, "RED offload failed - ECN marking not supported (p:%08x h:%08x)\n", 67462306a36Sopenharmony_ci opt->parent, opt->handle); 67562306a36Sopenharmony_ci return false; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci if (opt->set.is_harddrop) { 67862306a36Sopenharmony_ci nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n", 67962306a36Sopenharmony_ci opt->parent, opt->handle); 68062306a36Sopenharmony_ci return false; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci if (opt->set.min != opt->set.max) { 68362306a36Sopenharmony_ci nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n", 68462306a36Sopenharmony_ci opt->parent, opt->handle); 68562306a36Sopenharmony_ci return false; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci if (opt->set.min > NFP_ABM_LVL_INFINITY) { 68862306a36Sopenharmony_ci nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n", 68962306a36Sopenharmony_ci opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent, 69062306a36Sopenharmony_ci opt->handle); 69162306a36Sopenharmony_ci return false; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return true; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int 69862306a36Sopenharmony_cinfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink, 69962306a36Sopenharmony_ci struct tc_red_qopt_offload *opt) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_RED, opt->parent, 70562306a36Sopenharmony_ci opt->handle, 1, &qdisc); 70662306a36Sopenharmony_ci if (ret < 0) 70762306a36Sopenharmony_ci return ret; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* If limit != 0 child gets reset */ 71062306a36Sopenharmony_ci if (opt->set.limit) { 71162306a36Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, 0)) 71262306a36Sopenharmony_ci qdisc->children[0]->use_cnt--; 71362306a36Sopenharmony_ci qdisc->children[0] = NULL; 71462306a36Sopenharmony_ci } else { 71562306a36Sopenharmony_ci /* Qdisc was just allocated without a limit will use noop_qdisc, 71662306a36Sopenharmony_ci * i.e. a block hole. 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci if (!ret) 71962306a36Sopenharmony_ci qdisc->children[0] = NFP_QDISC_UNTRACKED; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci qdisc->params_ok = nfp_abm_red_check_params(alink, opt); 72362306a36Sopenharmony_ci if (qdisc->params_ok) { 72462306a36Sopenharmony_ci qdisc->red.num_bands = 1; 72562306a36Sopenharmony_ci qdisc->red.band[0].ecn = opt->set.is_ecn; 72662306a36Sopenharmony_ci qdisc->red.band[0].threshold = opt->set.min; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (qdisc->use_cnt == 1) 73062306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciint nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink, 73662306a36Sopenharmony_ci struct tc_red_qopt_offload *opt) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci switch (opt->command) { 73962306a36Sopenharmony_ci case TC_RED_REPLACE: 74062306a36Sopenharmony_ci return nfp_abm_red_replace(netdev, alink, opt); 74162306a36Sopenharmony_ci case TC_RED_DESTROY: 74262306a36Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci case TC_RED_STATS: 74562306a36Sopenharmony_ci return nfp_abm_red_stats(alink, opt->handle, &opt->stats); 74662306a36Sopenharmony_ci case TC_RED_XSTATS: 74762306a36Sopenharmony_ci return nfp_abm_red_xstats(alink, opt); 74862306a36Sopenharmony_ci case TC_RED_GRAFT: 74962306a36Sopenharmony_ci return nfp_abm_qdisc_graft(alink, opt->handle, 75062306a36Sopenharmony_ci opt->child_handle, 0); 75162306a36Sopenharmony_ci default: 75262306a36Sopenharmony_ci return -EOPNOTSUPP; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int 75762306a36Sopenharmony_cinfp_abm_mq_create(struct net_device *netdev, struct nfp_abm_link *alink, 75862306a36Sopenharmony_ci struct tc_mq_qopt_offload *opt) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct nfp_qdisc *qdisc; 76162306a36Sopenharmony_ci int ret; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_MQ, 76462306a36Sopenharmony_ci TC_H_ROOT, opt->handle, alink->total_queues, 76562306a36Sopenharmony_ci &qdisc); 76662306a36Sopenharmony_ci if (ret < 0) 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci qdisc->params_ok = true; 77062306a36Sopenharmony_ci qdisc->offloaded = true; 77162306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int 77662306a36Sopenharmony_cinfp_abm_mq_stats(struct nfp_abm_link *alink, u32 handle, 77762306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct nfp_qdisc *qdisc, *red; 78062306a36Sopenharmony_ci unsigned int i, j; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 78362306a36Sopenharmony_ci if (!qdisc) 78462306a36Sopenharmony_ci return -EOPNOTSUPP; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci nfp_abm_stats_update(alink); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* MQ stats are summed over the children in the core, so we need 78962306a36Sopenharmony_ci * to add up the unreported child values. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci memset(&qdisc->mq.stats, 0, sizeof(qdisc->mq.stats)); 79262306a36Sopenharmony_ci memset(&qdisc->mq.prev_stats, 0, sizeof(qdisc->mq.prev_stats)); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci for (i = 0; i < qdisc->num_children; i++) { 79562306a36Sopenharmony_ci if (!nfp_abm_qdisc_child_valid(qdisc, i)) 79662306a36Sopenharmony_ci continue; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!nfp_abm_qdisc_is_red(qdisc->children[i])) 79962306a36Sopenharmony_ci continue; 80062306a36Sopenharmony_ci red = qdisc->children[i]; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (j = 0; j < red->red.num_bands; j++) { 80362306a36Sopenharmony_ci nfp_abm_stats_propagate(&qdisc->mq.stats, 80462306a36Sopenharmony_ci &red->red.band[j].stats); 80562306a36Sopenharmony_ci nfp_abm_stats_propagate(&qdisc->mq.prev_stats, 80662306a36Sopenharmony_ci &red->red.band[j].prev_stats); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->mq.stats, &qdisc->mq.prev_stats, 81162306a36Sopenharmony_ci stats->bstats, stats->qstats); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ciint nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink, 81762306a36Sopenharmony_ci struct tc_mq_qopt_offload *opt) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci switch (opt->command) { 82062306a36Sopenharmony_ci case TC_MQ_CREATE: 82162306a36Sopenharmony_ci return nfp_abm_mq_create(netdev, alink, opt); 82262306a36Sopenharmony_ci case TC_MQ_DESTROY: 82362306a36Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 82462306a36Sopenharmony_ci return 0; 82562306a36Sopenharmony_ci case TC_MQ_STATS: 82662306a36Sopenharmony_ci return nfp_abm_mq_stats(alink, opt->handle, &opt->stats); 82762306a36Sopenharmony_ci case TC_MQ_GRAFT: 82862306a36Sopenharmony_ci return nfp_abm_qdisc_graft(alink, opt->handle, 82962306a36Sopenharmony_ci opt->graft_params.child_handle, 83062306a36Sopenharmony_ci opt->graft_params.queue); 83162306a36Sopenharmony_ci default: 83262306a36Sopenharmony_ci return -EOPNOTSUPP; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciint nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink, 83762306a36Sopenharmony_ci struct tc_root_qopt_offload *opt) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci if (opt->ingress) 84062306a36Sopenharmony_ci return -EOPNOTSUPP; 84162306a36Sopenharmony_ci if (alink->root_qdisc) 84262306a36Sopenharmony_ci alink->root_qdisc->use_cnt--; 84362306a36Sopenharmony_ci alink->root_qdisc = nfp_abm_qdisc_find(alink, opt->handle); 84462306a36Sopenharmony_ci if (alink->root_qdisc) 84562306a36Sopenharmony_ci alink->root_qdisc->use_cnt++; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return 0; 85062306a36Sopenharmony_ci} 851