xref: /openbmc/linux/net/sched/sch_choke.c (revision 55e43d6abd078ed6d219902ce8cb4d68e3c993ba)
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