18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_red.c Random Early Detection queue. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Changes: 88c2ecf20Sopenharmony_ci * J Hadi Salim 980914: computation fixes 98c2ecf20Sopenharmony_ci * Alexey Makarenko <makar@phoenix.kharkov.ua> 990814: qave on idle link was calculated incorrectly. 108c2ecf20Sopenharmony_ci * J Hadi Salim 980816: ECN support 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 188c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 198c2ecf20Sopenharmony_ci#include <net/inet_ecn.h> 208c2ecf20Sopenharmony_ci#include <net/red.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Parameters, settable by user: 248c2ecf20Sopenharmony_ci ----------------------------- 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci limit - bytes (must be > qth_max + burst) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci Hard limit on queue length, should be chosen >qth_max 298c2ecf20Sopenharmony_ci to allow packet bursts. This parameter does not 308c2ecf20Sopenharmony_ci affect the algorithms behaviour and can be chosen 318c2ecf20Sopenharmony_ci arbitrarily high (well, less than ram size) 328c2ecf20Sopenharmony_ci Really, this limit will never be reached 338c2ecf20Sopenharmony_ci if RED works correctly. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct red_sched_data { 378c2ecf20Sopenharmony_ci u32 limit; /* HARD maximal queue length */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci unsigned char flags; 408c2ecf20Sopenharmony_ci /* Non-flags in tc_red_qopt.flags. */ 418c2ecf20Sopenharmony_ci unsigned char userbits; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci struct timer_list adapt_timer; 448c2ecf20Sopenharmony_ci struct Qdisc *sch; 458c2ecf20Sopenharmony_ci struct red_parms parms; 468c2ecf20Sopenharmony_ci struct red_vars vars; 478c2ecf20Sopenharmony_ci struct red_stats stats; 488c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 498c2ecf20Sopenharmony_ci struct tcf_qevent qe_early_drop; 508c2ecf20Sopenharmony_ci struct tcf_qevent qe_mark; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define TC_RED_SUPPORTED_FLAGS (TC_RED_HISTORIC_FLAGS | TC_RED_NODROP) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline int red_use_ecn(struct red_sched_data *q) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return q->flags & TC_RED_ECN; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline int red_use_harddrop(struct red_sched_data *q) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return q->flags & TC_RED_HARDDROP; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int red_use_nodrop(struct red_sched_data *q) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return q->flags & TC_RED_NODROP; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, 718c2ecf20Sopenharmony_ci struct sk_buff **to_free) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 748c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 758c2ecf20Sopenharmony_ci unsigned int len; 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci q->vars.qavg = red_calc_qavg(&q->parms, 798c2ecf20Sopenharmony_ci &q->vars, 808c2ecf20Sopenharmony_ci child->qstats.backlog); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (red_is_idling(&q->vars)) 838c2ecf20Sopenharmony_ci red_end_of_idle_period(&q->vars); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (red_action(&q->parms, &q->vars, q->vars.qavg)) { 868c2ecf20Sopenharmony_ci case RED_DONT_MARK: 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci case RED_PROB_MARK: 908c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 918c2ecf20Sopenharmony_ci if (!red_use_ecn(q)) { 928c2ecf20Sopenharmony_ci q->stats.prob_drop++; 938c2ecf20Sopenharmony_ci goto congestion_drop; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (INET_ECN_set_ce(skb)) { 978c2ecf20Sopenharmony_ci q->stats.prob_mark++; 988c2ecf20Sopenharmony_ci skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); 998c2ecf20Sopenharmony_ci if (!skb) 1008c2ecf20Sopenharmony_ci return NET_XMIT_CN | ret; 1018c2ecf20Sopenharmony_ci } else if (!red_use_nodrop(q)) { 1028c2ecf20Sopenharmony_ci q->stats.prob_drop++; 1038c2ecf20Sopenharmony_ci goto congestion_drop; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Non-ECT packet in ECN nodrop mode: queue it. */ 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci case RED_HARD_MARK: 1108c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 1118c2ecf20Sopenharmony_ci if (red_use_harddrop(q) || !red_use_ecn(q)) { 1128c2ecf20Sopenharmony_ci q->stats.forced_drop++; 1138c2ecf20Sopenharmony_ci goto congestion_drop; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (INET_ECN_set_ce(skb)) { 1178c2ecf20Sopenharmony_ci q->stats.forced_mark++; 1188c2ecf20Sopenharmony_ci skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); 1198c2ecf20Sopenharmony_ci if (!skb) 1208c2ecf20Sopenharmony_ci return NET_XMIT_CN | ret; 1218c2ecf20Sopenharmony_ci } else if (!red_use_nodrop(q)) { 1228c2ecf20Sopenharmony_ci q->stats.forced_drop++; 1238c2ecf20Sopenharmony_ci goto congestion_drop; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Non-ECT packet in ECN nodrop mode: queue it. */ 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci len = qdisc_pkt_len(skb); 1318c2ecf20Sopenharmony_ci ret = qdisc_enqueue(skb, child, to_free); 1328c2ecf20Sopenharmony_ci if (likely(ret == NET_XMIT_SUCCESS)) { 1338c2ecf20Sopenharmony_ci sch->qstats.backlog += len; 1348c2ecf20Sopenharmony_ci sch->q.qlen++; 1358c2ecf20Sopenharmony_ci } else if (net_xmit_drop_count(ret)) { 1368c2ecf20Sopenharmony_ci q->stats.pdrop++; 1378c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cicongestion_drop: 1428c2ecf20Sopenharmony_ci skb = tcf_qevent_handle(&q->qe_early_drop, sch, skb, to_free, &ret); 1438c2ecf20Sopenharmony_ci if (!skb) 1448c2ecf20Sopenharmony_ci return NET_XMIT_CN | ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci qdisc_drop(skb, sch, to_free); 1478c2ecf20Sopenharmony_ci return NET_XMIT_CN; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct sk_buff *red_dequeue(struct Qdisc *sch) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct sk_buff *skb; 1538c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 1548c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci skb = child->dequeue(child); 1578c2ecf20Sopenharmony_ci if (skb) { 1588c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 1598c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 1608c2ecf20Sopenharmony_ci sch->q.qlen--; 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci if (!red_is_idling(&q->vars)) 1638c2ecf20Sopenharmony_ci red_start_of_idle_period(&q->vars); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci return skb; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic struct sk_buff *red_peek(struct Qdisc *sch) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 1718c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return child->ops->peek(child); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void red_reset(struct Qdisc *sch) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci qdisc_reset(q->qdisc); 1818c2ecf20Sopenharmony_ci red_restart(&q->vars); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int red_offload(struct Qdisc *sch, bool enable) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 1878c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 1888c2ecf20Sopenharmony_ci struct tc_red_qopt_offload opt = { 1898c2ecf20Sopenharmony_ci .handle = sch->handle, 1908c2ecf20Sopenharmony_ci .parent = sch->parent, 1918c2ecf20Sopenharmony_ci }; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 1948c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (enable) { 1978c2ecf20Sopenharmony_ci opt.command = TC_RED_REPLACE; 1988c2ecf20Sopenharmony_ci opt.set.min = q->parms.qth_min >> q->parms.Wlog; 1998c2ecf20Sopenharmony_ci opt.set.max = q->parms.qth_max >> q->parms.Wlog; 2008c2ecf20Sopenharmony_ci opt.set.probability = q->parms.max_P; 2018c2ecf20Sopenharmony_ci opt.set.limit = q->limit; 2028c2ecf20Sopenharmony_ci opt.set.is_ecn = red_use_ecn(q); 2038c2ecf20Sopenharmony_ci opt.set.is_harddrop = red_use_harddrop(q); 2048c2ecf20Sopenharmony_ci opt.set.is_nodrop = red_use_nodrop(q); 2058c2ecf20Sopenharmony_ci opt.set.qstats = &sch->qstats; 2068c2ecf20Sopenharmony_ci } else { 2078c2ecf20Sopenharmony_ci opt.command = TC_RED_DESTROY; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void red_destroy(struct Qdisc *sch) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci tcf_qevent_destroy(&q->qe_mark, sch); 2188c2ecf20Sopenharmony_ci tcf_qevent_destroy(&q->qe_early_drop, sch); 2198c2ecf20Sopenharmony_ci del_timer_sync(&q->adapt_timer); 2208c2ecf20Sopenharmony_ci red_offload(sch, false); 2218c2ecf20Sopenharmony_ci qdisc_put(q->qdisc); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic const struct nla_policy red_policy[TCA_RED_MAX + 1] = { 2258c2ecf20Sopenharmony_ci [TCA_RED_UNSPEC] = { .strict_start_type = TCA_RED_FLAGS }, 2268c2ecf20Sopenharmony_ci [TCA_RED_PARMS] = { .len = sizeof(struct tc_red_qopt) }, 2278c2ecf20Sopenharmony_ci [TCA_RED_STAB] = { .len = RED_STAB_SIZE }, 2288c2ecf20Sopenharmony_ci [TCA_RED_MAX_P] = { .type = NLA_U32 }, 2298c2ecf20Sopenharmony_ci [TCA_RED_FLAGS] = NLA_POLICY_BITFIELD32(TC_RED_SUPPORTED_FLAGS), 2308c2ecf20Sopenharmony_ci [TCA_RED_EARLY_DROP_BLOCK] = { .type = NLA_U32 }, 2318c2ecf20Sopenharmony_ci [TCA_RED_MARK_BLOCK] = { .type = NLA_U32 }, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int __red_change(struct Qdisc *sch, struct nlattr **tb, 2358c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct Qdisc *old_child = NULL, *child = NULL; 2388c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 2398c2ecf20Sopenharmony_ci struct nla_bitfield32 flags_bf; 2408c2ecf20Sopenharmony_ci struct tc_red_qopt *ctl; 2418c2ecf20Sopenharmony_ci unsigned char userbits; 2428c2ecf20Sopenharmony_ci unsigned char flags; 2438c2ecf20Sopenharmony_ci int err; 2448c2ecf20Sopenharmony_ci u32 max_P; 2458c2ecf20Sopenharmony_ci u8 *stab; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (tb[TCA_RED_PARMS] == NULL || 2488c2ecf20Sopenharmony_ci tb[TCA_RED_STAB] == NULL) 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ctl = nla_data(tb[TCA_RED_PARMS]); 2548c2ecf20Sopenharmony_ci stab = nla_data(tb[TCA_RED_STAB]); 2558c2ecf20Sopenharmony_ci if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, 2568c2ecf20Sopenharmony_ci ctl->Scell_log, stab)) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci err = red_get_flags(ctl->flags, TC_RED_HISTORIC_FLAGS, 2608c2ecf20Sopenharmony_ci tb[TCA_RED_FLAGS], TC_RED_SUPPORTED_FLAGS, 2618c2ecf20Sopenharmony_ci &flags_bf, &userbits, extack); 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (ctl->limit > 0) { 2668c2ecf20Sopenharmony_ci child = fifo_create_dflt(sch, &bfifo_qdisc_ops, ctl->limit, 2678c2ecf20Sopenharmony_ci extack); 2688c2ecf20Sopenharmony_ci if (IS_ERR(child)) 2698c2ecf20Sopenharmony_ci return PTR_ERR(child); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* child is fifo, no need to check for noop_qdisc */ 2728c2ecf20Sopenharmony_ci qdisc_hash_add(child, true); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci sch_tree_lock(sch); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci flags = (q->flags & ~flags_bf.selector) | flags_bf.value; 2788c2ecf20Sopenharmony_ci err = red_validate_flags(flags, extack); 2798c2ecf20Sopenharmony_ci if (err) 2808c2ecf20Sopenharmony_ci goto unlock_out; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci q->flags = flags; 2838c2ecf20Sopenharmony_ci q->userbits = userbits; 2848c2ecf20Sopenharmony_ci q->limit = ctl->limit; 2858c2ecf20Sopenharmony_ci if (child) { 2868c2ecf20Sopenharmony_ci qdisc_tree_flush_backlog(q->qdisc); 2878c2ecf20Sopenharmony_ci old_child = q->qdisc; 2888c2ecf20Sopenharmony_ci q->qdisc = child; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci red_set_parms(&q->parms, 2928c2ecf20Sopenharmony_ci ctl->qth_min, ctl->qth_max, ctl->Wlog, 2938c2ecf20Sopenharmony_ci ctl->Plog, ctl->Scell_log, 2948c2ecf20Sopenharmony_ci stab, 2958c2ecf20Sopenharmony_ci max_P); 2968c2ecf20Sopenharmony_ci red_set_vars(&q->vars); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci del_timer(&q->adapt_timer); 2998c2ecf20Sopenharmony_ci if (ctl->flags & TC_RED_ADAPTATIVE) 3008c2ecf20Sopenharmony_ci mod_timer(&q->adapt_timer, jiffies + HZ/2); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!q->qdisc->q.qlen) 3038c2ecf20Sopenharmony_ci red_start_of_idle_period(&q->vars); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci red_offload(sch, true); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (old_child) 3108c2ecf20Sopenharmony_ci qdisc_put(old_child); 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ciunlock_out: 3148c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 3158c2ecf20Sopenharmony_ci if (child) 3168c2ecf20Sopenharmony_ci qdisc_put(child); 3178c2ecf20Sopenharmony_ci return err; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic inline void red_adaptative_timer(struct timer_list *t) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct red_sched_data *q = from_timer(q, t, adapt_timer); 3238c2ecf20Sopenharmony_ci struct Qdisc *sch = q->sch; 3248c2ecf20Sopenharmony_ci spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci spin_lock(root_lock); 3278c2ecf20Sopenharmony_ci red_adaptative_algo(&q->parms, &q->vars); 3288c2ecf20Sopenharmony_ci mod_timer(&q->adapt_timer, jiffies + HZ/2); 3298c2ecf20Sopenharmony_ci spin_unlock(root_lock); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int red_init(struct Qdisc *sch, struct nlattr *opt, 3338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 3368c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_RED_MAX + 1]; 3378c2ecf20Sopenharmony_ci int err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci q->qdisc = &noop_qdisc; 3408c2ecf20Sopenharmony_ci q->sch = sch; 3418c2ecf20Sopenharmony_ci timer_setup(&q->adapt_timer, red_adaptative_timer, 0); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!opt) 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_RED_MAX, opt, red_policy, 3478c2ecf20Sopenharmony_ci extack); 3488c2ecf20Sopenharmony_ci if (err < 0) 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = __red_change(sch, tb, extack); 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci err = tcf_qevent_init(&q->qe_early_drop, sch, 3568c2ecf20Sopenharmony_ci FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP, 3578c2ecf20Sopenharmony_ci tb[TCA_RED_EARLY_DROP_BLOCK], extack); 3588c2ecf20Sopenharmony_ci if (err) 3598c2ecf20Sopenharmony_ci return err; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return tcf_qevent_init(&q->qe_mark, sch, 3628c2ecf20Sopenharmony_ci FLOW_BLOCK_BINDER_TYPE_RED_MARK, 3638c2ecf20Sopenharmony_ci tb[TCA_RED_MARK_BLOCK], extack); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int red_change(struct Qdisc *sch, struct nlattr *opt, 3678c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 3708c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_RED_MAX + 1]; 3718c2ecf20Sopenharmony_ci int err; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!opt) 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_RED_MAX, opt, red_policy, 3778c2ecf20Sopenharmony_ci extack); 3788c2ecf20Sopenharmony_ci if (err < 0) 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci err = tcf_qevent_validate_change(&q->qe_early_drop, 3828c2ecf20Sopenharmony_ci tb[TCA_RED_EARLY_DROP_BLOCK], extack); 3838c2ecf20Sopenharmony_ci if (err) 3848c2ecf20Sopenharmony_ci return err; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci err = tcf_qevent_validate_change(&q->qe_mark, 3878c2ecf20Sopenharmony_ci tb[TCA_RED_MARK_BLOCK], extack); 3888c2ecf20Sopenharmony_ci if (err) 3898c2ecf20Sopenharmony_ci return err; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return __red_change(sch, tb, extack); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int red_dump_offload_stats(struct Qdisc *sch) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct tc_red_qopt_offload hw_stats = { 3978c2ecf20Sopenharmony_ci .command = TC_RED_STATS, 3988c2ecf20Sopenharmony_ci .handle = sch->handle, 3998c2ecf20Sopenharmony_ci .parent = sch->parent, 4008c2ecf20Sopenharmony_ci { 4018c2ecf20Sopenharmony_ci .stats.bstats = &sch->bstats, 4028c2ecf20Sopenharmony_ci .stats.qstats = &sch->qstats, 4038c2ecf20Sopenharmony_ci }, 4048c2ecf20Sopenharmony_ci }; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_RED, &hw_stats); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int red_dump(struct Qdisc *sch, struct sk_buff *skb) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 4128c2ecf20Sopenharmony_ci struct nlattr *opts = NULL; 4138c2ecf20Sopenharmony_ci struct tc_red_qopt opt = { 4148c2ecf20Sopenharmony_ci .limit = q->limit, 4158c2ecf20Sopenharmony_ci .flags = (q->flags & TC_RED_HISTORIC_FLAGS) | 4168c2ecf20Sopenharmony_ci q->userbits, 4178c2ecf20Sopenharmony_ci .qth_min = q->parms.qth_min >> q->parms.Wlog, 4188c2ecf20Sopenharmony_ci .qth_max = q->parms.qth_max >> q->parms.Wlog, 4198c2ecf20Sopenharmony_ci .Wlog = q->parms.Wlog, 4208c2ecf20Sopenharmony_ci .Plog = q->parms.Plog, 4218c2ecf20Sopenharmony_ci .Scell_log = q->parms.Scell_log, 4228c2ecf20Sopenharmony_ci }; 4238c2ecf20Sopenharmony_ci int err; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci err = red_dump_offload_stats(sch); 4268c2ecf20Sopenharmony_ci if (err) 4278c2ecf20Sopenharmony_ci goto nla_put_failure; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci opts = nla_nest_start_noflag(skb, TCA_OPTIONS); 4308c2ecf20Sopenharmony_ci if (opts == NULL) 4318c2ecf20Sopenharmony_ci goto nla_put_failure; 4328c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_RED_PARMS, sizeof(opt), &opt) || 4338c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_RED_MAX_P, q->parms.max_P) || 4348c2ecf20Sopenharmony_ci nla_put_bitfield32(skb, TCA_RED_FLAGS, 4358c2ecf20Sopenharmony_ci q->flags, TC_RED_SUPPORTED_FLAGS) || 4368c2ecf20Sopenharmony_ci tcf_qevent_dump(skb, TCA_RED_MARK_BLOCK, &q->qe_mark) || 4378c2ecf20Sopenharmony_ci tcf_qevent_dump(skb, TCA_RED_EARLY_DROP_BLOCK, &q->qe_early_drop)) 4388c2ecf20Sopenharmony_ci goto nla_put_failure; 4398c2ecf20Sopenharmony_ci return nla_nest_end(skb, opts); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cinla_put_failure: 4428c2ecf20Sopenharmony_ci nla_nest_cancel(skb, opts); 4438c2ecf20Sopenharmony_ci return -EMSGSIZE; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 4498c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 4508c2ecf20Sopenharmony_ci struct tc_red_xstats st = {0}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (sch->flags & TCQ_F_OFFLOADED) { 4538c2ecf20Sopenharmony_ci struct tc_red_qopt_offload hw_stats_request = { 4548c2ecf20Sopenharmony_ci .command = TC_RED_XSTATS, 4558c2ecf20Sopenharmony_ci .handle = sch->handle, 4568c2ecf20Sopenharmony_ci .parent = sch->parent, 4578c2ecf20Sopenharmony_ci { 4588c2ecf20Sopenharmony_ci .xstats = &q->stats, 4598c2ecf20Sopenharmony_ci }, 4608c2ecf20Sopenharmony_ci }; 4618c2ecf20Sopenharmony_ci dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, 4628c2ecf20Sopenharmony_ci &hw_stats_request); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci st.early = q->stats.prob_drop + q->stats.forced_drop; 4658c2ecf20Sopenharmony_ci st.pdrop = q->stats.pdrop; 4668c2ecf20Sopenharmony_ci st.other = q->stats.other; 4678c2ecf20Sopenharmony_ci st.marked = q->stats.prob_mark + q->stats.forced_mark; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &st, sizeof(st)); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int red_dump_class(struct Qdisc *sch, unsigned long cl, 4738c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci tcm->tcm_handle |= TC_H_MIN(1); 4788c2ecf20Sopenharmony_ci tcm->tcm_info = q->qdisc->handle; 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void red_graft_offload(struct Qdisc *sch, 4838c2ecf20Sopenharmony_ci struct Qdisc *new, struct Qdisc *old, 4848c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct tc_red_qopt_offload graft_offload = { 4878c2ecf20Sopenharmony_ci .handle = sch->handle, 4888c2ecf20Sopenharmony_ci .parent = sch->parent, 4898c2ecf20Sopenharmony_ci .child_handle = new->handle, 4908c2ecf20Sopenharmony_ci .command = TC_RED_GRAFT, 4918c2ecf20Sopenharmony_ci }; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old, 4948c2ecf20Sopenharmony_ci TC_SETUP_QDISC_RED, &graft_offload, extack); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 4988c2ecf20Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (new == NULL) 5038c2ecf20Sopenharmony_ci new = &noop_qdisc; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &q->qdisc); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci red_graft_offload(sch, new, *old, extack); 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct red_sched_data *q = qdisc_priv(sch); 5148c2ecf20Sopenharmony_ci return q->qdisc; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic unsigned long red_find(struct Qdisc *sch, u32 classid) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return 1; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci if (!walker->stop) { 5258c2ecf20Sopenharmony_ci if (walker->count >= walker->skip) 5268c2ecf20Sopenharmony_ci if (walker->fn(sch, 1, walker) < 0) { 5278c2ecf20Sopenharmony_ci walker->stop = 1; 5288c2ecf20Sopenharmony_ci return; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci walker->count++; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops red_class_ops = { 5358c2ecf20Sopenharmony_ci .graft = red_graft, 5368c2ecf20Sopenharmony_ci .leaf = red_leaf, 5378c2ecf20Sopenharmony_ci .find = red_find, 5388c2ecf20Sopenharmony_ci .walk = red_walk, 5398c2ecf20Sopenharmony_ci .dump = red_dump_class, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic struct Qdisc_ops red_qdisc_ops __read_mostly = { 5438c2ecf20Sopenharmony_ci .id = "red", 5448c2ecf20Sopenharmony_ci .priv_size = sizeof(struct red_sched_data), 5458c2ecf20Sopenharmony_ci .cl_ops = &red_class_ops, 5468c2ecf20Sopenharmony_ci .enqueue = red_enqueue, 5478c2ecf20Sopenharmony_ci .dequeue = red_dequeue, 5488c2ecf20Sopenharmony_ci .peek = red_peek, 5498c2ecf20Sopenharmony_ci .init = red_init, 5508c2ecf20Sopenharmony_ci .reset = red_reset, 5518c2ecf20Sopenharmony_ci .destroy = red_destroy, 5528c2ecf20Sopenharmony_ci .change = red_change, 5538c2ecf20Sopenharmony_ci .dump = red_dump, 5548c2ecf20Sopenharmony_ci .dump_stats = red_dump_stats, 5558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5568c2ecf20Sopenharmony_ci}; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int __init red_module_init(void) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci return register_qdisc(&red_qdisc_ops); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void __exit red_module_exit(void) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci unregister_qdisc(&red_qdisc_ops); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cimodule_init(red_module_init) 5698c2ecf20Sopenharmony_cimodule_exit(red_module_exit) 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 572