18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_sfb.c Stochastic Fair Blue 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Juliusz Chroboczek <jch@pps.jussieu.fr> 68c2ecf20Sopenharmony_ci * Copyright (c) 2011 Eric Dumazet <eric.dumazet@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * W. Feng, D. Kandlur, D. Saha, K. Shin. Blue: 98c2ecf20Sopenharmony_ci * A New Class of Active Queue Management Algorithms. 108c2ecf20Sopenharmony_ci * U. Michigan CSE-TR-387-99, April 1999. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * http://www.thefengs.com/wuchang/blue/CSE-TR-387-99.pdf 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci#include <linux/random.h> 218c2ecf20Sopenharmony_ci#include <linux/siphash.h> 228c2ecf20Sopenharmony_ci#include <net/ip.h> 238c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 248c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 258c2ecf20Sopenharmony_ci#include <net/inet_ecn.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * SFB uses two B[l][n] : L x N arrays of bins (L levels, N bins per level) 298c2ecf20Sopenharmony_ci * This implementation uses L = 8 and N = 16 308c2ecf20Sopenharmony_ci * This permits us to split one 32bit hash (provided per packet by rxhash or 318c2ecf20Sopenharmony_ci * external classifier) into 8 subhashes of 4 bits. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define SFB_BUCKET_SHIFT 4 348c2ecf20Sopenharmony_ci#define SFB_NUMBUCKETS (1 << SFB_BUCKET_SHIFT) /* N bins per Level */ 358c2ecf20Sopenharmony_ci#define SFB_BUCKET_MASK (SFB_NUMBUCKETS - 1) 368c2ecf20Sopenharmony_ci#define SFB_LEVELS (32 / SFB_BUCKET_SHIFT) /* L */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* SFB algo uses a virtual queue, named "bin" */ 398c2ecf20Sopenharmony_cistruct sfb_bucket { 408c2ecf20Sopenharmony_ci u16 qlen; /* length of virtual queue */ 418c2ecf20Sopenharmony_ci u16 p_mark; /* marking probability */ 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* We use a double buffering right before hash change 458c2ecf20Sopenharmony_ci * (Section 4.4 of SFB reference : moving hash functions) 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistruct sfb_bins { 488c2ecf20Sopenharmony_ci siphash_key_t perturbation; /* siphash key */ 498c2ecf20Sopenharmony_ci struct sfb_bucket bins[SFB_LEVELS][SFB_NUMBUCKETS]; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct sfb_sched_data { 538c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 548c2ecf20Sopenharmony_ci struct tcf_proto __rcu *filter_list; 558c2ecf20Sopenharmony_ci struct tcf_block *block; 568c2ecf20Sopenharmony_ci unsigned long rehash_interval; 578c2ecf20Sopenharmony_ci unsigned long warmup_time; /* double buffering warmup time in jiffies */ 588c2ecf20Sopenharmony_ci u32 max; 598c2ecf20Sopenharmony_ci u32 bin_size; /* maximum queue length per bin */ 608c2ecf20Sopenharmony_ci u32 increment; /* d1 */ 618c2ecf20Sopenharmony_ci u32 decrement; /* d2 */ 628c2ecf20Sopenharmony_ci u32 limit; /* HARD maximal queue length */ 638c2ecf20Sopenharmony_ci u32 penalty_rate; 648c2ecf20Sopenharmony_ci u32 penalty_burst; 658c2ecf20Sopenharmony_ci u32 tokens_avail; 668c2ecf20Sopenharmony_ci unsigned long rehash_time; 678c2ecf20Sopenharmony_ci unsigned long token_time; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci u8 slot; /* current active bins (0 or 1) */ 708c2ecf20Sopenharmony_ci bool double_buffering; 718c2ecf20Sopenharmony_ci struct sfb_bins bins[2]; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci struct { 748c2ecf20Sopenharmony_ci u32 earlydrop; 758c2ecf20Sopenharmony_ci u32 penaltydrop; 768c2ecf20Sopenharmony_ci u32 bucketdrop; 778c2ecf20Sopenharmony_ci u32 queuedrop; 788c2ecf20Sopenharmony_ci u32 childdrop; /* drops in child qdisc */ 798c2ecf20Sopenharmony_ci u32 marked; /* ECN mark */ 808c2ecf20Sopenharmony_ci } stats; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Each queued skb might be hashed on one or two bins 858c2ecf20Sopenharmony_ci * We store in skb_cb the two hash values. 868c2ecf20Sopenharmony_ci * (A zero value means double buffering was not used) 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistruct sfb_skb_cb { 898c2ecf20Sopenharmony_ci u32 hashes[2]; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb)); 958c2ecf20Sopenharmony_ci return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * If using 'internal' SFB flow classifier, hash comes from skb rxhash 1008c2ecf20Sopenharmony_ci * If using external classifier, hash comes from the classid. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic u32 sfb_hash(const struct sk_buff *skb, u32 slot) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return sfb_skb_cb(skb)->hashes[slot]; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Probabilities are coded as Q0.16 fixed-point values, 1088c2ecf20Sopenharmony_ci * with 0xFFFF representing 65535/65536 (almost 1.0) 1098c2ecf20Sopenharmony_ci * Addition and subtraction are saturating in [0, 65535] 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic u32 prob_plus(u32 p1, u32 p2) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci u32 res = p1 + p2; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return min_t(u32, res, SFB_MAX_PROB); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic u32 prob_minus(u32 p1, u32 p2) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return p1 > p2 ? p1 - p2 : 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int i; 1268c2ecf20Sopenharmony_ci struct sfb_bucket *b = &q->bins[slot].bins[0][0]; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (i = 0; i < SFB_LEVELS; i++) { 1298c2ecf20Sopenharmony_ci u32 hash = sfbhash & SFB_BUCKET_MASK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci sfbhash >>= SFB_BUCKET_SHIFT; 1328c2ecf20Sopenharmony_ci if (b[hash].qlen < 0xFFFF) 1338c2ecf20Sopenharmony_ci b[hash].qlen++; 1348c2ecf20Sopenharmony_ci b += SFB_NUMBUCKETS; /* next level */ 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void increment_qlen(const struct sfb_skb_cb *cb, struct sfb_sched_data *q) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 sfbhash; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci sfbhash = cb->hashes[0]; 1438c2ecf20Sopenharmony_ci if (sfbhash) 1448c2ecf20Sopenharmony_ci increment_one_qlen(sfbhash, 0, q); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci sfbhash = cb->hashes[1]; 1478c2ecf20Sopenharmony_ci if (sfbhash) 1488c2ecf20Sopenharmony_ci increment_one_qlen(sfbhash, 1, q); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void decrement_one_qlen(u32 sfbhash, u32 slot, 1528c2ecf20Sopenharmony_ci struct sfb_sched_data *q) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int i; 1558c2ecf20Sopenharmony_ci struct sfb_bucket *b = &q->bins[slot].bins[0][0]; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < SFB_LEVELS; i++) { 1588c2ecf20Sopenharmony_ci u32 hash = sfbhash & SFB_BUCKET_MASK; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci sfbhash >>= SFB_BUCKET_SHIFT; 1618c2ecf20Sopenharmony_ci if (b[hash].qlen > 0) 1628c2ecf20Sopenharmony_ci b[hash].qlen--; 1638c2ecf20Sopenharmony_ci b += SFB_NUMBUCKETS; /* next level */ 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void decrement_qlen(const struct sk_buff *skb, struct sfb_sched_data *q) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u32 sfbhash; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sfbhash = sfb_hash(skb, 0); 1728c2ecf20Sopenharmony_ci if (sfbhash) 1738c2ecf20Sopenharmony_ci decrement_one_qlen(sfbhash, 0, q); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci sfbhash = sfb_hash(skb, 1); 1768c2ecf20Sopenharmony_ci if (sfbhash) 1778c2ecf20Sopenharmony_ci decrement_one_qlen(sfbhash, 1, q); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void decrement_prob(struct sfb_bucket *b, struct sfb_sched_data *q) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci b->p_mark = prob_minus(b->p_mark, q->decrement); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void increment_prob(struct sfb_bucket *b, struct sfb_sched_data *q) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci b->p_mark = prob_plus(b->p_mark, q->increment); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void sfb_zero_all_buckets(struct sfb_sched_data *q) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci memset(&q->bins, 0, sizeof(q->bins)); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * compute max qlen, max p_mark, and avg p_mark 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_data *q) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci u32 qlen = 0, prob = 0, totalpm = 0; 2028c2ecf20Sopenharmony_ci const struct sfb_bucket *b = &q->bins[q->slot].bins[0][0]; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < SFB_LEVELS * SFB_NUMBUCKETS; i++) { 2058c2ecf20Sopenharmony_ci if (qlen < b->qlen) 2068c2ecf20Sopenharmony_ci qlen = b->qlen; 2078c2ecf20Sopenharmony_ci totalpm += b->p_mark; 2088c2ecf20Sopenharmony_ci if (prob < b->p_mark) 2098c2ecf20Sopenharmony_ci prob = b->p_mark; 2108c2ecf20Sopenharmony_ci b++; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci *prob_r = prob; 2138c2ecf20Sopenharmony_ci *avgpm_r = totalpm / (SFB_LEVELS * SFB_NUMBUCKETS); 2148c2ecf20Sopenharmony_ci return qlen; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci get_random_bytes(&q->bins[slot].perturbation, 2218c2ecf20Sopenharmony_ci sizeof(q->bins[slot].perturbation)); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void sfb_swap_slot(struct sfb_sched_data *q) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci sfb_init_perturbation(q->slot, q); 2278c2ecf20Sopenharmony_ci q->slot ^= 1; 2288c2ecf20Sopenharmony_ci q->double_buffering = false; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* Non elastic flows are allowed to use part of the bandwidth, expressed 2328c2ecf20Sopenharmony_ci * in "penalty_rate" packets per second, with "penalty_burst" burst 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic bool sfb_rate_limit(struct sk_buff *skb, struct sfb_sched_data *q) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci if (q->penalty_rate == 0 || q->penalty_burst == 0) 2378c2ecf20Sopenharmony_ci return true; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (q->tokens_avail < 1) { 2408c2ecf20Sopenharmony_ci unsigned long age = min(10UL * HZ, jiffies - q->token_time); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci q->tokens_avail = (age * q->penalty_rate) / HZ; 2438c2ecf20Sopenharmony_ci if (q->tokens_avail > q->penalty_burst) 2448c2ecf20Sopenharmony_ci q->tokens_avail = q->penalty_burst; 2458c2ecf20Sopenharmony_ci q->token_time = jiffies; 2468c2ecf20Sopenharmony_ci if (q->tokens_avail < 1) 2478c2ecf20Sopenharmony_ci return true; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci q->tokens_avail--; 2518c2ecf20Sopenharmony_ci return false; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl, 2558c2ecf20Sopenharmony_ci int *qerr, u32 *salt) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct tcf_result res; 2588c2ecf20Sopenharmony_ci int result; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci result = tcf_classify(skb, fl, &res, false); 2618c2ecf20Sopenharmony_ci if (result >= 0) { 2628c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 2638c2ecf20Sopenharmony_ci switch (result) { 2648c2ecf20Sopenharmony_ci case TC_ACT_STOLEN: 2658c2ecf20Sopenharmony_ci case TC_ACT_QUEUED: 2668c2ecf20Sopenharmony_ci case TC_ACT_TRAP: 2678c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 2688c2ecf20Sopenharmony_ci fallthrough; 2698c2ecf20Sopenharmony_ci case TC_ACT_SHOT: 2708c2ecf20Sopenharmony_ci return false; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci#endif 2738c2ecf20Sopenharmony_ci *salt = TC_H_MIN(res.classid); 2748c2ecf20Sopenharmony_ci return true; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci return false; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, 2808c2ecf20Sopenharmony_ci struct sk_buff **to_free) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 2848c2ecf20Sopenharmony_ci unsigned int len = qdisc_pkt_len(skb); 2858c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 2868c2ecf20Sopenharmony_ci struct tcf_proto *fl; 2878c2ecf20Sopenharmony_ci struct sfb_skb_cb cb; 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci u32 p_min = ~0; 2908c2ecf20Sopenharmony_ci u32 minqlen = ~0; 2918c2ecf20Sopenharmony_ci u32 r, sfbhash; 2928c2ecf20Sopenharmony_ci u32 slot = q->slot; 2938c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (unlikely(sch->q.qlen >= q->limit)) { 2968c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 2978c2ecf20Sopenharmony_ci q->stats.queuedrop++; 2988c2ecf20Sopenharmony_ci goto drop; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (q->rehash_interval > 0) { 3028c2ecf20Sopenharmony_ci unsigned long limit = q->rehash_time + q->rehash_interval; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (unlikely(time_after(jiffies, limit))) { 3058c2ecf20Sopenharmony_ci sfb_swap_slot(q); 3068c2ecf20Sopenharmony_ci q->rehash_time = jiffies; 3078c2ecf20Sopenharmony_ci } else if (unlikely(!q->double_buffering && q->warmup_time > 0 && 3088c2ecf20Sopenharmony_ci time_after(jiffies, limit - q->warmup_time))) { 3098c2ecf20Sopenharmony_ci q->double_buffering = true; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci fl = rcu_dereference_bh(q->filter_list); 3148c2ecf20Sopenharmony_ci if (fl) { 3158c2ecf20Sopenharmony_ci u32 salt; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* If using external classifiers, get result and record it. */ 3188c2ecf20Sopenharmony_ci if (!sfb_classify(skb, fl, &ret, &salt)) 3198c2ecf20Sopenharmony_ci goto other_drop; 3208c2ecf20Sopenharmony_ci sfbhash = siphash_1u32(salt, &q->bins[slot].perturbation); 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci sfbhash = skb_get_hash_perturb(skb, &q->bins[slot].perturbation); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!sfbhash) 3278c2ecf20Sopenharmony_ci sfbhash = 1; 3288c2ecf20Sopenharmony_ci sfb_skb_cb(skb)->hashes[slot] = sfbhash; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci for (i = 0; i < SFB_LEVELS; i++) { 3318c2ecf20Sopenharmony_ci u32 hash = sfbhash & SFB_BUCKET_MASK; 3328c2ecf20Sopenharmony_ci struct sfb_bucket *b = &q->bins[slot].bins[i][hash]; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci sfbhash >>= SFB_BUCKET_SHIFT; 3358c2ecf20Sopenharmony_ci if (b->qlen == 0) 3368c2ecf20Sopenharmony_ci decrement_prob(b, q); 3378c2ecf20Sopenharmony_ci else if (b->qlen >= q->bin_size) 3388c2ecf20Sopenharmony_ci increment_prob(b, q); 3398c2ecf20Sopenharmony_ci if (minqlen > b->qlen) 3408c2ecf20Sopenharmony_ci minqlen = b->qlen; 3418c2ecf20Sopenharmony_ci if (p_min > b->p_mark) 3428c2ecf20Sopenharmony_ci p_min = b->p_mark; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci slot ^= 1; 3468c2ecf20Sopenharmony_ci sfb_skb_cb(skb)->hashes[slot] = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (unlikely(minqlen >= q->max)) { 3498c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 3508c2ecf20Sopenharmony_ci q->stats.bucketdrop++; 3518c2ecf20Sopenharmony_ci goto drop; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (unlikely(p_min >= SFB_MAX_PROB)) { 3558c2ecf20Sopenharmony_ci /* Inelastic flow */ 3568c2ecf20Sopenharmony_ci if (q->double_buffering) { 3578c2ecf20Sopenharmony_ci sfbhash = skb_get_hash_perturb(skb, 3588c2ecf20Sopenharmony_ci &q->bins[slot].perturbation); 3598c2ecf20Sopenharmony_ci if (!sfbhash) 3608c2ecf20Sopenharmony_ci sfbhash = 1; 3618c2ecf20Sopenharmony_ci sfb_skb_cb(skb)->hashes[slot] = sfbhash; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 0; i < SFB_LEVELS; i++) { 3648c2ecf20Sopenharmony_ci u32 hash = sfbhash & SFB_BUCKET_MASK; 3658c2ecf20Sopenharmony_ci struct sfb_bucket *b = &q->bins[slot].bins[i][hash]; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci sfbhash >>= SFB_BUCKET_SHIFT; 3688c2ecf20Sopenharmony_ci if (b->qlen == 0) 3698c2ecf20Sopenharmony_ci decrement_prob(b, q); 3708c2ecf20Sopenharmony_ci else if (b->qlen >= q->bin_size) 3718c2ecf20Sopenharmony_ci increment_prob(b, q); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci if (sfb_rate_limit(skb, q)) { 3758c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 3768c2ecf20Sopenharmony_ci q->stats.penaltydrop++; 3778c2ecf20Sopenharmony_ci goto drop; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci goto enqueue; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci r = prandom_u32() & SFB_MAX_PROB; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (unlikely(r < p_min)) { 3858c2ecf20Sopenharmony_ci if (unlikely(p_min > SFB_MAX_PROB / 2)) { 3868c2ecf20Sopenharmony_ci /* If we're marking that many packets, then either 3878c2ecf20Sopenharmony_ci * this flow is unresponsive, or we're badly congested. 3888c2ecf20Sopenharmony_ci * In either case, we want to start dropping packets. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci if (r < (p_min - SFB_MAX_PROB / 2) * 2) { 3918c2ecf20Sopenharmony_ci q->stats.earlydrop++; 3928c2ecf20Sopenharmony_ci goto drop; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci if (INET_ECN_set_ce(skb)) { 3968c2ecf20Sopenharmony_ci q->stats.marked++; 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci q->stats.earlydrop++; 3998c2ecf20Sopenharmony_ci goto drop; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cienqueue: 4048c2ecf20Sopenharmony_ci memcpy(&cb, sfb_skb_cb(skb), sizeof(cb)); 4058c2ecf20Sopenharmony_ci ret = qdisc_enqueue(skb, child, to_free); 4068c2ecf20Sopenharmony_ci if (likely(ret == NET_XMIT_SUCCESS)) { 4078c2ecf20Sopenharmony_ci sch->qstats.backlog += len; 4088c2ecf20Sopenharmony_ci sch->q.qlen++; 4098c2ecf20Sopenharmony_ci increment_qlen(&cb, q); 4108c2ecf20Sopenharmony_ci } else if (net_xmit_drop_count(ret)) { 4118c2ecf20Sopenharmony_ci q->stats.childdrop++; 4128c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cidrop: 4178c2ecf20Sopenharmony_ci qdisc_drop(skb, sch, to_free); 4188c2ecf20Sopenharmony_ci return NET_XMIT_CN; 4198c2ecf20Sopenharmony_ciother_drop: 4208c2ecf20Sopenharmony_ci if (ret & __NET_XMIT_BYPASS) 4218c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 4228c2ecf20Sopenharmony_ci kfree_skb(skb); 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic struct sk_buff *sfb_dequeue(struct Qdisc *sch) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 4298c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 4308c2ecf20Sopenharmony_ci struct sk_buff *skb; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci skb = child->dequeue(q->qdisc); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (skb) { 4358c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 4368c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 4378c2ecf20Sopenharmony_ci sch->q.qlen--; 4388c2ecf20Sopenharmony_ci decrement_qlen(skb, q); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return skb; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct sk_buff *sfb_peek(struct Qdisc *sch) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 4478c2ecf20Sopenharmony_ci struct Qdisc *child = q->qdisc; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return child->ops->peek(child); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* No sfb_drop -- impossible since the child doesn't return the dropped skb. */ 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void sfb_reset(struct Qdisc *sch) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (likely(q->qdisc)) 4598c2ecf20Sopenharmony_ci qdisc_reset(q->qdisc); 4608c2ecf20Sopenharmony_ci q->slot = 0; 4618c2ecf20Sopenharmony_ci q->double_buffering = false; 4628c2ecf20Sopenharmony_ci sfb_zero_all_buckets(q); 4638c2ecf20Sopenharmony_ci sfb_init_perturbation(0, q); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void sfb_destroy(struct Qdisc *sch) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci tcf_block_put(q->block); 4718c2ecf20Sopenharmony_ci qdisc_put(q->qdisc); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct nla_policy sfb_policy[TCA_SFB_MAX + 1] = { 4758c2ecf20Sopenharmony_ci [TCA_SFB_PARMS] = { .len = sizeof(struct tc_sfb_qopt) }, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic const struct tc_sfb_qopt sfb_default_ops = { 4798c2ecf20Sopenharmony_ci .rehash_interval = 600 * MSEC_PER_SEC, 4808c2ecf20Sopenharmony_ci .warmup_time = 60 * MSEC_PER_SEC, 4818c2ecf20Sopenharmony_ci .limit = 0, 4828c2ecf20Sopenharmony_ci .max = 25, 4838c2ecf20Sopenharmony_ci .bin_size = 20, 4848c2ecf20Sopenharmony_ci .increment = (SFB_MAX_PROB + 500) / 1000, /* 0.1 % */ 4858c2ecf20Sopenharmony_ci .decrement = (SFB_MAX_PROB + 3000) / 6000, 4868c2ecf20Sopenharmony_ci .penalty_rate = 10, 4878c2ecf20Sopenharmony_ci .penalty_burst = 20, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int sfb_change(struct Qdisc *sch, struct nlattr *opt, 4918c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 4948c2ecf20Sopenharmony_ci struct Qdisc *child, *old; 4958c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_SFB_MAX + 1]; 4968c2ecf20Sopenharmony_ci const struct tc_sfb_qopt *ctl = &sfb_default_ops; 4978c2ecf20Sopenharmony_ci u32 limit; 4988c2ecf20Sopenharmony_ci int err; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (opt) { 5018c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_SFB_MAX, opt, 5028c2ecf20Sopenharmony_ci sfb_policy, NULL); 5038c2ecf20Sopenharmony_ci if (err < 0) 5048c2ecf20Sopenharmony_ci return -EINVAL; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (tb[TCA_SFB_PARMS] == NULL) 5078c2ecf20Sopenharmony_ci return -EINVAL; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ctl = nla_data(tb[TCA_SFB_PARMS]); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci limit = ctl->limit; 5138c2ecf20Sopenharmony_ci if (limit == 0) 5148c2ecf20Sopenharmony_ci limit = qdisc_dev(sch)->tx_queue_len; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci child = fifo_create_dflt(sch, &pfifo_qdisc_ops, limit, extack); 5178c2ecf20Sopenharmony_ci if (IS_ERR(child)) 5188c2ecf20Sopenharmony_ci return PTR_ERR(child); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (child != &noop_qdisc) 5218c2ecf20Sopenharmony_ci qdisc_hash_add(child, true); 5228c2ecf20Sopenharmony_ci sch_tree_lock(sch); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci qdisc_purge_queue(q->qdisc); 5258c2ecf20Sopenharmony_ci old = q->qdisc; 5268c2ecf20Sopenharmony_ci q->qdisc = child; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci q->rehash_interval = msecs_to_jiffies(ctl->rehash_interval); 5298c2ecf20Sopenharmony_ci q->warmup_time = msecs_to_jiffies(ctl->warmup_time); 5308c2ecf20Sopenharmony_ci q->rehash_time = jiffies; 5318c2ecf20Sopenharmony_ci q->limit = limit; 5328c2ecf20Sopenharmony_ci q->increment = ctl->increment; 5338c2ecf20Sopenharmony_ci q->decrement = ctl->decrement; 5348c2ecf20Sopenharmony_ci q->max = ctl->max; 5358c2ecf20Sopenharmony_ci q->bin_size = ctl->bin_size; 5368c2ecf20Sopenharmony_ci q->penalty_rate = ctl->penalty_rate; 5378c2ecf20Sopenharmony_ci q->penalty_burst = ctl->penalty_burst; 5388c2ecf20Sopenharmony_ci q->tokens_avail = ctl->penalty_burst; 5398c2ecf20Sopenharmony_ci q->token_time = jiffies; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci q->slot = 0; 5428c2ecf20Sopenharmony_ci q->double_buffering = false; 5438c2ecf20Sopenharmony_ci sfb_zero_all_buckets(q); 5448c2ecf20Sopenharmony_ci sfb_init_perturbation(0, q); 5458c2ecf20Sopenharmony_ci sfb_init_perturbation(1, q); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 5488c2ecf20Sopenharmony_ci qdisc_put(old); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int sfb_init(struct Qdisc *sch, struct nlattr *opt, 5548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 5578c2ecf20Sopenharmony_ci int err; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci err = tcf_block_get(&q->block, &q->filter_list, sch, extack); 5608c2ecf20Sopenharmony_ci if (err) 5618c2ecf20Sopenharmony_ci return err; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci q->qdisc = &noop_qdisc; 5648c2ecf20Sopenharmony_ci return sfb_change(sch, opt, extack); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int sfb_dump(struct Qdisc *sch, struct sk_buff *skb) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 5708c2ecf20Sopenharmony_ci struct nlattr *opts; 5718c2ecf20Sopenharmony_ci struct tc_sfb_qopt opt = { 5728c2ecf20Sopenharmony_ci .rehash_interval = jiffies_to_msecs(q->rehash_interval), 5738c2ecf20Sopenharmony_ci .warmup_time = jiffies_to_msecs(q->warmup_time), 5748c2ecf20Sopenharmony_ci .limit = q->limit, 5758c2ecf20Sopenharmony_ci .max = q->max, 5768c2ecf20Sopenharmony_ci .bin_size = q->bin_size, 5778c2ecf20Sopenharmony_ci .increment = q->increment, 5788c2ecf20Sopenharmony_ci .decrement = q->decrement, 5798c2ecf20Sopenharmony_ci .penalty_rate = q->penalty_rate, 5808c2ecf20Sopenharmony_ci .penalty_burst = q->penalty_burst, 5818c2ecf20Sopenharmony_ci }; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci sch->qstats.backlog = q->qdisc->qstats.backlog; 5848c2ecf20Sopenharmony_ci opts = nla_nest_start_noflag(skb, TCA_OPTIONS); 5858c2ecf20Sopenharmony_ci if (opts == NULL) 5868c2ecf20Sopenharmony_ci goto nla_put_failure; 5878c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_SFB_PARMS, sizeof(opt), &opt)) 5888c2ecf20Sopenharmony_ci goto nla_put_failure; 5898c2ecf20Sopenharmony_ci return nla_nest_end(skb, opts); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cinla_put_failure: 5928c2ecf20Sopenharmony_ci nla_nest_cancel(skb, opts); 5938c2ecf20Sopenharmony_ci return -EMSGSIZE; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int sfb_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 5998c2ecf20Sopenharmony_ci struct tc_sfb_xstats st = { 6008c2ecf20Sopenharmony_ci .earlydrop = q->stats.earlydrop, 6018c2ecf20Sopenharmony_ci .penaltydrop = q->stats.penaltydrop, 6028c2ecf20Sopenharmony_ci .bucketdrop = q->stats.bucketdrop, 6038c2ecf20Sopenharmony_ci .queuedrop = q->stats.queuedrop, 6048c2ecf20Sopenharmony_ci .childdrop = q->stats.childdrop, 6058c2ecf20Sopenharmony_ci .marked = q->stats.marked, 6068c2ecf20Sopenharmony_ci }; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci st.maxqlen = sfb_compute_qlen(&st.maxprob, &st.avgprob, q); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &st, sizeof(st)); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic int sfb_dump_class(struct Qdisc *sch, unsigned long cl, 6148c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci return -ENOSYS; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int sfb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 6208c2ecf20Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (new == NULL) 6258c2ecf20Sopenharmony_ci new = &noop_qdisc; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &q->qdisc); 6288c2ecf20Sopenharmony_ci return 0; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic struct Qdisc *sfb_leaf(struct Qdisc *sch, unsigned long arg) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return q->qdisc; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic unsigned long sfb_find(struct Qdisc *sch, u32 classid) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci return 1; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic void sfb_unbind(struct Qdisc *sch, unsigned long arg) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int sfb_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 6488c2ecf20Sopenharmony_ci struct nlattr **tca, unsigned long *arg, 6498c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci return -ENOSYS; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic int sfb_delete(struct Qdisc *sch, unsigned long cl) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci return -ENOSYS; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic void sfb_walk(struct Qdisc *sch, struct qdisc_walker *walker) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci if (!walker->stop) { 6628c2ecf20Sopenharmony_ci if (walker->count >= walker->skip) 6638c2ecf20Sopenharmony_ci if (walker->fn(sch, 1, walker) < 0) { 6648c2ecf20Sopenharmony_ci walker->stop = 1; 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci walker->count++; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic struct tcf_block *sfb_tcf_block(struct Qdisc *sch, unsigned long cl, 6728c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct sfb_sched_data *q = qdisc_priv(sch); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (cl) 6778c2ecf20Sopenharmony_ci return NULL; 6788c2ecf20Sopenharmony_ci return q->block; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic unsigned long sfb_bind(struct Qdisc *sch, unsigned long parent, 6828c2ecf20Sopenharmony_ci u32 classid) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci return 0; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops sfb_class_ops = { 6898c2ecf20Sopenharmony_ci .graft = sfb_graft, 6908c2ecf20Sopenharmony_ci .leaf = sfb_leaf, 6918c2ecf20Sopenharmony_ci .find = sfb_find, 6928c2ecf20Sopenharmony_ci .change = sfb_change_class, 6938c2ecf20Sopenharmony_ci .delete = sfb_delete, 6948c2ecf20Sopenharmony_ci .walk = sfb_walk, 6958c2ecf20Sopenharmony_ci .tcf_block = sfb_tcf_block, 6968c2ecf20Sopenharmony_ci .bind_tcf = sfb_bind, 6978c2ecf20Sopenharmony_ci .unbind_tcf = sfb_unbind, 6988c2ecf20Sopenharmony_ci .dump = sfb_dump_class, 6998c2ecf20Sopenharmony_ci}; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic struct Qdisc_ops sfb_qdisc_ops __read_mostly = { 7028c2ecf20Sopenharmony_ci .id = "sfb", 7038c2ecf20Sopenharmony_ci .priv_size = sizeof(struct sfb_sched_data), 7048c2ecf20Sopenharmony_ci .cl_ops = &sfb_class_ops, 7058c2ecf20Sopenharmony_ci .enqueue = sfb_enqueue, 7068c2ecf20Sopenharmony_ci .dequeue = sfb_dequeue, 7078c2ecf20Sopenharmony_ci .peek = sfb_peek, 7088c2ecf20Sopenharmony_ci .init = sfb_init, 7098c2ecf20Sopenharmony_ci .reset = sfb_reset, 7108c2ecf20Sopenharmony_ci .destroy = sfb_destroy, 7118c2ecf20Sopenharmony_ci .change = sfb_change, 7128c2ecf20Sopenharmony_ci .dump = sfb_dump, 7138c2ecf20Sopenharmony_ci .dump_stats = sfb_dump_stats, 7148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7158c2ecf20Sopenharmony_ci}; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int __init sfb_module_init(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci return register_qdisc(&sfb_qdisc_ops); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic void __exit sfb_module_exit(void) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci unregister_qdisc(&sfb_qdisc_ops); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cimodule_init(sfb_module_init) 7288c2ecf20Sopenharmony_cimodule_exit(sfb_module_exit) 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Stochastic Fair Blue queue discipline"); 7318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juliusz Chroboczek"); 7328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Dumazet"); 7338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 734