1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
245e14433Sstephen hemminger /*
345e14433Sstephen hemminger * net/sched/sch_choke.c CHOKE scheduler
445e14433Sstephen hemminger *
545e14433Sstephen hemminger * Copyright (c) 2011 Stephen Hemminger <shemminger@vyatta.com>
645e14433Sstephen hemminger * Copyright (c) 2011 Eric Dumazet <eric.dumazet@gmail.com>
745e14433Sstephen hemminger */
845e14433Sstephen hemminger
945e14433Sstephen hemminger #include <linux/module.h>
1045e14433Sstephen hemminger #include <linux/types.h>
1145e14433Sstephen hemminger #include <linux/kernel.h>
1245e14433Sstephen hemminger #include <linux/skbuff.h>
13cdfb74d4SDavid S. Miller #include <linux/vmalloc.h>
1445e14433Sstephen hemminger #include <net/pkt_sched.h>
15cf1facdaSJiri Pirko #include <net/pkt_cls.h>
1645e14433Sstephen hemminger #include <net/inet_ecn.h>
1745e14433Sstephen hemminger #include <net/red.h>
181bd758ebSJiri Pirko #include <net/flow_dissector.h>
1945e14433Sstephen hemminger
2045e14433Sstephen hemminger /*
2145e14433Sstephen hemminger CHOKe stateless AQM for fair bandwidth allocation
2245e14433Sstephen hemminger =================================================
2345e14433Sstephen hemminger
2445e14433Sstephen hemminger CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for
2545e14433Sstephen hemminger unresponsive flows) is a variant of RED that penalizes misbehaving flows but
2645e14433Sstephen hemminger maintains no flow state. The difference from RED is an additional step
2745e14433Sstephen hemminger during the enqueuing process. If average queue size is over the
2845e14433Sstephen hemminger low threshold (qmin), a packet is chosen at random from the queue.
2945e14433Sstephen hemminger If both the new and chosen packet are from the same flow, both
3045e14433Sstephen hemminger are dropped. Unlike RED, CHOKe is not really a "classful" qdisc because it
3145e14433Sstephen hemminger needs to access packets in queue randomly. It has a minimal class
3245e14433Sstephen hemminger interface to allow overriding the builtin flow classifier with
3345e14433Sstephen hemminger filters.
3445e14433Sstephen hemminger
3545e14433Sstephen hemminger Source:
3645e14433Sstephen hemminger R. Pan, B. Prabhakar, and K. Psounis, "CHOKe, A Stateless
3745e14433Sstephen hemminger Active Queue Management Scheme for Approximating Fair Bandwidth Allocation",
3845e14433Sstephen hemminger IEEE INFOCOM, 2000.
3945e14433Sstephen hemminger
4045e14433Sstephen hemminger A. Tang, J. Wang, S. Low, "Understanding CHOKe: Throughput and Spatial
4145e14433Sstephen hemminger Characteristics", IEEE/ACM Transactions on Networking, 2004
4245e14433Sstephen hemminger
4345e14433Sstephen hemminger */
4445e14433Sstephen hemminger
4545e14433Sstephen hemminger /* Upper bound on size of sk_buff table (packets) */
4645e14433Sstephen hemminger #define CHOKE_MAX_QUEUE (128*1024 - 1)
4745e14433Sstephen hemminger
4845e14433Sstephen hemminger struct choke_sched_data {
4945e14433Sstephen hemminger /* Parameters */
5045e14433Sstephen hemminger u32 limit;
5145e14433Sstephen hemminger unsigned char flags;
5245e14433Sstephen hemminger
5345e14433Sstephen hemminger struct red_parms parms;
5445e14433Sstephen hemminger
5545e14433Sstephen hemminger /* Variables */
56eeca6688SEric Dumazet struct red_vars vars;
5745e14433Sstephen hemminger struct {
5845e14433Sstephen hemminger u32 prob_drop; /* Early probability drops */
5945e14433Sstephen hemminger u32 prob_mark; /* Early probability marks */
6045e14433Sstephen hemminger u32 forced_drop; /* Forced drops, qavg > max_thresh */
6145e14433Sstephen hemminger u32 forced_mark; /* Forced marks, qavg > max_thresh */
6245e14433Sstephen hemminger u32 pdrop; /* Drops due to queue limits */
6345e14433Sstephen hemminger u32 matched; /* Drops to flow match */
6445e14433Sstephen hemminger } stats;
6545e14433Sstephen hemminger
6645e14433Sstephen hemminger unsigned int head;
6745e14433Sstephen hemminger unsigned int tail;
6845e14433Sstephen hemminger
6945e14433Sstephen hemminger unsigned int tab_mask; /* size - 1 */
7045e14433Sstephen hemminger
7145e14433Sstephen hemminger struct sk_buff **tab;
7245e14433Sstephen hemminger };
7345e14433Sstephen hemminger
7445e14433Sstephen hemminger /* number of elements in queue including holes */
choke_len(const struct choke_sched_data * q)7545e14433Sstephen hemminger static unsigned int choke_len(const struct choke_sched_data *q)
7645e14433Sstephen hemminger {
7745e14433Sstephen hemminger return (q->tail - q->head) & q->tab_mask;
7845e14433Sstephen hemminger }
7945e14433Sstephen hemminger
8045e14433Sstephen hemminger /* Is ECN parameter configured */
use_ecn(const struct choke_sched_data * q)8145e14433Sstephen hemminger static int use_ecn(const struct choke_sched_data *q)
8245e14433Sstephen hemminger {
8345e14433Sstephen hemminger return q->flags & TC_RED_ECN;
8445e14433Sstephen hemminger }
8545e14433Sstephen hemminger
8645e14433Sstephen hemminger /* Should packets over max just be dropped (versus marked) */
use_harddrop(const struct choke_sched_data * q)8745e14433Sstephen hemminger static int use_harddrop(const struct choke_sched_data *q)
8845e14433Sstephen hemminger {
8945e14433Sstephen hemminger return q->flags & TC_RED_HARDDROP;
9045e14433Sstephen hemminger }
9145e14433Sstephen hemminger
9245e14433Sstephen hemminger /* Move head pointer forward to skip over holes */
choke_zap_head_holes(struct choke_sched_data * q)9345e14433Sstephen hemminger static void choke_zap_head_holes(struct choke_sched_data *q)
9445e14433Sstephen hemminger {
9545e14433Sstephen hemminger do {
9645e14433Sstephen hemminger q->head = (q->head + 1) & q->tab_mask;
9745e14433Sstephen hemminger if (q->head == q->tail)
9845e14433Sstephen hemminger break;
9945e14433Sstephen hemminger } while (q->tab[q->head] == NULL);
10045e14433Sstephen hemminger }
10145e14433Sstephen hemminger
10245e14433Sstephen hemminger /* Move tail pointer backwards to reuse holes */
choke_zap_tail_holes(struct choke_sched_data * q)10345e14433Sstephen hemminger static void choke_zap_tail_holes(struct choke_sched_data *q)
10445e14433Sstephen hemminger {
10545e14433Sstephen hemminger do {
10645e14433Sstephen hemminger q->tail = (q->tail - 1) & q->tab_mask;
10745e14433Sstephen hemminger if (q->head == q->tail)
10845e14433Sstephen hemminger break;
10945e14433Sstephen hemminger } while (q->tab[q->tail] == NULL);
11045e14433Sstephen hemminger }
11145e14433Sstephen hemminger
11245e14433Sstephen hemminger /* Drop packet from queue array by creating a "hole" */
choke_drop_by_idx(struct Qdisc * sch,unsigned int idx,struct sk_buff ** to_free)113520ac30fSEric Dumazet static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx,
114520ac30fSEric Dumazet struct sk_buff **to_free)
11545e14433Sstephen hemminger {
11645e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
11745e14433Sstephen hemminger struct sk_buff *skb = q->tab[idx];
11845e14433Sstephen hemminger
11945e14433Sstephen hemminger q->tab[idx] = NULL;
12045e14433Sstephen hemminger
12145e14433Sstephen hemminger if (idx == q->head)
12245e14433Sstephen hemminger choke_zap_head_holes(q);
12345e14433Sstephen hemminger if (idx == q->tail)
12445e14433Sstephen hemminger choke_zap_tail_holes(q);
12545e14433Sstephen hemminger
126*97e13434SLion Ackermann --sch->q.qlen;
12725331d6cSJohn Fastabend qdisc_qstats_backlog_dec(sch, skb);
1282ccccf5fSWANG Cong qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb));
129520ac30fSEric Dumazet qdisc_drop(skb, sch, to_free);
13045e14433Sstephen hemminger }
13145e14433Sstephen hemminger
13226f70e12SEric Dumazet struct choke_skb_cb {
1332bcc34bbSEric Dumazet u8 keys_valid;
1342e99403dSTom Herbert struct flow_keys_digest keys;
13526f70e12SEric Dumazet };
13626f70e12SEric Dumazet
choke_skb_cb(const struct sk_buff * skb)13726f70e12SEric Dumazet static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
13826f70e12SEric Dumazet {
13916bda13dSDavid S. Miller qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb));
14026f70e12SEric Dumazet return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data;
14126f70e12SEric Dumazet }
14226f70e12SEric Dumazet
14345e14433Sstephen hemminger /*
1442bcc34bbSEric Dumazet * Compare flow of two packets
1452bcc34bbSEric Dumazet * Returns true only if source and destination address and port match.
1462bcc34bbSEric Dumazet * false for special cases
1472bcc34bbSEric Dumazet */
choke_match_flow(struct sk_buff * skb1,struct sk_buff * skb2)1482bcc34bbSEric Dumazet static bool choke_match_flow(struct sk_buff *skb1,
1492bcc34bbSEric Dumazet struct sk_buff *skb2)
1502bcc34bbSEric Dumazet {
15125711786SEric Dumazet struct flow_keys temp;
15225711786SEric Dumazet
1532bcc34bbSEric Dumazet if (skb1->protocol != skb2->protocol)
1542bcc34bbSEric Dumazet return false;
1552bcc34bbSEric Dumazet
1562bcc34bbSEric Dumazet if (!choke_skb_cb(skb1)->keys_valid) {
1572bcc34bbSEric Dumazet choke_skb_cb(skb1)->keys_valid = 1;
158cd79a238STom Herbert skb_flow_dissect_flow_keys(skb1, &temp, 0);
1592e99403dSTom Herbert make_flow_keys_digest(&choke_skb_cb(skb1)->keys, &temp);
1602bcc34bbSEric Dumazet }
1612bcc34bbSEric Dumazet
1622bcc34bbSEric Dumazet if (!choke_skb_cb(skb2)->keys_valid) {
1632bcc34bbSEric Dumazet choke_skb_cb(skb2)->keys_valid = 1;
164cd79a238STom Herbert skb_flow_dissect_flow_keys(skb2, &temp, 0);
1652e99403dSTom Herbert make_flow_keys_digest(&choke_skb_cb(skb2)->keys, &temp);
1662bcc34bbSEric Dumazet }
1672bcc34bbSEric Dumazet
1682bcc34bbSEric Dumazet return !memcmp(&choke_skb_cb(skb1)->keys,
1692bcc34bbSEric Dumazet &choke_skb_cb(skb2)->keys,
1702e99403dSTom Herbert sizeof(choke_skb_cb(skb1)->keys));
1712bcc34bbSEric Dumazet }
1722bcc34bbSEric Dumazet
1732bcc34bbSEric Dumazet /*
17445e14433Sstephen hemminger * Select a packet at random from queue
17545e14433Sstephen hemminger * HACK: since queue can have holes from previous deletion; retry several
17645e14433Sstephen hemminger * times to find a random skb but then just give up and return the head
17745e14433Sstephen hemminger * Will return NULL if queue is empty (q->head == q->tail)
17845e14433Sstephen hemminger */
choke_peek_random(const struct choke_sched_data * q,unsigned int * pidx)17945e14433Sstephen hemminger static struct sk_buff *choke_peek_random(const struct choke_sched_data *q,
18045e14433Sstephen hemminger unsigned int *pidx)
18145e14433Sstephen hemminger {
18245e14433Sstephen hemminger struct sk_buff *skb;
18345e14433Sstephen hemminger int retrys = 3;
18445e14433Sstephen hemminger
18545e14433Sstephen hemminger do {
1868032bf12SJason A. Donenfeld *pidx = (q->head + get_random_u32_below(choke_len(q))) & q->tab_mask;
18745e14433Sstephen hemminger skb = q->tab[*pidx];
18845e14433Sstephen hemminger if (skb)
18945e14433Sstephen hemminger return skb;
19045e14433Sstephen hemminger } while (--retrys > 0);
19145e14433Sstephen hemminger
19245e14433Sstephen hemminger return q->tab[*pidx = q->head];
19345e14433Sstephen hemminger }
19445e14433Sstephen hemminger
19545e14433Sstephen hemminger /*
19645e14433Sstephen hemminger * Compare new packet with random packet in queue
19745e14433Sstephen hemminger * returns true if matched and sets *pidx
19845e14433Sstephen hemminger */
choke_match_random(const struct choke_sched_data * q,struct sk_buff * nskb,unsigned int * pidx)19945e14433Sstephen hemminger static bool choke_match_random(const struct choke_sched_data *q,
20045e14433Sstephen hemminger struct sk_buff *nskb,
20145e14433Sstephen hemminger unsigned int *pidx)
20245e14433Sstephen hemminger {
20345e14433Sstephen hemminger struct sk_buff *oskb;
20445e14433Sstephen hemminger
20545e14433Sstephen hemminger if (q->head == q->tail)
20645e14433Sstephen hemminger return false;
20745e14433Sstephen hemminger
20845e14433Sstephen hemminger oskb = choke_peek_random(q, pidx);
20945e14433Sstephen hemminger return choke_match_flow(oskb, nskb);
21045e14433Sstephen hemminger }
21145e14433Sstephen hemminger
choke_enqueue(struct sk_buff * skb,struct Qdisc * sch,struct sk_buff ** to_free)212520ac30fSEric Dumazet static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch,
213520ac30fSEric Dumazet struct sk_buff **to_free)
21445e14433Sstephen hemminger {
21545e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
216eeca6688SEric Dumazet const struct red_parms *p = &q->parms;
21745e14433Sstephen hemminger
2182bcc34bbSEric Dumazet choke_skb_cb(skb)->keys_valid = 0;
21945e14433Sstephen hemminger /* Compute average queue usage (see RED) */
220eeca6688SEric Dumazet q->vars.qavg = red_calc_qavg(p, &q->vars, sch->q.qlen);
221eeca6688SEric Dumazet if (red_is_idling(&q->vars))
222eeca6688SEric Dumazet red_end_of_idle_period(&q->vars);
22345e14433Sstephen hemminger
22445e14433Sstephen hemminger /* Is queue small? */
225eeca6688SEric Dumazet if (q->vars.qavg <= p->qth_min)
226eeca6688SEric Dumazet q->vars.qcount = -1;
22745e14433Sstephen hemminger else {
22845e14433Sstephen hemminger unsigned int idx;
22945e14433Sstephen hemminger
23045e14433Sstephen hemminger /* Draw a packet at random from queue and compare flow */
23145e14433Sstephen hemminger if (choke_match_random(q, skb, &idx)) {
23245e14433Sstephen hemminger q->stats.matched++;
233520ac30fSEric Dumazet choke_drop_by_idx(sch, idx, to_free);
23445e14433Sstephen hemminger goto congestion_drop;
23545e14433Sstephen hemminger }
23645e14433Sstephen hemminger
23745e14433Sstephen hemminger /* Queue is large, always mark/drop */
238eeca6688SEric Dumazet if (q->vars.qavg > p->qth_max) {
239eeca6688SEric Dumazet q->vars.qcount = -1;
24045e14433Sstephen hemminger
24125331d6cSJohn Fastabend qdisc_qstats_overlimit(sch);
24245e14433Sstephen hemminger if (use_harddrop(q) || !use_ecn(q) ||
24345e14433Sstephen hemminger !INET_ECN_set_ce(skb)) {
24445e14433Sstephen hemminger q->stats.forced_drop++;
24545e14433Sstephen hemminger goto congestion_drop;
24645e14433Sstephen hemminger }
24745e14433Sstephen hemminger
24845e14433Sstephen hemminger q->stats.forced_mark++;
249eeca6688SEric Dumazet } else if (++q->vars.qcount) {
250eeca6688SEric Dumazet if (red_mark_probability(p, &q->vars, q->vars.qavg)) {
251eeca6688SEric Dumazet q->vars.qcount = 0;
252eeca6688SEric Dumazet q->vars.qR = red_random(p);
25345e14433Sstephen hemminger
25425331d6cSJohn Fastabend qdisc_qstats_overlimit(sch);
25545e14433Sstephen hemminger if (!use_ecn(q) || !INET_ECN_set_ce(skb)) {
25645e14433Sstephen hemminger q->stats.prob_drop++;
25745e14433Sstephen hemminger goto congestion_drop;
25845e14433Sstephen hemminger }
25945e14433Sstephen hemminger
26045e14433Sstephen hemminger q->stats.prob_mark++;
26145e14433Sstephen hemminger }
26245e14433Sstephen hemminger } else
263eeca6688SEric Dumazet q->vars.qR = red_random(p);
26445e14433Sstephen hemminger }
26545e14433Sstephen hemminger
26645e14433Sstephen hemminger /* Admit new packet */
26745e14433Sstephen hemminger if (sch->q.qlen < q->limit) {
26845e14433Sstephen hemminger q->tab[q->tail] = skb;
26945e14433Sstephen hemminger q->tail = (q->tail + 1) & q->tab_mask;
27045e14433Sstephen hemminger ++sch->q.qlen;
27125331d6cSJohn Fastabend qdisc_qstats_backlog_inc(sch, skb);
27245e14433Sstephen hemminger return NET_XMIT_SUCCESS;
27345e14433Sstephen hemminger }
27445e14433Sstephen hemminger
27545e14433Sstephen hemminger q->stats.pdrop++;
276520ac30fSEric Dumazet return qdisc_drop(skb, sch, to_free);
27745e14433Sstephen hemminger
27845e14433Sstephen hemminger congestion_drop:
279520ac30fSEric Dumazet qdisc_drop(skb, sch, to_free);
28045e14433Sstephen hemminger return NET_XMIT_CN;
28145e14433Sstephen hemminger }
28245e14433Sstephen hemminger
choke_dequeue(struct Qdisc * sch)28345e14433Sstephen hemminger static struct sk_buff *choke_dequeue(struct Qdisc *sch)
28445e14433Sstephen hemminger {
28545e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
28645e14433Sstephen hemminger struct sk_buff *skb;
28745e14433Sstephen hemminger
28845e14433Sstephen hemminger if (q->head == q->tail) {
289eeca6688SEric Dumazet if (!red_is_idling(&q->vars))
290eeca6688SEric Dumazet red_start_of_idle_period(&q->vars);
29145e14433Sstephen hemminger return NULL;
29245e14433Sstephen hemminger }
29345e14433Sstephen hemminger
29445e14433Sstephen hemminger skb = q->tab[q->head];
29545e14433Sstephen hemminger q->tab[q->head] = NULL;
29645e14433Sstephen hemminger choke_zap_head_holes(q);
29745e14433Sstephen hemminger --sch->q.qlen;
29825331d6cSJohn Fastabend qdisc_qstats_backlog_dec(sch, skb);
29945e14433Sstephen hemminger qdisc_bstats_update(sch, skb);
30045e14433Sstephen hemminger
30145e14433Sstephen hemminger return skb;
30245e14433Sstephen hemminger }
30345e14433Sstephen hemminger
choke_reset(struct Qdisc * sch)30445e14433Sstephen hemminger static void choke_reset(struct Qdisc *sch)
30545e14433Sstephen hemminger {
30645e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
30745e14433Sstephen hemminger
30877e62da6SWANG Cong while (q->head != q->tail) {
30977e62da6SWANG Cong struct sk_buff *skb = q->tab[q->head];
31077e62da6SWANG Cong
31177e62da6SWANG Cong q->head = (q->head + 1) & q->tab_mask;
31277e62da6SWANG Cong if (!skb)
31377e62da6SWANG Cong continue;
314f9aed311SEric Dumazet rtnl_qdisc_drop(skb, sch);
31577e62da6SWANG Cong }
31677e62da6SWANG Cong
3178738c85cSEric Dumazet if (q->tab)
31877e62da6SWANG Cong memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *));
31977e62da6SWANG Cong q->head = q->tail = 0;
320eeca6688SEric Dumazet red_restart(&q->vars);
32145e14433Sstephen hemminger }
32245e14433Sstephen hemminger
32345e14433Sstephen hemminger static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {
32445e14433Sstephen hemminger [TCA_CHOKE_PARMS] = { .len = sizeof(struct tc_red_qopt) },
32545e14433Sstephen hemminger [TCA_CHOKE_STAB] = { .len = RED_STAB_SIZE },
326a73ed26bSEric Dumazet [TCA_CHOKE_MAX_P] = { .type = NLA_U32 },
32745e14433Sstephen hemminger };
32845e14433Sstephen hemminger
32945e14433Sstephen hemminger
choke_free(void * addr)33045e14433Sstephen hemminger static void choke_free(void *addr)
33145e14433Sstephen hemminger {
3324cb28970SWANG Cong kvfree(addr);
33345e14433Sstephen hemminger }
33445e14433Sstephen hemminger
choke_change(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)3352030721cSAlexander Aring static int choke_change(struct Qdisc *sch, struct nlattr *opt,
3362030721cSAlexander Aring struct netlink_ext_ack *extack)
33745e14433Sstephen hemminger {
33845e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
33945e14433Sstephen hemminger struct nlattr *tb[TCA_CHOKE_MAX + 1];
34045e14433Sstephen hemminger const struct tc_red_qopt *ctl;
34145e14433Sstephen hemminger int err;
34245e14433Sstephen hemminger struct sk_buff **old = NULL;
34345e14433Sstephen hemminger unsigned int mask;
344a73ed26bSEric Dumazet u32 max_P;
345e323d865SEric Dumazet u8 *stab;
34645e14433Sstephen hemminger
34745e14433Sstephen hemminger if (opt == NULL)
34845e14433Sstephen hemminger return -EINVAL;
34945e14433Sstephen hemminger
3508cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_CHOKE_MAX, opt,
3518cb08174SJohannes Berg choke_policy, NULL);
35245e14433Sstephen hemminger if (err < 0)
35345e14433Sstephen hemminger return err;
35445e14433Sstephen hemminger
35545e14433Sstephen hemminger if (tb[TCA_CHOKE_PARMS] == NULL ||
35645e14433Sstephen hemminger tb[TCA_CHOKE_STAB] == NULL)
35745e14433Sstephen hemminger return -EINVAL;
35845e14433Sstephen hemminger
359a73ed26bSEric Dumazet max_P = tb[TCA_CHOKE_MAX_P] ? nla_get_u32(tb[TCA_CHOKE_MAX_P]) : 0;
360a73ed26bSEric Dumazet
36145e14433Sstephen hemminger ctl = nla_data(tb[TCA_CHOKE_PARMS]);
362e323d865SEric Dumazet stab = nla_data(tb[TCA_CHOKE_STAB]);
363e323d865SEric Dumazet if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log, stab))
3648afa10cbSNogah Frankel return -EINVAL;
3658afa10cbSNogah Frankel
36645e14433Sstephen hemminger if (ctl->limit > CHOKE_MAX_QUEUE)
36745e14433Sstephen hemminger return -EINVAL;
36845e14433Sstephen hemminger
36945e14433Sstephen hemminger mask = roundup_pow_of_two(ctl->limit + 1) - 1;
37045e14433Sstephen hemminger if (mask != q->tab_mask) {
37145e14433Sstephen hemminger struct sk_buff **ntab;
37245e14433Sstephen hemminger
373793da4bfSJoe Perches ntab = kvcalloc(mask + 1, sizeof(struct sk_buff *), GFP_KERNEL);
37445e14433Sstephen hemminger if (!ntab)
37545e14433Sstephen hemminger return -ENOMEM;
37645e14433Sstephen hemminger
37745e14433Sstephen hemminger sch_tree_lock(sch);
37845e14433Sstephen hemminger old = q->tab;
37945e14433Sstephen hemminger if (old) {
38045e14433Sstephen hemminger unsigned int oqlen = sch->q.qlen, tail = 0;
3812ccccf5fSWANG Cong unsigned dropped = 0;
38245e14433Sstephen hemminger
38345e14433Sstephen hemminger while (q->head != q->tail) {
38445e14433Sstephen hemminger struct sk_buff *skb = q->tab[q->head];
38545e14433Sstephen hemminger
38645e14433Sstephen hemminger q->head = (q->head + 1) & q->tab_mask;
38745e14433Sstephen hemminger if (!skb)
38845e14433Sstephen hemminger continue;
38945e14433Sstephen hemminger if (tail < mask) {
39045e14433Sstephen hemminger ntab[tail++] = skb;
39145e14433Sstephen hemminger continue;
39245e14433Sstephen hemminger }
3932ccccf5fSWANG Cong dropped += qdisc_pkt_len(skb);
39425331d6cSJohn Fastabend qdisc_qstats_backlog_dec(sch, skb);
39545e14433Sstephen hemminger --sch->q.qlen;
396f9aed311SEric Dumazet rtnl_qdisc_drop(skb, sch);
39745e14433Sstephen hemminger }
3982ccccf5fSWANG Cong qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped);
39945e14433Sstephen hemminger q->head = 0;
40045e14433Sstephen hemminger q->tail = tail;
40145e14433Sstephen hemminger }
40245e14433Sstephen hemminger
40345e14433Sstephen hemminger q->tab_mask = mask;
40445e14433Sstephen hemminger q->tab = ntab;
40545e14433Sstephen hemminger } else
40645e14433Sstephen hemminger sch_tree_lock(sch);
40745e14433Sstephen hemminger
40845e14433Sstephen hemminger q->flags = ctl->flags;
40945e14433Sstephen hemminger q->limit = ctl->limit;
41045e14433Sstephen hemminger
41145e14433Sstephen hemminger red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
41245e14433Sstephen hemminger ctl->Plog, ctl->Scell_log,
413e323d865SEric Dumazet stab,
414a73ed26bSEric Dumazet max_P);
415eeca6688SEric Dumazet red_set_vars(&q->vars);
41645e14433Sstephen hemminger
41745e14433Sstephen hemminger if (q->head == q->tail)
418eeca6688SEric Dumazet red_end_of_idle_period(&q->vars);
41945e14433Sstephen hemminger
42045e14433Sstephen hemminger sch_tree_unlock(sch);
42145e14433Sstephen hemminger choke_free(old);
42245e14433Sstephen hemminger return 0;
42345e14433Sstephen hemminger }
42445e14433Sstephen hemminger
choke_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)425e63d7dfdSAlexander Aring static int choke_init(struct Qdisc *sch, struct nlattr *opt,
426e63d7dfdSAlexander Aring struct netlink_ext_ack *extack)
42745e14433Sstephen hemminger {
4282030721cSAlexander Aring return choke_change(sch, opt, extack);
42945e14433Sstephen hemminger }
43045e14433Sstephen hemminger
choke_dump(struct Qdisc * sch,struct sk_buff * skb)43145e14433Sstephen hemminger static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)
43245e14433Sstephen hemminger {
43345e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
43445e14433Sstephen hemminger struct nlattr *opts = NULL;
43545e14433Sstephen hemminger struct tc_red_qopt opt = {
43645e14433Sstephen hemminger .limit = q->limit,
43745e14433Sstephen hemminger .flags = q->flags,
43845e14433Sstephen hemminger .qth_min = q->parms.qth_min >> q->parms.Wlog,
43945e14433Sstephen hemminger .qth_max = q->parms.qth_max >> q->parms.Wlog,
44045e14433Sstephen hemminger .Wlog = q->parms.Wlog,
44145e14433Sstephen hemminger .Plog = q->parms.Plog,
44245e14433Sstephen hemminger .Scell_log = q->parms.Scell_log,
44345e14433Sstephen hemminger };
44445e14433Sstephen hemminger
445ae0be8deSMichal Kubecek opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
44645e14433Sstephen hemminger if (opts == NULL)
44745e14433Sstephen hemminger goto nla_put_failure;
44845e14433Sstephen hemminger
4491b34ec43SDavid S. Miller if (nla_put(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt) ||
4501b34ec43SDavid S. Miller nla_put_u32(skb, TCA_CHOKE_MAX_P, q->parms.max_P))
4511b34ec43SDavid S. Miller goto nla_put_failure;
45245e14433Sstephen hemminger return nla_nest_end(skb, opts);
45345e14433Sstephen hemminger
45445e14433Sstephen hemminger nla_put_failure:
45545e14433Sstephen hemminger nla_nest_cancel(skb, opts);
45645e14433Sstephen hemminger return -EMSGSIZE;
45745e14433Sstephen hemminger }
45845e14433Sstephen hemminger
choke_dump_stats(struct Qdisc * sch,struct gnet_dump * d)45945e14433Sstephen hemminger static int choke_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
46045e14433Sstephen hemminger {
46145e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
46245e14433Sstephen hemminger struct tc_choke_xstats st = {
46345e14433Sstephen hemminger .early = q->stats.prob_drop + q->stats.forced_drop,
46445e14433Sstephen hemminger .marked = q->stats.prob_mark + q->stats.forced_mark,
46545e14433Sstephen hemminger .pdrop = q->stats.pdrop,
46645e14433Sstephen hemminger .matched = q->stats.matched,
46745e14433Sstephen hemminger };
46845e14433Sstephen hemminger
46945e14433Sstephen hemminger return gnet_stats_copy_app(d, &st, sizeof(st));
47045e14433Sstephen hemminger }
47145e14433Sstephen hemminger
choke_destroy(struct Qdisc * sch)47245e14433Sstephen hemminger static void choke_destroy(struct Qdisc *sch)
47345e14433Sstephen hemminger {
47445e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
47545e14433Sstephen hemminger
47645e14433Sstephen hemminger choke_free(q->tab);
47745e14433Sstephen hemminger }
47845e14433Sstephen hemminger
choke_peek_head(struct Qdisc * sch)47945e14433Sstephen hemminger static struct sk_buff *choke_peek_head(struct Qdisc *sch)
48045e14433Sstephen hemminger {
48145e14433Sstephen hemminger struct choke_sched_data *q = qdisc_priv(sch);
48245e14433Sstephen hemminger
48345e14433Sstephen hemminger return (q->head != q->tail) ? q->tab[q->head] : NULL;
48445e14433Sstephen hemminger }
48545e14433Sstephen hemminger
48645e14433Sstephen hemminger static struct Qdisc_ops choke_qdisc_ops __read_mostly = {
48745e14433Sstephen hemminger .id = "choke",
48845e14433Sstephen hemminger .priv_size = sizeof(struct choke_sched_data),
48945e14433Sstephen hemminger
49045e14433Sstephen hemminger .enqueue = choke_enqueue,
49145e14433Sstephen hemminger .dequeue = choke_dequeue,
49245e14433Sstephen hemminger .peek = choke_peek_head,
49345e14433Sstephen hemminger .init = choke_init,
49445e14433Sstephen hemminger .destroy = choke_destroy,
49545e14433Sstephen hemminger .reset = choke_reset,
49645e14433Sstephen hemminger .change = choke_change,
49745e14433Sstephen hemminger .dump = choke_dump,
49845e14433Sstephen hemminger .dump_stats = choke_dump_stats,
49945e14433Sstephen hemminger .owner = THIS_MODULE,
50045e14433Sstephen hemminger };
50145e14433Sstephen hemminger
choke_module_init(void)50245e14433Sstephen hemminger static int __init choke_module_init(void)
50345e14433Sstephen hemminger {
50445e14433Sstephen hemminger return register_qdisc(&choke_qdisc_ops);
50545e14433Sstephen hemminger }
50645e14433Sstephen hemminger
choke_module_exit(void)50745e14433Sstephen hemminger static void __exit choke_module_exit(void)
50845e14433Sstephen hemminger {
50945e14433Sstephen hemminger unregister_qdisc(&choke_qdisc_ops);
51045e14433Sstephen hemminger }
51145e14433Sstephen hemminger
51245e14433Sstephen hemminger module_init(choke_module_init)
51345e14433Sstephen hemminger module_exit(choke_module_exit)
51445e14433Sstephen hemminger
51545e14433Sstephen hemminger MODULE_LICENSE("GPL");
516