18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* Copyright (C) 2018 Netronome Systems, Inc. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 58c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 68c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 78c2ecf20Sopenharmony_ci#include <net/red.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_cpp.h" 108c2ecf20Sopenharmony_ci#include "../nfp_app.h" 118c2ecf20Sopenharmony_ci#include "../nfp_main.h" 128c2ecf20Sopenharmony_ci#include "../nfp_net.h" 138c2ecf20Sopenharmony_ci#include "../nfp_port.h" 148c2ecf20Sopenharmony_ci#include "main.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic bool nfp_abm_qdisc_is_red(struct nfp_qdisc *qdisc) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci return qdisc->type == NFP_QDISC_RED || qdisc->type == NFP_QDISC_GRED; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic bool nfp_abm_qdisc_child_valid(struct nfp_qdisc *qdisc, unsigned int id) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci return qdisc->children[id] && 248c2ecf20Sopenharmony_ci qdisc->children[id] != NFP_QDISC_UNTRACKED; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void *nfp_abm_qdisc_tree_deref_slot(void __rcu **slot) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci return rtnl_dereference(*slot); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void 338c2ecf20Sopenharmony_cinfp_abm_stats_propagate(struct nfp_alink_stats *parent, 348c2ecf20Sopenharmony_ci struct nfp_alink_stats *child) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci parent->tx_pkts += child->tx_pkts; 378c2ecf20Sopenharmony_ci parent->tx_bytes += child->tx_bytes; 388c2ecf20Sopenharmony_ci parent->backlog_pkts += child->backlog_pkts; 398c2ecf20Sopenharmony_ci parent->backlog_bytes += child->backlog_bytes; 408c2ecf20Sopenharmony_ci parent->overlimits += child->overlimits; 418c2ecf20Sopenharmony_ci parent->drops += child->drops; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void 458c2ecf20Sopenharmony_cinfp_abm_stats_update_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 468c2ecf20Sopenharmony_ci unsigned int queue) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 498c2ecf20Sopenharmony_ci unsigned int i; 508c2ecf20Sopenharmony_ci int err; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!qdisc->offloaded) 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 568c2ecf20Sopenharmony_ci err = nfp_abm_ctrl_read_q_stats(alink, i, queue, 578c2ecf20Sopenharmony_ci &qdisc->red.band[i].stats); 588c2ecf20Sopenharmony_ci if (err) 598c2ecf20Sopenharmony_ci nfp_err(cpp, "RED stats (%d, %d) read failed with error %d\n", 608c2ecf20Sopenharmony_ci i, queue, err); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci err = nfp_abm_ctrl_read_q_xstats(alink, i, queue, 638c2ecf20Sopenharmony_ci &qdisc->red.band[i].xstats); 648c2ecf20Sopenharmony_ci if (err) 658c2ecf20Sopenharmony_ci nfp_err(cpp, "RED xstats (%d, %d) read failed with error %d\n", 668c2ecf20Sopenharmony_ci i, queue, err); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void 718c2ecf20Sopenharmony_cinfp_abm_stats_update_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned int i; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (qdisc->type != NFP_QDISC_MQ) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < alink->total_queues; i++) 798c2ecf20Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, i)) 808c2ecf20Sopenharmony_ci nfp_abm_stats_update_red(alink, qdisc->children[i], i); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void __nfp_abm_stats_update(struct nfp_abm_link *alink, u64 time_now) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci alink->last_stats_update = time_now; 868c2ecf20Sopenharmony_ci if (alink->root_qdisc) 878c2ecf20Sopenharmony_ci nfp_abm_stats_update_mq(alink, alink->root_qdisc); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void nfp_abm_stats_update(struct nfp_abm_link *alink) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci u64 now; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Limit the frequency of updates - stats of non-leaf qdiscs are a sum 958c2ecf20Sopenharmony_ci * of all their leafs, so we would read the same stat multiple times 968c2ecf20Sopenharmony_ci * for every dump. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci now = ktime_get(); 998c2ecf20Sopenharmony_ci if (now - alink->last_stats_update < NFP_ABM_STATS_REFRESH_IVAL) 1008c2ecf20Sopenharmony_ci return; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci __nfp_abm_stats_update(alink, now); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void 1068c2ecf20Sopenharmony_cinfp_abm_qdisc_unlink_children(struct nfp_qdisc *qdisc, 1078c2ecf20Sopenharmony_ci unsigned int start, unsigned int end) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned int i; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for (i = start; i < end; i++) 1128c2ecf20Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, i)) { 1138c2ecf20Sopenharmony_ci qdisc->children[i]->use_cnt--; 1148c2ecf20Sopenharmony_ci qdisc->children[i] = NULL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void 1198c2ecf20Sopenharmony_cinfp_abm_qdisc_offload_stop(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned int i; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Don't complain when qdisc is getting unlinked */ 1248c2ecf20Sopenharmony_ci if (qdisc->use_cnt) 1258c2ecf20Sopenharmony_ci nfp_warn(alink->abm->app->cpp, "Offload of '%08x' stopped\n", 1268c2ecf20Sopenharmony_ci qdisc->handle); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (!nfp_abm_qdisc_is_red(qdisc)) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 1328c2ecf20Sopenharmony_ci qdisc->red.band[i].stats.backlog_pkts = 0; 1338c2ecf20Sopenharmony_ci qdisc->red.band[i].stats.backlog_bytes = 0; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int 1388c2ecf20Sopenharmony_ci__nfp_abm_stats_init(struct nfp_abm_link *alink, unsigned int band, 1398c2ecf20Sopenharmony_ci unsigned int queue, struct nfp_alink_stats *prev_stats, 1408c2ecf20Sopenharmony_ci struct nfp_alink_xstats *prev_xstats) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci u64 backlog_pkts, backlog_bytes; 1438c2ecf20Sopenharmony_ci int err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Don't touch the backlog, backlog can only be reset after it has 1468c2ecf20Sopenharmony_ci * been reported back to the tc qdisc stats. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci backlog_pkts = prev_stats->backlog_pkts; 1498c2ecf20Sopenharmony_ci backlog_bytes = prev_stats->backlog_bytes; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci err = nfp_abm_ctrl_read_q_stats(alink, band, queue, prev_stats); 1528c2ecf20Sopenharmony_ci if (err) { 1538c2ecf20Sopenharmony_ci nfp_err(alink->abm->app->cpp, 1548c2ecf20Sopenharmony_ci "RED stats init (%d, %d) failed with error %d\n", 1558c2ecf20Sopenharmony_ci band, queue, err); 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci err = nfp_abm_ctrl_read_q_xstats(alink, band, queue, prev_xstats); 1608c2ecf20Sopenharmony_ci if (err) { 1618c2ecf20Sopenharmony_ci nfp_err(alink->abm->app->cpp, 1628c2ecf20Sopenharmony_ci "RED xstats init (%d, %d) failed with error %d\n", 1638c2ecf20Sopenharmony_ci band, queue, err); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci prev_stats->backlog_pkts = backlog_pkts; 1688c2ecf20Sopenharmony_ci prev_stats->backlog_bytes = backlog_bytes; 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int 1738c2ecf20Sopenharmony_cinfp_abm_stats_init(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 1748c2ecf20Sopenharmony_ci unsigned int queue) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci unsigned int i; 1778c2ecf20Sopenharmony_ci int err; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 1808c2ecf20Sopenharmony_ci err = __nfp_abm_stats_init(alink, i, queue, 1818c2ecf20Sopenharmony_ci &qdisc->red.band[i].prev_stats, 1828c2ecf20Sopenharmony_ci &qdisc->red.band[i].prev_xstats); 1838c2ecf20Sopenharmony_ci if (err) 1848c2ecf20Sopenharmony_ci return err; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void 1918c2ecf20Sopenharmony_cinfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc, 1928c2ecf20Sopenharmony_ci unsigned int queue) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci bool good_red, good_gred; 1958c2ecf20Sopenharmony_ci unsigned int i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci good_red = qdisc->type == NFP_QDISC_RED && 1988c2ecf20Sopenharmony_ci qdisc->params_ok && 1998c2ecf20Sopenharmony_ci qdisc->use_cnt == 1 && 2008c2ecf20Sopenharmony_ci !alink->has_prio && 2018c2ecf20Sopenharmony_ci !qdisc->children[0]; 2028c2ecf20Sopenharmony_ci good_gred = qdisc->type == NFP_QDISC_GRED && 2038c2ecf20Sopenharmony_ci qdisc->params_ok && 2048c2ecf20Sopenharmony_ci qdisc->use_cnt == 1; 2058c2ecf20Sopenharmony_ci qdisc->offload_mark = good_red || good_gred; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* If we are starting offload init prev_stats */ 2088c2ecf20Sopenharmony_ci if (qdisc->offload_mark && !qdisc->offloaded) 2098c2ecf20Sopenharmony_ci if (nfp_abm_stats_init(alink, qdisc, queue)) 2108c2ecf20Sopenharmony_ci qdisc->offload_mark = false; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!qdisc->offload_mark) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < alink->abm->num_bands; i++) { 2168c2ecf20Sopenharmony_ci enum nfp_abm_q_action act; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci nfp_abm_ctrl_set_q_lvl(alink, i, queue, 2198c2ecf20Sopenharmony_ci qdisc->red.band[i].threshold); 2208c2ecf20Sopenharmony_ci act = qdisc->red.band[i].ecn ? 2218c2ecf20Sopenharmony_ci NFP_ABM_ACT_MARK_DROP : NFP_ABM_ACT_DROP; 2228c2ecf20Sopenharmony_ci nfp_abm_ctrl_set_q_act(alink, i, queue, act); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void 2278c2ecf20Sopenharmony_cinfp_abm_offload_compile_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci unsigned int i; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci qdisc->offload_mark = qdisc->type == NFP_QDISC_MQ; 2328c2ecf20Sopenharmony_ci if (!qdisc->offload_mark) 2338c2ecf20Sopenharmony_ci return; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci for (i = 0; i < alink->total_queues; i++) { 2368c2ecf20Sopenharmony_ci struct nfp_qdisc *child = qdisc->children[i]; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!nfp_abm_qdisc_child_valid(qdisc, i)) 2398c2ecf20Sopenharmony_ci continue; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci nfp_abm_offload_compile_red(alink, child, i); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct nfp_abm *abm = alink->abm; 2488c2ecf20Sopenharmony_ci struct radix_tree_iter iter; 2498c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 2508c2ecf20Sopenharmony_ci void __rcu **slot; 2518c2ecf20Sopenharmony_ci size_t i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Mark all thresholds as unconfigured */ 2548c2ecf20Sopenharmony_ci for (i = 0; i < abm->num_bands; i++) 2558c2ecf20Sopenharmony_ci __bitmap_set(abm->threshold_undef, 2568c2ecf20Sopenharmony_ci i * NFP_NET_MAX_RX_RINGS + alink->queue_base, 2578c2ecf20Sopenharmony_ci alink->total_queues); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Clear offload marks */ 2608c2ecf20Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 2618c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_tree_deref_slot(slot); 2628c2ecf20Sopenharmony_ci qdisc->offload_mark = false; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (alink->root_qdisc) 2668c2ecf20Sopenharmony_ci nfp_abm_offload_compile_mq(alink, alink->root_qdisc); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Refresh offload status */ 2698c2ecf20Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 2708c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_tree_deref_slot(slot); 2718c2ecf20Sopenharmony_ci if (!qdisc->offload_mark && qdisc->offloaded) 2728c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_stop(alink, qdisc); 2738c2ecf20Sopenharmony_ci qdisc->offloaded = qdisc->offload_mark; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Reset the unconfigured thresholds */ 2778c2ecf20Sopenharmony_ci for (i = 0; i < abm->num_thresholds; i++) 2788c2ecf20Sopenharmony_ci if (test_bit(i, abm->threshold_undef)) 2798c2ecf20Sopenharmony_ci __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci __nfp_abm_stats_update(alink, ktime_get()); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void 2858c2ecf20Sopenharmony_cinfp_abm_qdisc_clear_mq(struct net_device *netdev, struct nfp_abm_link *alink, 2868c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct radix_tree_iter iter; 2898c2ecf20Sopenharmony_ci unsigned int mq_refs = 0; 2908c2ecf20Sopenharmony_ci void __rcu **slot; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!qdisc->use_cnt) 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci /* MQ doesn't notify well on destruction, we need special handling of 2958c2ecf20Sopenharmony_ci * MQ's children. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci if (qdisc->type == NFP_QDISC_MQ && 2988c2ecf20Sopenharmony_ci qdisc == alink->root_qdisc && 2998c2ecf20Sopenharmony_ci netdev->reg_state == NETREG_UNREGISTERING) 3008c2ecf20Sopenharmony_ci return; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Count refs held by MQ instances and clear pointers */ 3038c2ecf20Sopenharmony_ci radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) { 3048c2ecf20Sopenharmony_ci struct nfp_qdisc *mq = nfp_abm_qdisc_tree_deref_slot(slot); 3058c2ecf20Sopenharmony_ci unsigned int i; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (mq->type != NFP_QDISC_MQ || mq->netdev != netdev) 3088c2ecf20Sopenharmony_ci continue; 3098c2ecf20Sopenharmony_ci for (i = 0; i < mq->num_children; i++) 3108c2ecf20Sopenharmony_ci if (mq->children[i] == qdisc) { 3118c2ecf20Sopenharmony_ci mq->children[i] = NULL; 3128c2ecf20Sopenharmony_ci mq_refs++; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci WARN(qdisc->use_cnt != mq_refs, "non-zero qdisc use count: %d (- %d)\n", 3178c2ecf20Sopenharmony_ci qdisc->use_cnt, mq_refs); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void 3218c2ecf20Sopenharmony_cinfp_abm_qdisc_free(struct net_device *netdev, struct nfp_abm_link *alink, 3228c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct nfp_port *port = nfp_port_from_netdev(netdev); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!qdisc) 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci nfp_abm_qdisc_clear_mq(netdev, alink, qdisc); 3298c2ecf20Sopenharmony_ci WARN_ON(radix_tree_delete(&alink->qdiscs, 3308c2ecf20Sopenharmony_ci TC_H_MAJ(qdisc->handle)) != qdisc); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci kfree(qdisc->children); 3338c2ecf20Sopenharmony_ci kfree(qdisc); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci port->tc_offload_cnt--; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic struct nfp_qdisc * 3398c2ecf20Sopenharmony_cinfp_abm_qdisc_alloc(struct net_device *netdev, struct nfp_abm_link *alink, 3408c2ecf20Sopenharmony_ci enum nfp_qdisc_type type, u32 parent_handle, u32 handle, 3418c2ecf20Sopenharmony_ci unsigned int children) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct nfp_port *port = nfp_port_from_netdev(netdev); 3448c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 3458c2ecf20Sopenharmony_ci int err; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci qdisc = kzalloc(sizeof(*qdisc), GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!qdisc) 3498c2ecf20Sopenharmony_ci return NULL; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (children) { 3528c2ecf20Sopenharmony_ci qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!qdisc->children) 3548c2ecf20Sopenharmony_ci goto err_free_qdisc; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci qdisc->netdev = netdev; 3588c2ecf20Sopenharmony_ci qdisc->type = type; 3598c2ecf20Sopenharmony_ci qdisc->parent_handle = parent_handle; 3608c2ecf20Sopenharmony_ci qdisc->handle = handle; 3618c2ecf20Sopenharmony_ci qdisc->num_children = children; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci err = radix_tree_insert(&alink->qdiscs, TC_H_MAJ(qdisc->handle), qdisc); 3648c2ecf20Sopenharmony_ci if (err) { 3658c2ecf20Sopenharmony_ci nfp_err(alink->abm->app->cpp, 3668c2ecf20Sopenharmony_ci "Qdisc insertion into radix tree failed: %d\n", err); 3678c2ecf20Sopenharmony_ci goto err_free_child_tbl; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci port->tc_offload_cnt++; 3718c2ecf20Sopenharmony_ci return qdisc; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cierr_free_child_tbl: 3748c2ecf20Sopenharmony_ci kfree(qdisc->children); 3758c2ecf20Sopenharmony_cierr_free_qdisc: 3768c2ecf20Sopenharmony_ci kfree(qdisc); 3778c2ecf20Sopenharmony_ci return NULL; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic struct nfp_qdisc * 3818c2ecf20Sopenharmony_cinfp_abm_qdisc_find(struct nfp_abm_link *alink, u32 handle) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return radix_tree_lookup(&alink->qdiscs, TC_H_MAJ(handle)); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int 3878c2ecf20Sopenharmony_cinfp_abm_qdisc_replace(struct net_device *netdev, struct nfp_abm_link *alink, 3888c2ecf20Sopenharmony_ci enum nfp_qdisc_type type, u32 parent_handle, u32 handle, 3898c2ecf20Sopenharmony_ci unsigned int children, struct nfp_qdisc **qdisc) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci *qdisc = nfp_abm_qdisc_find(alink, handle); 3928c2ecf20Sopenharmony_ci if (*qdisc) { 3938c2ecf20Sopenharmony_ci if (WARN_ON((*qdisc)->type != type)) 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci return 1; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci *qdisc = nfp_abm_qdisc_alloc(netdev, alink, type, parent_handle, handle, 3998c2ecf20Sopenharmony_ci children); 4008c2ecf20Sopenharmony_ci return *qdisc ? 0 : -ENOMEM; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void 4048c2ecf20Sopenharmony_cinfp_abm_qdisc_destroy(struct net_device *netdev, struct nfp_abm_link *alink, 4058c2ecf20Sopenharmony_ci u32 handle) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 4108c2ecf20Sopenharmony_ci if (!qdisc) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* We don't get TC_SETUP_ROOT_QDISC w/ MQ when netdev is unregistered */ 4148c2ecf20Sopenharmony_ci if (alink->root_qdisc == qdisc) 4158c2ecf20Sopenharmony_ci qdisc->use_cnt--; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci nfp_abm_qdisc_unlink_children(qdisc, 0, qdisc->num_children); 4188c2ecf20Sopenharmony_ci nfp_abm_qdisc_free(netdev, alink, qdisc); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (alink->root_qdisc == qdisc) { 4218c2ecf20Sopenharmony_ci alink->root_qdisc = NULL; 4228c2ecf20Sopenharmony_ci /* Only root change matters, other changes are acted upon on 4238c2ecf20Sopenharmony_ci * the graft notification. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int 4308c2ecf20Sopenharmony_cinfp_abm_qdisc_graft(struct nfp_abm_link *alink, u32 handle, u32 child_handle, 4318c2ecf20Sopenharmony_ci unsigned int id) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct nfp_qdisc *parent, *child; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci parent = nfp_abm_qdisc_find(alink, handle); 4368c2ecf20Sopenharmony_ci if (!parent) 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (WARN(id >= parent->num_children, 4408c2ecf20Sopenharmony_ci "graft child out of bound %d >= %d\n", 4418c2ecf20Sopenharmony_ci id, parent->num_children)) 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci nfp_abm_qdisc_unlink_children(parent, id, id + 1); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci child = nfp_abm_qdisc_find(alink, child_handle); 4478c2ecf20Sopenharmony_ci if (child) 4488c2ecf20Sopenharmony_ci child->use_cnt++; 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci child = NFP_QDISC_UNTRACKED; 4518c2ecf20Sopenharmony_ci parent->children[id] = child; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void 4598c2ecf20Sopenharmony_cinfp_abm_stats_calculate(struct nfp_alink_stats *new, 4608c2ecf20Sopenharmony_ci struct nfp_alink_stats *old, 4618c2ecf20Sopenharmony_ci struct gnet_stats_basic_packed *bstats, 4628c2ecf20Sopenharmony_ci struct gnet_stats_queue *qstats) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci _bstats_update(bstats, new->tx_bytes - old->tx_bytes, 4658c2ecf20Sopenharmony_ci new->tx_pkts - old->tx_pkts); 4668c2ecf20Sopenharmony_ci qstats->qlen += new->backlog_pkts - old->backlog_pkts; 4678c2ecf20Sopenharmony_ci qstats->backlog += new->backlog_bytes - old->backlog_bytes; 4688c2ecf20Sopenharmony_ci qstats->overlimits += new->overlimits - old->overlimits; 4698c2ecf20Sopenharmony_ci qstats->drops += new->drops - old->drops; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void 4738c2ecf20Sopenharmony_cinfp_abm_stats_red_calculate(struct nfp_alink_xstats *new, 4748c2ecf20Sopenharmony_ci struct nfp_alink_xstats *old, 4758c2ecf20Sopenharmony_ci struct red_stats *stats) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci stats->forced_mark += new->ecn_marked - old->ecn_marked; 4788c2ecf20Sopenharmony_ci stats->pdrop += new->pdrop - old->pdrop; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int 4828c2ecf20Sopenharmony_cinfp_abm_gred_stats(struct nfp_abm_link *alink, u32 handle, 4838c2ecf20Sopenharmony_ci struct tc_gred_qopt_offload_stats *stats) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 4868c2ecf20Sopenharmony_ci unsigned int i; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci nfp_abm_stats_update(alink); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 4918c2ecf20Sopenharmony_ci if (!qdisc) 4928c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4938c2ecf20Sopenharmony_ci /* If the qdisc offload has stopped we may need to adjust the backlog 4948c2ecf20Sopenharmony_ci * counters back so carry on even if qdisc is not currently offloaded. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 4988c2ecf20Sopenharmony_ci if (!stats->xstats[i]) 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->red.band[i].stats, 5028c2ecf20Sopenharmony_ci &qdisc->red.band[i].prev_stats, 5038c2ecf20Sopenharmony_ci &stats->bstats[i], &stats->qstats[i]); 5048c2ecf20Sopenharmony_ci qdisc->red.band[i].prev_stats = qdisc->red.band[i].stats; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci nfp_abm_stats_red_calculate(&qdisc->red.band[i].xstats, 5078c2ecf20Sopenharmony_ci &qdisc->red.band[i].prev_xstats, 5088c2ecf20Sopenharmony_ci stats->xstats[i]); 5098c2ecf20Sopenharmony_ci qdisc->red.band[i].prev_xstats = qdisc->red.band[i].xstats; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic bool 5168c2ecf20Sopenharmony_cinfp_abm_gred_check_params(struct nfp_abm_link *alink, 5178c2ecf20Sopenharmony_ci struct tc_gred_qopt_offload *opt) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 5208c2ecf20Sopenharmony_ci struct nfp_abm *abm = alink->abm; 5218c2ecf20Sopenharmony_ci unsigned int i; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (opt->set.grio_on || opt->set.wred_on) { 5248c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - GRIO and WRED not supported (p:%08x h:%08x)\n", 5258c2ecf20Sopenharmony_ci opt->parent, opt->handle); 5268c2ecf20Sopenharmony_ci return false; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci if (opt->set.dp_def != alink->def_band) { 5298c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - default band must be %d (p:%08x h:%08x)\n", 5308c2ecf20Sopenharmony_ci alink->def_band, opt->parent, opt->handle); 5318c2ecf20Sopenharmony_ci return false; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci if (opt->set.dp_cnt != abm->num_bands) { 5348c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - band count must be %d (p:%08x h:%08x)\n", 5358c2ecf20Sopenharmony_ci abm->num_bands, opt->parent, opt->handle); 5368c2ecf20Sopenharmony_ci return false; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (i = 0; i < abm->num_bands; i++) { 5408c2ecf20Sopenharmony_ci struct tc_gred_vq_qopt_offload_params *band = &opt->set.tab[i]; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (!band->present) 5438c2ecf20Sopenharmony_ci return false; 5448c2ecf20Sopenharmony_ci if (!band->is_ecn && !nfp_abm_has_drop(abm)) { 5458c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n", 5468c2ecf20Sopenharmony_ci opt->parent, opt->handle, i); 5478c2ecf20Sopenharmony_ci return false; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci if (band->is_ecn && !nfp_abm_has_mark(abm)) { 5508c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - ECN marking not supported (p:%08x h:%08x vq:%d)\n", 5518c2ecf20Sopenharmony_ci opt->parent, opt->handle, i); 5528c2ecf20Sopenharmony_ci return false; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci if (band->is_harddrop) { 5558c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n", 5568c2ecf20Sopenharmony_ci opt->parent, opt->handle, i); 5578c2ecf20Sopenharmony_ci return false; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci if (band->min != band->max) { 5608c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - threshold mismatch (p:%08x h:%08x vq:%d)\n", 5618c2ecf20Sopenharmony_ci opt->parent, opt->handle, i); 5628c2ecf20Sopenharmony_ci return false; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci if (band->min > S32_MAX) { 5658c2ecf20Sopenharmony_ci nfp_warn(cpp, "GRED offload failed - threshold too large %d > %d (p:%08x h:%08x vq:%d)\n", 5668c2ecf20Sopenharmony_ci band->min, S32_MAX, opt->parent, opt->handle, 5678c2ecf20Sopenharmony_ci i); 5688c2ecf20Sopenharmony_ci return false; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return true; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int 5768c2ecf20Sopenharmony_cinfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink, 5778c2ecf20Sopenharmony_ci struct tc_gred_qopt_offload *opt) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 5808c2ecf20Sopenharmony_ci unsigned int i; 5818c2ecf20Sopenharmony_ci int ret; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_GRED, opt->parent, 5848c2ecf20Sopenharmony_ci opt->handle, 0, &qdisc); 5858c2ecf20Sopenharmony_ci if (ret < 0) 5868c2ecf20Sopenharmony_ci return ret; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci qdisc->params_ok = nfp_abm_gred_check_params(alink, opt); 5898c2ecf20Sopenharmony_ci if (qdisc->params_ok) { 5908c2ecf20Sopenharmony_ci qdisc->red.num_bands = opt->set.dp_cnt; 5918c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->red.num_bands; i++) { 5928c2ecf20Sopenharmony_ci qdisc->red.band[i].ecn = opt->set.tab[i].is_ecn; 5938c2ecf20Sopenharmony_ci qdisc->red.band[i].threshold = opt->set.tab[i].min; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (qdisc->use_cnt) 5988c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciint nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink, 6048c2ecf20Sopenharmony_ci struct tc_gred_qopt_offload *opt) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci switch (opt->command) { 6078c2ecf20Sopenharmony_ci case TC_GRED_REPLACE: 6088c2ecf20Sopenharmony_ci return nfp_abm_gred_replace(netdev, alink, opt); 6098c2ecf20Sopenharmony_ci case TC_GRED_DESTROY: 6108c2ecf20Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci case TC_GRED_STATS: 6138c2ecf20Sopenharmony_ci return nfp_abm_gred_stats(alink, opt->handle, &opt->stats); 6148c2ecf20Sopenharmony_ci default: 6158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int 6208c2ecf20Sopenharmony_cinfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci nfp_abm_stats_update(alink); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, opt->handle); 6278c2ecf20Sopenharmony_ci if (!qdisc || !qdisc->offloaded) 6288c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci nfp_abm_stats_red_calculate(&qdisc->red.band[0].xstats, 6318c2ecf20Sopenharmony_ci &qdisc->red.band[0].prev_xstats, 6328c2ecf20Sopenharmony_ci opt->xstats); 6338c2ecf20Sopenharmony_ci qdisc->red.band[0].prev_xstats = qdisc->red.band[0].xstats; 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic int 6388c2ecf20Sopenharmony_cinfp_abm_red_stats(struct nfp_abm_link *alink, u32 handle, 6398c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci nfp_abm_stats_update(alink); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 6468c2ecf20Sopenharmony_ci if (!qdisc) 6478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6488c2ecf20Sopenharmony_ci /* If the qdisc offload has stopped we may need to adjust the backlog 6498c2ecf20Sopenharmony_ci * counters back so carry on even if qdisc is not currently offloaded. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->red.band[0].stats, 6538c2ecf20Sopenharmony_ci &qdisc->red.band[0].prev_stats, 6548c2ecf20Sopenharmony_ci stats->bstats, stats->qstats); 6558c2ecf20Sopenharmony_ci qdisc->red.band[0].prev_stats = qdisc->red.band[0].stats; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic bool 6618c2ecf20Sopenharmony_cinfp_abm_red_check_params(struct nfp_abm_link *alink, 6628c2ecf20Sopenharmony_ci struct tc_red_qopt_offload *opt) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct nfp_cpp *cpp = alink->abm->app->cpp; 6658c2ecf20Sopenharmony_ci struct nfp_abm *abm = alink->abm; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (!opt->set.is_ecn && !nfp_abm_has_drop(abm)) { 6688c2ecf20Sopenharmony_ci nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n", 6698c2ecf20Sopenharmony_ci opt->parent, opt->handle); 6708c2ecf20Sopenharmony_ci return false; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci if (opt->set.is_ecn && !nfp_abm_has_mark(abm)) { 6738c2ecf20Sopenharmony_ci nfp_warn(cpp, "RED offload failed - ECN marking not supported (p:%08x h:%08x)\n", 6748c2ecf20Sopenharmony_ci opt->parent, opt->handle); 6758c2ecf20Sopenharmony_ci return false; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci if (opt->set.is_harddrop) { 6788c2ecf20Sopenharmony_ci nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n", 6798c2ecf20Sopenharmony_ci opt->parent, opt->handle); 6808c2ecf20Sopenharmony_ci return false; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci if (opt->set.min != opt->set.max) { 6838c2ecf20Sopenharmony_ci nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n", 6848c2ecf20Sopenharmony_ci opt->parent, opt->handle); 6858c2ecf20Sopenharmony_ci return false; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci if (opt->set.min > NFP_ABM_LVL_INFINITY) { 6888c2ecf20Sopenharmony_ci nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n", 6898c2ecf20Sopenharmony_ci opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent, 6908c2ecf20Sopenharmony_ci opt->handle); 6918c2ecf20Sopenharmony_ci return false; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return true; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int 6988c2ecf20Sopenharmony_cinfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink, 6998c2ecf20Sopenharmony_ci struct tc_red_qopt_offload *opt) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 7028c2ecf20Sopenharmony_ci int ret; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_RED, opt->parent, 7058c2ecf20Sopenharmony_ci opt->handle, 1, &qdisc); 7068c2ecf20Sopenharmony_ci if (ret < 0) 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* If limit != 0 child gets reset */ 7108c2ecf20Sopenharmony_ci if (opt->set.limit) { 7118c2ecf20Sopenharmony_ci if (nfp_abm_qdisc_child_valid(qdisc, 0)) 7128c2ecf20Sopenharmony_ci qdisc->children[0]->use_cnt--; 7138c2ecf20Sopenharmony_ci qdisc->children[0] = NULL; 7148c2ecf20Sopenharmony_ci } else { 7158c2ecf20Sopenharmony_ci /* Qdisc was just allocated without a limit will use noop_qdisc, 7168c2ecf20Sopenharmony_ci * i.e. a block hole. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci if (!ret) 7198c2ecf20Sopenharmony_ci qdisc->children[0] = NFP_QDISC_UNTRACKED; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci qdisc->params_ok = nfp_abm_red_check_params(alink, opt); 7238c2ecf20Sopenharmony_ci if (qdisc->params_ok) { 7248c2ecf20Sopenharmony_ci qdisc->red.num_bands = 1; 7258c2ecf20Sopenharmony_ci qdisc->red.band[0].ecn = opt->set.is_ecn; 7268c2ecf20Sopenharmony_ci qdisc->red.band[0].threshold = opt->set.min; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (qdisc->use_cnt == 1) 7308c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ciint nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink, 7368c2ecf20Sopenharmony_ci struct tc_red_qopt_offload *opt) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci switch (opt->command) { 7398c2ecf20Sopenharmony_ci case TC_RED_REPLACE: 7408c2ecf20Sopenharmony_ci return nfp_abm_red_replace(netdev, alink, opt); 7418c2ecf20Sopenharmony_ci case TC_RED_DESTROY: 7428c2ecf20Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 7438c2ecf20Sopenharmony_ci return 0; 7448c2ecf20Sopenharmony_ci case TC_RED_STATS: 7458c2ecf20Sopenharmony_ci return nfp_abm_red_stats(alink, opt->handle, &opt->stats); 7468c2ecf20Sopenharmony_ci case TC_RED_XSTATS: 7478c2ecf20Sopenharmony_ci return nfp_abm_red_xstats(alink, opt); 7488c2ecf20Sopenharmony_ci case TC_RED_GRAFT: 7498c2ecf20Sopenharmony_ci return nfp_abm_qdisc_graft(alink, opt->handle, 7508c2ecf20Sopenharmony_ci opt->child_handle, 0); 7518c2ecf20Sopenharmony_ci default: 7528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int 7578c2ecf20Sopenharmony_cinfp_abm_mq_create(struct net_device *netdev, struct nfp_abm_link *alink, 7588c2ecf20Sopenharmony_ci struct tc_mq_qopt_offload *opt) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc; 7618c2ecf20Sopenharmony_ci int ret; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_MQ, 7648c2ecf20Sopenharmony_ci TC_H_ROOT, opt->handle, alink->total_queues, 7658c2ecf20Sopenharmony_ci &qdisc); 7668c2ecf20Sopenharmony_ci if (ret < 0) 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci qdisc->params_ok = true; 7708c2ecf20Sopenharmony_ci qdisc->offloaded = true; 7718c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int 7768c2ecf20Sopenharmony_cinfp_abm_mq_stats(struct nfp_abm_link *alink, u32 handle, 7778c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct nfp_qdisc *qdisc, *red; 7808c2ecf20Sopenharmony_ci unsigned int i, j; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci qdisc = nfp_abm_qdisc_find(alink, handle); 7838c2ecf20Sopenharmony_ci if (!qdisc) 7848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci nfp_abm_stats_update(alink); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* MQ stats are summed over the children in the core, so we need 7898c2ecf20Sopenharmony_ci * to add up the unreported child values. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci memset(&qdisc->mq.stats, 0, sizeof(qdisc->mq.stats)); 7928c2ecf20Sopenharmony_ci memset(&qdisc->mq.prev_stats, 0, sizeof(qdisc->mq.prev_stats)); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci for (i = 0; i < qdisc->num_children; i++) { 7958c2ecf20Sopenharmony_ci if (!nfp_abm_qdisc_child_valid(qdisc, i)) 7968c2ecf20Sopenharmony_ci continue; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (!nfp_abm_qdisc_is_red(qdisc->children[i])) 7998c2ecf20Sopenharmony_ci continue; 8008c2ecf20Sopenharmony_ci red = qdisc->children[i]; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (j = 0; j < red->red.num_bands; j++) { 8038c2ecf20Sopenharmony_ci nfp_abm_stats_propagate(&qdisc->mq.stats, 8048c2ecf20Sopenharmony_ci &red->red.band[j].stats); 8058c2ecf20Sopenharmony_ci nfp_abm_stats_propagate(&qdisc->mq.prev_stats, 8068c2ecf20Sopenharmony_ci &red->red.band[j].prev_stats); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci nfp_abm_stats_calculate(&qdisc->mq.stats, &qdisc->mq.prev_stats, 8118c2ecf20Sopenharmony_ci stats->bstats, stats->qstats); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci return qdisc->offloaded ? 0 : -EOPNOTSUPP; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ciint nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink, 8178c2ecf20Sopenharmony_ci struct tc_mq_qopt_offload *opt) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci switch (opt->command) { 8208c2ecf20Sopenharmony_ci case TC_MQ_CREATE: 8218c2ecf20Sopenharmony_ci return nfp_abm_mq_create(netdev, alink, opt); 8228c2ecf20Sopenharmony_ci case TC_MQ_DESTROY: 8238c2ecf20Sopenharmony_ci nfp_abm_qdisc_destroy(netdev, alink, opt->handle); 8248c2ecf20Sopenharmony_ci return 0; 8258c2ecf20Sopenharmony_ci case TC_MQ_STATS: 8268c2ecf20Sopenharmony_ci return nfp_abm_mq_stats(alink, opt->handle, &opt->stats); 8278c2ecf20Sopenharmony_ci case TC_MQ_GRAFT: 8288c2ecf20Sopenharmony_ci return nfp_abm_qdisc_graft(alink, opt->handle, 8298c2ecf20Sopenharmony_ci opt->graft_params.child_handle, 8308c2ecf20Sopenharmony_ci opt->graft_params.queue); 8318c2ecf20Sopenharmony_ci default: 8328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ciint nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink, 8378c2ecf20Sopenharmony_ci struct tc_root_qopt_offload *opt) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci if (opt->ingress) 8408c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8418c2ecf20Sopenharmony_ci if (alink->root_qdisc) 8428c2ecf20Sopenharmony_ci alink->root_qdisc->use_cnt--; 8438c2ecf20Sopenharmony_ci alink->root_qdisc = nfp_abm_qdisc_find(alink, opt->handle); 8448c2ecf20Sopenharmony_ci if (alink->root_qdisc) 8458c2ecf20Sopenharmony_ci alink->root_qdisc->use_cnt++; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci nfp_abm_qdisc_offload_update(alink); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci} 851