18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_choke.c CHOKE scheduler 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Stephen Hemminger <shemminger@vyatta.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2011 Eric Dumazet <eric.dumazet@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 148c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 158c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 168c2ecf20Sopenharmony_ci#include <net/inet_ecn.h> 178c2ecf20Sopenharmony_ci#include <net/red.h> 188c2ecf20Sopenharmony_ci#include <net/flow_dissector.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci CHOKe stateless AQM for fair bandwidth allocation 228c2ecf20Sopenharmony_ci ================================================= 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for 258c2ecf20Sopenharmony_ci unresponsive flows) is a variant of RED that penalizes misbehaving flows but 268c2ecf20Sopenharmony_ci maintains no flow state. The difference from RED is an additional step 278c2ecf20Sopenharmony_ci during the enqueuing process. If average queue size is over the 288c2ecf20Sopenharmony_ci low threshold (qmin), a packet is chosen at random from the queue. 298c2ecf20Sopenharmony_ci If both the new and chosen packet are from the same flow, both 308c2ecf20Sopenharmony_ci are dropped. Unlike RED, CHOKe is not really a "classful" qdisc because it 318c2ecf20Sopenharmony_ci needs to access packets in queue randomly. It has a minimal class 328c2ecf20Sopenharmony_ci interface to allow overriding the builtin flow classifier with 338c2ecf20Sopenharmony_ci filters. 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci Source: 368c2ecf20Sopenharmony_ci R. Pan, B. Prabhakar, and K. Psounis, "CHOKe, A Stateless 378c2ecf20Sopenharmony_ci Active Queue Management Scheme for Approximating Fair Bandwidth Allocation", 388c2ecf20Sopenharmony_ci IEEE INFOCOM, 2000. 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci A. Tang, J. Wang, S. Low, "Understanding CHOKe: Throughput and Spatial 418c2ecf20Sopenharmony_ci Characteristics", IEEE/ACM Transactions on Networking, 2004 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Upper bound on size of sk_buff table (packets) */ 468c2ecf20Sopenharmony_ci#define CHOKE_MAX_QUEUE (128*1024 - 1) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct choke_sched_data { 498c2ecf20Sopenharmony_ci/* Parameters */ 508c2ecf20Sopenharmony_ci u32 limit; 518c2ecf20Sopenharmony_ci unsigned char flags; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci struct red_parms parms; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Variables */ 568c2ecf20Sopenharmony_ci struct red_vars vars; 578c2ecf20Sopenharmony_ci struct { 588c2ecf20Sopenharmony_ci u32 prob_drop; /* Early probability drops */ 598c2ecf20Sopenharmony_ci u32 prob_mark; /* Early probability marks */ 608c2ecf20Sopenharmony_ci u32 forced_drop; /* Forced drops, qavg > max_thresh */ 618c2ecf20Sopenharmony_ci u32 forced_mark; /* Forced marks, qavg > max_thresh */ 628c2ecf20Sopenharmony_ci u32 pdrop; /* Drops due to queue limits */ 638c2ecf20Sopenharmony_ci u32 other; /* Drops due to drop() calls */ 648c2ecf20Sopenharmony_ci u32 matched; /* Drops to flow match */ 658c2ecf20Sopenharmony_ci } stats; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci unsigned int head; 688c2ecf20Sopenharmony_ci unsigned int tail; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci unsigned int tab_mask; /* size - 1 */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci struct sk_buff **tab; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* number of elements in queue including holes */ 768c2ecf20Sopenharmony_cistatic unsigned int choke_len(const struct choke_sched_data *q) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return (q->tail - q->head) & q->tab_mask; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Is ECN parameter configured */ 828c2ecf20Sopenharmony_cistatic int use_ecn(const struct choke_sched_data *q) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return q->flags & TC_RED_ECN; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Should packets over max just be dropped (versus marked) */ 888c2ecf20Sopenharmony_cistatic int use_harddrop(const struct choke_sched_data *q) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return q->flags & TC_RED_HARDDROP; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Move head pointer forward to skip over holes */ 948c2ecf20Sopenharmony_cistatic void choke_zap_head_holes(struct choke_sched_data *q) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci do { 978c2ecf20Sopenharmony_ci q->head = (q->head + 1) & q->tab_mask; 988c2ecf20Sopenharmony_ci if (q->head == q->tail) 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } while (q->tab[q->head] == NULL); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Move tail pointer backwards to reuse holes */ 1048c2ecf20Sopenharmony_cistatic void choke_zap_tail_holes(struct choke_sched_data *q) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci do { 1078c2ecf20Sopenharmony_ci q->tail = (q->tail - 1) & q->tab_mask; 1088c2ecf20Sopenharmony_ci if (q->head == q->tail) 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } while (q->tab[q->tail] == NULL); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Drop packet from queue array by creating a "hole" */ 1148c2ecf20Sopenharmony_cistatic void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx, 1158c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 1188c2ecf20Sopenharmony_ci struct sk_buff *skb = q->tab[idx]; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci q->tab[idx] = NULL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (idx == q->head) 1238c2ecf20Sopenharmony_ci choke_zap_head_holes(q); 1248c2ecf20Sopenharmony_ci if (idx == q->tail) 1258c2ecf20Sopenharmony_ci choke_zap_tail_holes(q); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 1288c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); 1298c2ecf20Sopenharmony_ci qdisc_drop(skb, sch, to_free); 1308c2ecf20Sopenharmony_ci --sch->q.qlen; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistruct choke_skb_cb { 1348c2ecf20Sopenharmony_ci u8 keys_valid; 1358c2ecf20Sopenharmony_ci struct flow_keys_digest keys; 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb)); 1418c2ecf20Sopenharmony_ci return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Compare flow of two packets 1468c2ecf20Sopenharmony_ci * Returns true only if source and destination address and port match. 1478c2ecf20Sopenharmony_ci * false for special cases 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic bool choke_match_flow(struct sk_buff *skb1, 1508c2ecf20Sopenharmony_ci struct sk_buff *skb2) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct flow_keys temp; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (skb1->protocol != skb2->protocol) 1558c2ecf20Sopenharmony_ci return false; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!choke_skb_cb(skb1)->keys_valid) { 1588c2ecf20Sopenharmony_ci choke_skb_cb(skb1)->keys_valid = 1; 1598c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb1, &temp, 0); 1608c2ecf20Sopenharmony_ci make_flow_keys_digest(&choke_skb_cb(skb1)->keys, &temp); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!choke_skb_cb(skb2)->keys_valid) { 1648c2ecf20Sopenharmony_ci choke_skb_cb(skb2)->keys_valid = 1; 1658c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb2, &temp, 0); 1668c2ecf20Sopenharmony_ci make_flow_keys_digest(&choke_skb_cb(skb2)->keys, &temp); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return !memcmp(&choke_skb_cb(skb1)->keys, 1708c2ecf20Sopenharmony_ci &choke_skb_cb(skb2)->keys, 1718c2ecf20Sopenharmony_ci sizeof(choke_skb_cb(skb1)->keys)); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * Select a packet at random from queue 1768c2ecf20Sopenharmony_ci * HACK: since queue can have holes from previous deletion; retry several 1778c2ecf20Sopenharmony_ci * times to find a random skb but then just give up and return the head 1788c2ecf20Sopenharmony_ci * Will return NULL if queue is empty (q->head == q->tail) 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistatic struct sk_buff *choke_peek_random(const struct choke_sched_data *q, 1818c2ecf20Sopenharmony_ci unsigned int *pidx) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct sk_buff *skb; 1848c2ecf20Sopenharmony_ci int retrys = 3; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci do { 1878c2ecf20Sopenharmony_ci *pidx = (q->head + prandom_u32_max(choke_len(q))) & q->tab_mask; 1888c2ecf20Sopenharmony_ci skb = q->tab[*pidx]; 1898c2ecf20Sopenharmony_ci if (skb) 1908c2ecf20Sopenharmony_ci return skb; 1918c2ecf20Sopenharmony_ci } while (--retrys > 0); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return q->tab[*pidx = q->head]; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Compare new packet with random packet in queue 1988c2ecf20Sopenharmony_ci * returns true if matched and sets *pidx 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_cistatic bool choke_match_random(const struct choke_sched_data *q, 2018c2ecf20Sopenharmony_ci struct sk_buff *nskb, 2028c2ecf20Sopenharmony_ci unsigned int *pidx) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct sk_buff *oskb; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (q->head == q->tail) 2078c2ecf20Sopenharmony_ci return false; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci oskb = choke_peek_random(q, pidx); 2108c2ecf20Sopenharmony_ci return choke_match_flow(oskb, nskb); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, 2148c2ecf20Sopenharmony_ci struct sk_buff **to_free) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 2178c2ecf20Sopenharmony_ci const struct red_parms *p = &q->parms; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci choke_skb_cb(skb)->keys_valid = 0; 2208c2ecf20Sopenharmony_ci /* Compute average queue usage (see RED) */ 2218c2ecf20Sopenharmony_ci q->vars.qavg = red_calc_qavg(p, &q->vars, sch->q.qlen); 2228c2ecf20Sopenharmony_ci if (red_is_idling(&q->vars)) 2238c2ecf20Sopenharmony_ci red_end_of_idle_period(&q->vars); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Is queue small? */ 2268c2ecf20Sopenharmony_ci if (q->vars.qavg <= p->qth_min) 2278c2ecf20Sopenharmony_ci q->vars.qcount = -1; 2288c2ecf20Sopenharmony_ci else { 2298c2ecf20Sopenharmony_ci unsigned int idx; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Draw a packet at random from queue and compare flow */ 2328c2ecf20Sopenharmony_ci if (choke_match_random(q, skb, &idx)) { 2338c2ecf20Sopenharmony_ci q->stats.matched++; 2348c2ecf20Sopenharmony_ci choke_drop_by_idx(sch, idx, to_free); 2358c2ecf20Sopenharmony_ci goto congestion_drop; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Queue is large, always mark/drop */ 2398c2ecf20Sopenharmony_ci if (q->vars.qavg > p->qth_max) { 2408c2ecf20Sopenharmony_ci q->vars.qcount = -1; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 2438c2ecf20Sopenharmony_ci if (use_harddrop(q) || !use_ecn(q) || 2448c2ecf20Sopenharmony_ci !INET_ECN_set_ce(skb)) { 2458c2ecf20Sopenharmony_ci q->stats.forced_drop++; 2468c2ecf20Sopenharmony_ci goto congestion_drop; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci q->stats.forced_mark++; 2508c2ecf20Sopenharmony_ci } else if (++q->vars.qcount) { 2518c2ecf20Sopenharmony_ci if (red_mark_probability(p, &q->vars, q->vars.qavg)) { 2528c2ecf20Sopenharmony_ci q->vars.qcount = 0; 2538c2ecf20Sopenharmony_ci q->vars.qR = red_random(p); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 2568c2ecf20Sopenharmony_ci if (!use_ecn(q) || !INET_ECN_set_ce(skb)) { 2578c2ecf20Sopenharmony_ci q->stats.prob_drop++; 2588c2ecf20Sopenharmony_ci goto congestion_drop; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci q->stats.prob_mark++; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } else 2648c2ecf20Sopenharmony_ci q->vars.qR = red_random(p); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Admit new packet */ 2688c2ecf20Sopenharmony_ci if (sch->q.qlen < q->limit) { 2698c2ecf20Sopenharmony_ci q->tab[q->tail] = skb; 2708c2ecf20Sopenharmony_ci q->tail = (q->tail + 1) & q->tab_mask; 2718c2ecf20Sopenharmony_ci ++sch->q.qlen; 2728c2ecf20Sopenharmony_ci qdisc_qstats_backlog_inc(sch, skb); 2738c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci q->stats.pdrop++; 2778c2ecf20Sopenharmony_ci return qdisc_drop(skb, sch, to_free); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cicongestion_drop: 2808c2ecf20Sopenharmony_ci qdisc_drop(skb, sch, to_free); 2818c2ecf20Sopenharmony_ci return NET_XMIT_CN; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic struct sk_buff *choke_dequeue(struct Qdisc *sch) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 2878c2ecf20Sopenharmony_ci struct sk_buff *skb; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (q->head == q->tail) { 2908c2ecf20Sopenharmony_ci if (!red_is_idling(&q->vars)) 2918c2ecf20Sopenharmony_ci red_start_of_idle_period(&q->vars); 2928c2ecf20Sopenharmony_ci return NULL; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci skb = q->tab[q->head]; 2968c2ecf20Sopenharmony_ci q->tab[q->head] = NULL; 2978c2ecf20Sopenharmony_ci choke_zap_head_holes(q); 2988c2ecf20Sopenharmony_ci --sch->q.qlen; 2998c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 3008c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return skb; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void choke_reset(struct Qdisc *sch) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci while (q->head != q->tail) { 3108c2ecf20Sopenharmony_ci struct sk_buff *skb = q->tab[q->head]; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci q->head = (q->head + 1) & q->tab_mask; 3138c2ecf20Sopenharmony_ci if (!skb) 3148c2ecf20Sopenharmony_ci continue; 3158c2ecf20Sopenharmony_ci rtnl_qdisc_drop(skb, sch); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (q->tab) 3198c2ecf20Sopenharmony_ci memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *)); 3208c2ecf20Sopenharmony_ci q->head = q->tail = 0; 3218c2ecf20Sopenharmony_ci red_restart(&q->vars); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = { 3258c2ecf20Sopenharmony_ci [TCA_CHOKE_PARMS] = { .len = sizeof(struct tc_red_qopt) }, 3268c2ecf20Sopenharmony_ci [TCA_CHOKE_STAB] = { .len = RED_STAB_SIZE }, 3278c2ecf20Sopenharmony_ci [TCA_CHOKE_MAX_P] = { .type = NLA_U32 }, 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void choke_free(void *addr) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci kvfree(addr); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int choke_change(struct Qdisc *sch, struct nlattr *opt, 3378c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 3408c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CHOKE_MAX + 1]; 3418c2ecf20Sopenharmony_ci const struct tc_red_qopt *ctl; 3428c2ecf20Sopenharmony_ci int err; 3438c2ecf20Sopenharmony_ci struct sk_buff **old = NULL; 3448c2ecf20Sopenharmony_ci unsigned int mask; 3458c2ecf20Sopenharmony_ci u32 max_P; 3468c2ecf20Sopenharmony_ci u8 *stab; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (opt == NULL) 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CHOKE_MAX, opt, 3528c2ecf20Sopenharmony_ci choke_policy, NULL); 3538c2ecf20Sopenharmony_ci if (err < 0) 3548c2ecf20Sopenharmony_ci return err; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (tb[TCA_CHOKE_PARMS] == NULL || 3578c2ecf20Sopenharmony_ci tb[TCA_CHOKE_STAB] == NULL) 3588c2ecf20Sopenharmony_ci return -EINVAL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci max_P = tb[TCA_CHOKE_MAX_P] ? nla_get_u32(tb[TCA_CHOKE_MAX_P]) : 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ctl = nla_data(tb[TCA_CHOKE_PARMS]); 3638c2ecf20Sopenharmony_ci stab = nla_data(tb[TCA_CHOKE_STAB]); 3648c2ecf20Sopenharmony_ci if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log, stab)) 3658c2ecf20Sopenharmony_ci return -EINVAL; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (ctl->limit > CHOKE_MAX_QUEUE) 3688c2ecf20Sopenharmony_ci return -EINVAL; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mask = roundup_pow_of_two(ctl->limit + 1) - 1; 3718c2ecf20Sopenharmony_ci if (mask != q->tab_mask) { 3728c2ecf20Sopenharmony_ci struct sk_buff **ntab; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ntab = kvcalloc(mask + 1, sizeof(struct sk_buff *), GFP_KERNEL); 3758c2ecf20Sopenharmony_ci if (!ntab) 3768c2ecf20Sopenharmony_ci return -ENOMEM; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci sch_tree_lock(sch); 3798c2ecf20Sopenharmony_ci old = q->tab; 3808c2ecf20Sopenharmony_ci if (old) { 3818c2ecf20Sopenharmony_ci unsigned int oqlen = sch->q.qlen, tail = 0; 3828c2ecf20Sopenharmony_ci unsigned dropped = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci while (q->head != q->tail) { 3858c2ecf20Sopenharmony_ci struct sk_buff *skb = q->tab[q->head]; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci q->head = (q->head + 1) & q->tab_mask; 3888c2ecf20Sopenharmony_ci if (!skb) 3898c2ecf20Sopenharmony_ci continue; 3908c2ecf20Sopenharmony_ci if (tail < mask) { 3918c2ecf20Sopenharmony_ci ntab[tail++] = skb; 3928c2ecf20Sopenharmony_ci continue; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci dropped += qdisc_pkt_len(skb); 3958c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 3968c2ecf20Sopenharmony_ci --sch->q.qlen; 3978c2ecf20Sopenharmony_ci rtnl_qdisc_drop(skb, sch); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped); 4008c2ecf20Sopenharmony_ci q->head = 0; 4018c2ecf20Sopenharmony_ci q->tail = tail; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci q->tab_mask = mask; 4058c2ecf20Sopenharmony_ci q->tab = ntab; 4068c2ecf20Sopenharmony_ci } else 4078c2ecf20Sopenharmony_ci sch_tree_lock(sch); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci q->flags = ctl->flags; 4108c2ecf20Sopenharmony_ci q->limit = ctl->limit; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog, 4138c2ecf20Sopenharmony_ci ctl->Plog, ctl->Scell_log, 4148c2ecf20Sopenharmony_ci stab, 4158c2ecf20Sopenharmony_ci max_P); 4168c2ecf20Sopenharmony_ci red_set_vars(&q->vars); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (q->head == q->tail) 4198c2ecf20Sopenharmony_ci red_end_of_idle_period(&q->vars); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 4228c2ecf20Sopenharmony_ci choke_free(old); 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int choke_init(struct Qdisc *sch, struct nlattr *opt, 4278c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci return choke_change(sch, opt, extack); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int choke_dump(struct Qdisc *sch, struct sk_buff *skb) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 4358c2ecf20Sopenharmony_ci struct nlattr *opts = NULL; 4368c2ecf20Sopenharmony_ci struct tc_red_qopt opt = { 4378c2ecf20Sopenharmony_ci .limit = q->limit, 4388c2ecf20Sopenharmony_ci .flags = q->flags, 4398c2ecf20Sopenharmony_ci .qth_min = q->parms.qth_min >> q->parms.Wlog, 4408c2ecf20Sopenharmony_ci .qth_max = q->parms.qth_max >> q->parms.Wlog, 4418c2ecf20Sopenharmony_ci .Wlog = q->parms.Wlog, 4428c2ecf20Sopenharmony_ci .Plog = q->parms.Plog, 4438c2ecf20Sopenharmony_ci .Scell_log = q->parms.Scell_log, 4448c2ecf20Sopenharmony_ci }; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci opts = nla_nest_start_noflag(skb, TCA_OPTIONS); 4478c2ecf20Sopenharmony_ci if (opts == NULL) 4488c2ecf20Sopenharmony_ci goto nla_put_failure; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt) || 4518c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_CHOKE_MAX_P, q->parms.max_P)) 4528c2ecf20Sopenharmony_ci goto nla_put_failure; 4538c2ecf20Sopenharmony_ci return nla_nest_end(skb, opts); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cinla_put_failure: 4568c2ecf20Sopenharmony_ci nla_nest_cancel(skb, opts); 4578c2ecf20Sopenharmony_ci return -EMSGSIZE; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int choke_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 4638c2ecf20Sopenharmony_ci struct tc_choke_xstats st = { 4648c2ecf20Sopenharmony_ci .early = q->stats.prob_drop + q->stats.forced_drop, 4658c2ecf20Sopenharmony_ci .marked = q->stats.prob_mark + q->stats.forced_mark, 4668c2ecf20Sopenharmony_ci .pdrop = q->stats.pdrop, 4678c2ecf20Sopenharmony_ci .other = q->stats.other, 4688c2ecf20Sopenharmony_ci .matched = q->stats.matched, 4698c2ecf20Sopenharmony_ci }; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &st, sizeof(st)); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void choke_destroy(struct Qdisc *sch) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci choke_free(q->tab); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic struct sk_buff *choke_peek_head(struct Qdisc *sch) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct choke_sched_data *q = qdisc_priv(sch); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return (q->head != q->tail) ? q->tab[q->head] : NULL; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic struct Qdisc_ops choke_qdisc_ops __read_mostly = { 4898c2ecf20Sopenharmony_ci .id = "choke", 4908c2ecf20Sopenharmony_ci .priv_size = sizeof(struct choke_sched_data), 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci .enqueue = choke_enqueue, 4938c2ecf20Sopenharmony_ci .dequeue = choke_dequeue, 4948c2ecf20Sopenharmony_ci .peek = choke_peek_head, 4958c2ecf20Sopenharmony_ci .init = choke_init, 4968c2ecf20Sopenharmony_ci .destroy = choke_destroy, 4978c2ecf20Sopenharmony_ci .reset = choke_reset, 4988c2ecf20Sopenharmony_ci .change = choke_change, 4998c2ecf20Sopenharmony_ci .dump = choke_dump, 5008c2ecf20Sopenharmony_ci .dump_stats = choke_dump_stats, 5018c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5028c2ecf20Sopenharmony_ci}; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int __init choke_module_init(void) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci return register_qdisc(&choke_qdisc_ops); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic void __exit choke_module_exit(void) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci unregister_qdisc(&choke_qdisc_ops); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cimodule_init(choke_module_init) 5158c2ecf20Sopenharmony_cimodule_exit(choke_module_exit) 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 518