xref: /OK3568_Linux_fs/kernel/net/sched/sch_fq_codel.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Fair Queue CoDel discipline
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/jiffies.h>
12*4882a593Smuzhiyun #include <linux/string.h>
13*4882a593Smuzhiyun #include <linux/in.h>
14*4882a593Smuzhiyun #include <linux/errno.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/skbuff.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <linux/vmalloc.h>
19*4882a593Smuzhiyun #include <net/netlink.h>
20*4882a593Smuzhiyun #include <net/pkt_sched.h>
21*4882a593Smuzhiyun #include <net/pkt_cls.h>
22*4882a593Smuzhiyun #include <net/codel.h>
23*4882a593Smuzhiyun #include <net/codel_impl.h>
24*4882a593Smuzhiyun #include <net/codel_qdisc.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /*	Fair Queue CoDel.
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * Principles :
29*4882a593Smuzhiyun  * Packets are classified (internal classifier or external) on flows.
30*4882a593Smuzhiyun  * This is a Stochastic model (as we use a hash, several flows
31*4882a593Smuzhiyun  *			       might be hashed on same slot)
32*4882a593Smuzhiyun  * Each flow has a CoDel managed queue.
33*4882a593Smuzhiyun  * Flows are linked onto two (Round Robin) lists,
34*4882a593Smuzhiyun  * so that new flows have priority on old ones.
35*4882a593Smuzhiyun  *
36*4882a593Smuzhiyun  * For a given flow, packets are not reordered (CoDel uses a FIFO)
37*4882a593Smuzhiyun  * head drops only.
38*4882a593Smuzhiyun  * ECN capability is on by default.
39*4882a593Smuzhiyun  * Low memory footprint (64 bytes per flow)
40*4882a593Smuzhiyun  */
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun struct fq_codel_flow {
43*4882a593Smuzhiyun 	struct sk_buff	  *head;
44*4882a593Smuzhiyun 	struct sk_buff	  *tail;
45*4882a593Smuzhiyun 	struct list_head  flowchain;
46*4882a593Smuzhiyun 	int		  deficit;
47*4882a593Smuzhiyun 	struct codel_vars cvars;
48*4882a593Smuzhiyun }; /* please try to keep this structure <= 64 bytes */
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun struct fq_codel_sched_data {
51*4882a593Smuzhiyun 	struct tcf_proto __rcu *filter_list; /* optional external classifier */
52*4882a593Smuzhiyun 	struct tcf_block *block;
53*4882a593Smuzhiyun 	struct fq_codel_flow *flows;	/* Flows table [flows_cnt] */
54*4882a593Smuzhiyun 	u32		*backlogs;	/* backlog table [flows_cnt] */
55*4882a593Smuzhiyun 	u32		flows_cnt;	/* number of flows */
56*4882a593Smuzhiyun 	u32		quantum;	/* psched_mtu(qdisc_dev(sch)); */
57*4882a593Smuzhiyun 	u32		drop_batch_size;
58*4882a593Smuzhiyun 	u32		memory_limit;
59*4882a593Smuzhiyun 	struct codel_params cparams;
60*4882a593Smuzhiyun 	struct codel_stats cstats;
61*4882a593Smuzhiyun 	u32		memory_usage;
62*4882a593Smuzhiyun 	u32		drop_overmemory;
63*4882a593Smuzhiyun 	u32		drop_overlimit;
64*4882a593Smuzhiyun 	u32		new_flow_count;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	struct list_head new_flows;	/* list of new flows */
67*4882a593Smuzhiyun 	struct list_head old_flows;	/* list of old flows */
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun 
fq_codel_hash(const struct fq_codel_sched_data * q,struct sk_buff * skb)70*4882a593Smuzhiyun static unsigned int fq_codel_hash(const struct fq_codel_sched_data *q,
71*4882a593Smuzhiyun 				  struct sk_buff *skb)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	return reciprocal_scale(skb_get_hash(skb), q->flows_cnt);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
fq_codel_classify(struct sk_buff * skb,struct Qdisc * sch,int * qerr)76*4882a593Smuzhiyun static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
77*4882a593Smuzhiyun 				      int *qerr)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
80*4882a593Smuzhiyun 	struct tcf_proto *filter;
81*4882a593Smuzhiyun 	struct tcf_result res;
82*4882a593Smuzhiyun 	int result;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (TC_H_MAJ(skb->priority) == sch->handle &&
85*4882a593Smuzhiyun 	    TC_H_MIN(skb->priority) > 0 &&
86*4882a593Smuzhiyun 	    TC_H_MIN(skb->priority) <= q->flows_cnt)
87*4882a593Smuzhiyun 		return TC_H_MIN(skb->priority);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	filter = rcu_dereference_bh(q->filter_list);
90*4882a593Smuzhiyun 	if (!filter)
91*4882a593Smuzhiyun 		return fq_codel_hash(q, skb) + 1;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
94*4882a593Smuzhiyun 	result = tcf_classify(skb, filter, &res, false);
95*4882a593Smuzhiyun 	if (result >= 0) {
96*4882a593Smuzhiyun #ifdef CONFIG_NET_CLS_ACT
97*4882a593Smuzhiyun 		switch (result) {
98*4882a593Smuzhiyun 		case TC_ACT_STOLEN:
99*4882a593Smuzhiyun 		case TC_ACT_QUEUED:
100*4882a593Smuzhiyun 		case TC_ACT_TRAP:
101*4882a593Smuzhiyun 			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
102*4882a593Smuzhiyun 			fallthrough;
103*4882a593Smuzhiyun 		case TC_ACT_SHOT:
104*4882a593Smuzhiyun 			return 0;
105*4882a593Smuzhiyun 		}
106*4882a593Smuzhiyun #endif
107*4882a593Smuzhiyun 		if (TC_H_MIN(res.classid) <= q->flows_cnt)
108*4882a593Smuzhiyun 			return TC_H_MIN(res.classid);
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 	return 0;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun /* helper functions : might be changed when/if skb use a standard list_head */
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /* remove one skb from head of slot queue */
dequeue_head(struct fq_codel_flow * flow)116*4882a593Smuzhiyun static inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	struct sk_buff *skb = flow->head;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	flow->head = skb->next;
121*4882a593Smuzhiyun 	skb_mark_not_on_list(skb);
122*4882a593Smuzhiyun 	return skb;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun /* add skb to flow queue (tail add) */
flow_queue_add(struct fq_codel_flow * flow,struct sk_buff * skb)126*4882a593Smuzhiyun static inline void flow_queue_add(struct fq_codel_flow *flow,
127*4882a593Smuzhiyun 				  struct sk_buff *skb)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	if (flow->head == NULL)
130*4882a593Smuzhiyun 		flow->head = skb;
131*4882a593Smuzhiyun 	else
132*4882a593Smuzhiyun 		flow->tail->next = skb;
133*4882a593Smuzhiyun 	flow->tail = skb;
134*4882a593Smuzhiyun 	skb->next = NULL;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
fq_codel_drop(struct Qdisc * sch,unsigned int max_packets,struct sk_buff ** to_free)137*4882a593Smuzhiyun static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets,
138*4882a593Smuzhiyun 				  struct sk_buff **to_free)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
141*4882a593Smuzhiyun 	struct sk_buff *skb;
142*4882a593Smuzhiyun 	unsigned int maxbacklog = 0, idx = 0, i, len;
143*4882a593Smuzhiyun 	struct fq_codel_flow *flow;
144*4882a593Smuzhiyun 	unsigned int threshold;
145*4882a593Smuzhiyun 	unsigned int mem = 0;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	/* Queue is full! Find the fat flow and drop packet(s) from it.
148*4882a593Smuzhiyun 	 * This might sound expensive, but with 1024 flows, we scan
149*4882a593Smuzhiyun 	 * 4KB of memory, and we dont need to handle a complex tree
150*4882a593Smuzhiyun 	 * in fast path (packet queue/enqueue) with many cache misses.
151*4882a593Smuzhiyun 	 * In stress mode, we'll try to drop 64 packets from the flow,
152*4882a593Smuzhiyun 	 * amortizing this linear lookup to one cache line per drop.
153*4882a593Smuzhiyun 	 */
154*4882a593Smuzhiyun 	for (i = 0; i < q->flows_cnt; i++) {
155*4882a593Smuzhiyun 		if (q->backlogs[i] > maxbacklog) {
156*4882a593Smuzhiyun 			maxbacklog = q->backlogs[i];
157*4882a593Smuzhiyun 			idx = i;
158*4882a593Smuzhiyun 		}
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	/* Our goal is to drop half of this fat flow backlog */
162*4882a593Smuzhiyun 	threshold = maxbacklog >> 1;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	flow = &q->flows[idx];
165*4882a593Smuzhiyun 	len = 0;
166*4882a593Smuzhiyun 	i = 0;
167*4882a593Smuzhiyun 	do {
168*4882a593Smuzhiyun 		skb = dequeue_head(flow);
169*4882a593Smuzhiyun 		len += qdisc_pkt_len(skb);
170*4882a593Smuzhiyun 		mem += get_codel_cb(skb)->mem_usage;
171*4882a593Smuzhiyun 		__qdisc_drop(skb, to_free);
172*4882a593Smuzhiyun 	} while (++i < max_packets && len < threshold);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	/* Tell codel to increase its signal strength also */
175*4882a593Smuzhiyun 	flow->cvars.count += i;
176*4882a593Smuzhiyun 	q->backlogs[idx] -= len;
177*4882a593Smuzhiyun 	q->memory_usage -= mem;
178*4882a593Smuzhiyun 	sch->qstats.drops += i;
179*4882a593Smuzhiyun 	sch->qstats.backlog -= len;
180*4882a593Smuzhiyun 	sch->q.qlen -= i;
181*4882a593Smuzhiyun 	return idx;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
fq_codel_enqueue(struct sk_buff * skb,struct Qdisc * sch,struct sk_buff ** to_free)184*4882a593Smuzhiyun static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch,
185*4882a593Smuzhiyun 			    struct sk_buff **to_free)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
188*4882a593Smuzhiyun 	unsigned int idx, prev_backlog, prev_qlen;
189*4882a593Smuzhiyun 	struct fq_codel_flow *flow;
190*4882a593Smuzhiyun 	int ret;
191*4882a593Smuzhiyun 	unsigned int pkt_len;
192*4882a593Smuzhiyun 	bool memory_limited;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	idx = fq_codel_classify(skb, sch, &ret);
195*4882a593Smuzhiyun 	if (idx == 0) {
196*4882a593Smuzhiyun 		if (ret & __NET_XMIT_BYPASS)
197*4882a593Smuzhiyun 			qdisc_qstats_drop(sch);
198*4882a593Smuzhiyun 		__qdisc_drop(skb, to_free);
199*4882a593Smuzhiyun 		return ret;
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 	idx--;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	codel_set_enqueue_time(skb);
204*4882a593Smuzhiyun 	flow = &q->flows[idx];
205*4882a593Smuzhiyun 	flow_queue_add(flow, skb);
206*4882a593Smuzhiyun 	q->backlogs[idx] += qdisc_pkt_len(skb);
207*4882a593Smuzhiyun 	qdisc_qstats_backlog_inc(sch, skb);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (list_empty(&flow->flowchain)) {
210*4882a593Smuzhiyun 		list_add_tail(&flow->flowchain, &q->new_flows);
211*4882a593Smuzhiyun 		q->new_flow_count++;
212*4882a593Smuzhiyun 		flow->deficit = q->quantum;
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 	get_codel_cb(skb)->mem_usage = skb->truesize;
215*4882a593Smuzhiyun 	q->memory_usage += get_codel_cb(skb)->mem_usage;
216*4882a593Smuzhiyun 	memory_limited = q->memory_usage > q->memory_limit;
217*4882a593Smuzhiyun 	if (++sch->q.qlen <= sch->limit && !memory_limited)
218*4882a593Smuzhiyun 		return NET_XMIT_SUCCESS;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	prev_backlog = sch->qstats.backlog;
221*4882a593Smuzhiyun 	prev_qlen = sch->q.qlen;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	/* save this packet length as it might be dropped by fq_codel_drop() */
224*4882a593Smuzhiyun 	pkt_len = qdisc_pkt_len(skb);
225*4882a593Smuzhiyun 	/* fq_codel_drop() is quite expensive, as it performs a linear search
226*4882a593Smuzhiyun 	 * in q->backlogs[] to find a fat flow.
227*4882a593Smuzhiyun 	 * So instead of dropping a single packet, drop half of its backlog
228*4882a593Smuzhiyun 	 * with a 64 packets limit to not add a too big cpu spike here.
229*4882a593Smuzhiyun 	 */
230*4882a593Smuzhiyun 	ret = fq_codel_drop(sch, q->drop_batch_size, to_free);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	prev_qlen -= sch->q.qlen;
233*4882a593Smuzhiyun 	prev_backlog -= sch->qstats.backlog;
234*4882a593Smuzhiyun 	q->drop_overlimit += prev_qlen;
235*4882a593Smuzhiyun 	if (memory_limited)
236*4882a593Smuzhiyun 		q->drop_overmemory += prev_qlen;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	/* As we dropped packet(s), better let upper stack know this.
239*4882a593Smuzhiyun 	 * If we dropped a packet for this flow, return NET_XMIT_CN,
240*4882a593Smuzhiyun 	 * but in this case, our parents wont increase their backlogs.
241*4882a593Smuzhiyun 	 */
242*4882a593Smuzhiyun 	if (ret == idx) {
243*4882a593Smuzhiyun 		qdisc_tree_reduce_backlog(sch, prev_qlen - 1,
244*4882a593Smuzhiyun 					  prev_backlog - pkt_len);
245*4882a593Smuzhiyun 		return NET_XMIT_CN;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog);
248*4882a593Smuzhiyun 	return NET_XMIT_SUCCESS;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun /* This is the specific function called from codel_dequeue()
252*4882a593Smuzhiyun  * to dequeue a packet from queue. Note: backlog is handled in
253*4882a593Smuzhiyun  * codel, we dont need to reduce it here.
254*4882a593Smuzhiyun  */
dequeue_func(struct codel_vars * vars,void * ctx)255*4882a593Smuzhiyun static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	struct Qdisc *sch = ctx;
258*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
259*4882a593Smuzhiyun 	struct fq_codel_flow *flow;
260*4882a593Smuzhiyun 	struct sk_buff *skb = NULL;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	flow = container_of(vars, struct fq_codel_flow, cvars);
263*4882a593Smuzhiyun 	if (flow->head) {
264*4882a593Smuzhiyun 		skb = dequeue_head(flow);
265*4882a593Smuzhiyun 		q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb);
266*4882a593Smuzhiyun 		q->memory_usage -= get_codel_cb(skb)->mem_usage;
267*4882a593Smuzhiyun 		sch->q.qlen--;
268*4882a593Smuzhiyun 		sch->qstats.backlog -= qdisc_pkt_len(skb);
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 	return skb;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
drop_func(struct sk_buff * skb,void * ctx)273*4882a593Smuzhiyun static void drop_func(struct sk_buff *skb, void *ctx)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	struct Qdisc *sch = ctx;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	kfree_skb(skb);
278*4882a593Smuzhiyun 	qdisc_qstats_drop(sch);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
fq_codel_dequeue(struct Qdisc * sch)281*4882a593Smuzhiyun static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
284*4882a593Smuzhiyun 	struct sk_buff *skb;
285*4882a593Smuzhiyun 	struct fq_codel_flow *flow;
286*4882a593Smuzhiyun 	struct list_head *head;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun begin:
289*4882a593Smuzhiyun 	head = &q->new_flows;
290*4882a593Smuzhiyun 	if (list_empty(head)) {
291*4882a593Smuzhiyun 		head = &q->old_flows;
292*4882a593Smuzhiyun 		if (list_empty(head))
293*4882a593Smuzhiyun 			return NULL;
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun 	flow = list_first_entry(head, struct fq_codel_flow, flowchain);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	if (flow->deficit <= 0) {
298*4882a593Smuzhiyun 		flow->deficit += q->quantum;
299*4882a593Smuzhiyun 		list_move_tail(&flow->flowchain, &q->old_flows);
300*4882a593Smuzhiyun 		goto begin;
301*4882a593Smuzhiyun 	}
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams,
304*4882a593Smuzhiyun 			    &flow->cvars, &q->cstats, qdisc_pkt_len,
305*4882a593Smuzhiyun 			    codel_get_enqueue_time, drop_func, dequeue_func);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (!skb) {
308*4882a593Smuzhiyun 		/* force a pass through old_flows to prevent starvation */
309*4882a593Smuzhiyun 		if ((head == &q->new_flows) && !list_empty(&q->old_flows))
310*4882a593Smuzhiyun 			list_move_tail(&flow->flowchain, &q->old_flows);
311*4882a593Smuzhiyun 		else
312*4882a593Smuzhiyun 			list_del_init(&flow->flowchain);
313*4882a593Smuzhiyun 		goto begin;
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun 	qdisc_bstats_update(sch, skb);
316*4882a593Smuzhiyun 	flow->deficit -= qdisc_pkt_len(skb);
317*4882a593Smuzhiyun 	/* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
318*4882a593Smuzhiyun 	 * or HTB crashes. Defer it for next round.
319*4882a593Smuzhiyun 	 */
320*4882a593Smuzhiyun 	if (q->cstats.drop_count && sch->q.qlen) {
321*4882a593Smuzhiyun 		qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
322*4882a593Smuzhiyun 					  q->cstats.drop_len);
323*4882a593Smuzhiyun 		q->cstats.drop_count = 0;
324*4882a593Smuzhiyun 		q->cstats.drop_len = 0;
325*4882a593Smuzhiyun 	}
326*4882a593Smuzhiyun 	return skb;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
fq_codel_flow_purge(struct fq_codel_flow * flow)329*4882a593Smuzhiyun static void fq_codel_flow_purge(struct fq_codel_flow *flow)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	rtnl_kfree_skbs(flow->head, flow->tail);
332*4882a593Smuzhiyun 	flow->head = NULL;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun 
fq_codel_reset(struct Qdisc * sch)335*4882a593Smuzhiyun static void fq_codel_reset(struct Qdisc *sch)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
338*4882a593Smuzhiyun 	int i;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	INIT_LIST_HEAD(&q->new_flows);
341*4882a593Smuzhiyun 	INIT_LIST_HEAD(&q->old_flows);
342*4882a593Smuzhiyun 	for (i = 0; i < q->flows_cnt; i++) {
343*4882a593Smuzhiyun 		struct fq_codel_flow *flow = q->flows + i;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 		fq_codel_flow_purge(flow);
346*4882a593Smuzhiyun 		INIT_LIST_HEAD(&flow->flowchain);
347*4882a593Smuzhiyun 		codel_vars_init(&flow->cvars);
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 	memset(q->backlogs, 0, q->flows_cnt * sizeof(u32));
350*4882a593Smuzhiyun 	q->memory_usage = 0;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
354*4882a593Smuzhiyun 	[TCA_FQ_CODEL_TARGET]	= { .type = NLA_U32 },
355*4882a593Smuzhiyun 	[TCA_FQ_CODEL_LIMIT]	= { .type = NLA_U32 },
356*4882a593Smuzhiyun 	[TCA_FQ_CODEL_INTERVAL]	= { .type = NLA_U32 },
357*4882a593Smuzhiyun 	[TCA_FQ_CODEL_ECN]	= { .type = NLA_U32 },
358*4882a593Smuzhiyun 	[TCA_FQ_CODEL_FLOWS]	= { .type = NLA_U32 },
359*4882a593Smuzhiyun 	[TCA_FQ_CODEL_QUANTUM]	= { .type = NLA_U32 },
360*4882a593Smuzhiyun 	[TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NLA_U32 },
361*4882a593Smuzhiyun 	[TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NLA_U32 },
362*4882a593Smuzhiyun 	[TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NLA_U32 },
363*4882a593Smuzhiyun };
364*4882a593Smuzhiyun 
fq_codel_change(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)365*4882a593Smuzhiyun static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt,
366*4882a593Smuzhiyun 			   struct netlink_ext_ack *extack)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
369*4882a593Smuzhiyun 	struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
370*4882a593Smuzhiyun 	u32 quantum = 0;
371*4882a593Smuzhiyun 	int err;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	if (!opt)
374*4882a593Smuzhiyun 		return -EINVAL;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	err = nla_parse_nested_deprecated(tb, TCA_FQ_CODEL_MAX, opt,
377*4882a593Smuzhiyun 					  fq_codel_policy, NULL);
378*4882a593Smuzhiyun 	if (err < 0)
379*4882a593Smuzhiyun 		return err;
380*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_FLOWS]) {
381*4882a593Smuzhiyun 		if (q->flows)
382*4882a593Smuzhiyun 			return -EINVAL;
383*4882a593Smuzhiyun 		q->flows_cnt = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
384*4882a593Smuzhiyun 		if (!q->flows_cnt ||
385*4882a593Smuzhiyun 		    q->flows_cnt > 65536)
386*4882a593Smuzhiyun 			return -EINVAL;
387*4882a593Smuzhiyun 	}
388*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_QUANTUM]) {
389*4882a593Smuzhiyun 		quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]));
390*4882a593Smuzhiyun 		if (quantum > FQ_CODEL_QUANTUM_MAX) {
391*4882a593Smuzhiyun 			NL_SET_ERR_MSG(extack, "Invalid quantum");
392*4882a593Smuzhiyun 			return -EINVAL;
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 	}
395*4882a593Smuzhiyun 	sch_tree_lock(sch);
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_TARGET]) {
398*4882a593Smuzhiyun 		u64 target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 		q->cparams.target = (target * NSEC_PER_USEC) >> CODEL_SHIFT;
401*4882a593Smuzhiyun 	}
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_CE_THRESHOLD]) {
404*4882a593Smuzhiyun 		u64 val = nla_get_u32(tb[TCA_FQ_CODEL_CE_THRESHOLD]);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 		q->cparams.ce_threshold = (val * NSEC_PER_USEC) >> CODEL_SHIFT;
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_INTERVAL]) {
410*4882a593Smuzhiyun 		u64 interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 		q->cparams.interval = (interval * NSEC_PER_USEC) >> CODEL_SHIFT;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_LIMIT])
416*4882a593Smuzhiyun 		sch->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_ECN])
419*4882a593Smuzhiyun 		q->cparams.ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	if (quantum)
422*4882a593Smuzhiyun 		q->quantum = quantum;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])
425*4882a593Smuzhiyun 		q->drop_batch_size = max(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (tb[TCA_FQ_CODEL_MEMORY_LIMIT])
428*4882a593Smuzhiyun 		q->memory_limit = min(1U << 31, nla_get_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT]));
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	while (sch->q.qlen > sch->limit ||
431*4882a593Smuzhiyun 	       q->memory_usage > q->memory_limit) {
432*4882a593Smuzhiyun 		struct sk_buff *skb = fq_codel_dequeue(sch);
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 		q->cstats.drop_len += qdisc_pkt_len(skb);
435*4882a593Smuzhiyun 		rtnl_kfree_skbs(skb, skb);
436*4882a593Smuzhiyun 		q->cstats.drop_count++;
437*4882a593Smuzhiyun 	}
438*4882a593Smuzhiyun 	qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len);
439*4882a593Smuzhiyun 	q->cstats.drop_count = 0;
440*4882a593Smuzhiyun 	q->cstats.drop_len = 0;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	sch_tree_unlock(sch);
443*4882a593Smuzhiyun 	return 0;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun 
fq_codel_destroy(struct Qdisc * sch)446*4882a593Smuzhiyun static void fq_codel_destroy(struct Qdisc *sch)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	tcf_block_put(q->block);
451*4882a593Smuzhiyun 	kvfree(q->backlogs);
452*4882a593Smuzhiyun 	kvfree(q->flows);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun 
fq_codel_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)455*4882a593Smuzhiyun static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
456*4882a593Smuzhiyun 			 struct netlink_ext_ack *extack)
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
459*4882a593Smuzhiyun 	int i;
460*4882a593Smuzhiyun 	int err;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	sch->limit = 10*1024;
463*4882a593Smuzhiyun 	q->flows_cnt = 1024;
464*4882a593Smuzhiyun 	q->memory_limit = 32 << 20; /* 32 MBytes */
465*4882a593Smuzhiyun 	q->drop_batch_size = 64;
466*4882a593Smuzhiyun 	q->quantum = psched_mtu(qdisc_dev(sch));
467*4882a593Smuzhiyun 	INIT_LIST_HEAD(&q->new_flows);
468*4882a593Smuzhiyun 	INIT_LIST_HEAD(&q->old_flows);
469*4882a593Smuzhiyun 	codel_params_init(&q->cparams);
470*4882a593Smuzhiyun 	codel_stats_init(&q->cstats);
471*4882a593Smuzhiyun 	q->cparams.ecn = true;
472*4882a593Smuzhiyun 	q->cparams.mtu = psched_mtu(qdisc_dev(sch));
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	if (opt) {
475*4882a593Smuzhiyun 		err = fq_codel_change(sch, opt, extack);
476*4882a593Smuzhiyun 		if (err)
477*4882a593Smuzhiyun 			goto init_failure;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
481*4882a593Smuzhiyun 	if (err)
482*4882a593Smuzhiyun 		goto init_failure;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	if (!q->flows) {
485*4882a593Smuzhiyun 		q->flows = kvcalloc(q->flows_cnt,
486*4882a593Smuzhiyun 				    sizeof(struct fq_codel_flow),
487*4882a593Smuzhiyun 				    GFP_KERNEL);
488*4882a593Smuzhiyun 		if (!q->flows) {
489*4882a593Smuzhiyun 			err = -ENOMEM;
490*4882a593Smuzhiyun 			goto init_failure;
491*4882a593Smuzhiyun 		}
492*4882a593Smuzhiyun 		q->backlogs = kvcalloc(q->flows_cnt, sizeof(u32), GFP_KERNEL);
493*4882a593Smuzhiyun 		if (!q->backlogs) {
494*4882a593Smuzhiyun 			err = -ENOMEM;
495*4882a593Smuzhiyun 			goto alloc_failure;
496*4882a593Smuzhiyun 		}
497*4882a593Smuzhiyun 		for (i = 0; i < q->flows_cnt; i++) {
498*4882a593Smuzhiyun 			struct fq_codel_flow *flow = q->flows + i;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 			INIT_LIST_HEAD(&flow->flowchain);
501*4882a593Smuzhiyun 			codel_vars_init(&flow->cvars);
502*4882a593Smuzhiyun 		}
503*4882a593Smuzhiyun 	}
504*4882a593Smuzhiyun 	if (sch->limit >= 1)
505*4882a593Smuzhiyun 		sch->flags |= TCQ_F_CAN_BYPASS;
506*4882a593Smuzhiyun 	else
507*4882a593Smuzhiyun 		sch->flags &= ~TCQ_F_CAN_BYPASS;
508*4882a593Smuzhiyun 	return 0;
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun alloc_failure:
511*4882a593Smuzhiyun 	kvfree(q->flows);
512*4882a593Smuzhiyun 	q->flows = NULL;
513*4882a593Smuzhiyun init_failure:
514*4882a593Smuzhiyun 	q->flows_cnt = 0;
515*4882a593Smuzhiyun 	return err;
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun 
fq_codel_dump(struct Qdisc * sch,struct sk_buff * skb)518*4882a593Smuzhiyun static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb)
519*4882a593Smuzhiyun {
520*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
521*4882a593Smuzhiyun 	struct nlattr *opts;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
524*4882a593Smuzhiyun 	if (opts == NULL)
525*4882a593Smuzhiyun 		goto nla_put_failure;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	if (nla_put_u32(skb, TCA_FQ_CODEL_TARGET,
528*4882a593Smuzhiyun 			codel_time_to_us(q->cparams.target)) ||
529*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_LIMIT,
530*4882a593Smuzhiyun 			sch->limit) ||
531*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_INTERVAL,
532*4882a593Smuzhiyun 			codel_time_to_us(q->cparams.interval)) ||
533*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_ECN,
534*4882a593Smuzhiyun 			q->cparams.ecn) ||
535*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM,
536*4882a593Smuzhiyun 			q->quantum) ||
537*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_DROP_BATCH_SIZE,
538*4882a593Smuzhiyun 			q->drop_batch_size) ||
539*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_MEMORY_LIMIT,
540*4882a593Smuzhiyun 			q->memory_limit) ||
541*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_FLOWS,
542*4882a593Smuzhiyun 			q->flows_cnt))
543*4882a593Smuzhiyun 		goto nla_put_failure;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	if (q->cparams.ce_threshold != CODEL_DISABLED_THRESHOLD &&
546*4882a593Smuzhiyun 	    nla_put_u32(skb, TCA_FQ_CODEL_CE_THRESHOLD,
547*4882a593Smuzhiyun 			codel_time_to_us(q->cparams.ce_threshold)))
548*4882a593Smuzhiyun 		goto nla_put_failure;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	return nla_nest_end(skb, opts);
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun nla_put_failure:
553*4882a593Smuzhiyun 	return -1;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun 
fq_codel_dump_stats(struct Qdisc * sch,struct gnet_dump * d)556*4882a593Smuzhiyun static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
559*4882a593Smuzhiyun 	struct tc_fq_codel_xstats st = {
560*4882a593Smuzhiyun 		.type				= TCA_FQ_CODEL_XSTATS_QDISC,
561*4882a593Smuzhiyun 	};
562*4882a593Smuzhiyun 	struct list_head *pos;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	st.qdisc_stats.maxpacket = q->cstats.maxpacket;
565*4882a593Smuzhiyun 	st.qdisc_stats.drop_overlimit = q->drop_overlimit;
566*4882a593Smuzhiyun 	st.qdisc_stats.ecn_mark = q->cstats.ecn_mark;
567*4882a593Smuzhiyun 	st.qdisc_stats.new_flow_count = q->new_flow_count;
568*4882a593Smuzhiyun 	st.qdisc_stats.ce_mark = q->cstats.ce_mark;
569*4882a593Smuzhiyun 	st.qdisc_stats.memory_usage  = q->memory_usage;
570*4882a593Smuzhiyun 	st.qdisc_stats.drop_overmemory = q->drop_overmemory;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	sch_tree_lock(sch);
573*4882a593Smuzhiyun 	list_for_each(pos, &q->new_flows)
574*4882a593Smuzhiyun 		st.qdisc_stats.new_flows_len++;
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	list_for_each(pos, &q->old_flows)
577*4882a593Smuzhiyun 		st.qdisc_stats.old_flows_len++;
578*4882a593Smuzhiyun 	sch_tree_unlock(sch);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	return gnet_stats_copy_app(d, &st, sizeof(st));
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun 
fq_codel_leaf(struct Qdisc * sch,unsigned long arg)583*4882a593Smuzhiyun static struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg)
584*4882a593Smuzhiyun {
585*4882a593Smuzhiyun 	return NULL;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun 
fq_codel_find(struct Qdisc * sch,u32 classid)588*4882a593Smuzhiyun static unsigned long fq_codel_find(struct Qdisc *sch, u32 classid)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun 	return 0;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun 
fq_codel_bind(struct Qdisc * sch,unsigned long parent,u32 classid)593*4882a593Smuzhiyun static unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent,
594*4882a593Smuzhiyun 			      u32 classid)
595*4882a593Smuzhiyun {
596*4882a593Smuzhiyun 	return 0;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun 
fq_codel_unbind(struct Qdisc * q,unsigned long cl)599*4882a593Smuzhiyun static void fq_codel_unbind(struct Qdisc *q, unsigned long cl)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun 
fq_codel_tcf_block(struct Qdisc * sch,unsigned long cl,struct netlink_ext_ack * extack)603*4882a593Smuzhiyun static struct tcf_block *fq_codel_tcf_block(struct Qdisc *sch, unsigned long cl,
604*4882a593Smuzhiyun 					    struct netlink_ext_ack *extack)
605*4882a593Smuzhiyun {
606*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	if (cl)
609*4882a593Smuzhiyun 		return NULL;
610*4882a593Smuzhiyun 	return q->block;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun 
fq_codel_dump_class(struct Qdisc * sch,unsigned long cl,struct sk_buff * skb,struct tcmsg * tcm)613*4882a593Smuzhiyun static int fq_codel_dump_class(struct Qdisc *sch, unsigned long cl,
614*4882a593Smuzhiyun 			  struct sk_buff *skb, struct tcmsg *tcm)
615*4882a593Smuzhiyun {
616*4882a593Smuzhiyun 	tcm->tcm_handle |= TC_H_MIN(cl);
617*4882a593Smuzhiyun 	return 0;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun 
fq_codel_dump_class_stats(struct Qdisc * sch,unsigned long cl,struct gnet_dump * d)620*4882a593Smuzhiyun static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl,
621*4882a593Smuzhiyun 				     struct gnet_dump *d)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
624*4882a593Smuzhiyun 	u32 idx = cl - 1;
625*4882a593Smuzhiyun 	struct gnet_stats_queue qs = { 0 };
626*4882a593Smuzhiyun 	struct tc_fq_codel_xstats xstats;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	if (idx < q->flows_cnt) {
629*4882a593Smuzhiyun 		const struct fq_codel_flow *flow = &q->flows[idx];
630*4882a593Smuzhiyun 		const struct sk_buff *skb;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 		memset(&xstats, 0, sizeof(xstats));
633*4882a593Smuzhiyun 		xstats.type = TCA_FQ_CODEL_XSTATS_CLASS;
634*4882a593Smuzhiyun 		xstats.class_stats.deficit = flow->deficit;
635*4882a593Smuzhiyun 		xstats.class_stats.ldelay =
636*4882a593Smuzhiyun 			codel_time_to_us(flow->cvars.ldelay);
637*4882a593Smuzhiyun 		xstats.class_stats.count = flow->cvars.count;
638*4882a593Smuzhiyun 		xstats.class_stats.lastcount = flow->cvars.lastcount;
639*4882a593Smuzhiyun 		xstats.class_stats.dropping = flow->cvars.dropping;
640*4882a593Smuzhiyun 		if (flow->cvars.dropping) {
641*4882a593Smuzhiyun 			codel_tdiff_t delta = flow->cvars.drop_next -
642*4882a593Smuzhiyun 					      codel_get_time();
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 			xstats.class_stats.drop_next = (delta >= 0) ?
645*4882a593Smuzhiyun 				codel_time_to_us(delta) :
646*4882a593Smuzhiyun 				-codel_time_to_us(-delta);
647*4882a593Smuzhiyun 		}
648*4882a593Smuzhiyun 		if (flow->head) {
649*4882a593Smuzhiyun 			sch_tree_lock(sch);
650*4882a593Smuzhiyun 			skb = flow->head;
651*4882a593Smuzhiyun 			while (skb) {
652*4882a593Smuzhiyun 				qs.qlen++;
653*4882a593Smuzhiyun 				skb = skb->next;
654*4882a593Smuzhiyun 			}
655*4882a593Smuzhiyun 			sch_tree_unlock(sch);
656*4882a593Smuzhiyun 		}
657*4882a593Smuzhiyun 		qs.backlog = q->backlogs[idx];
658*4882a593Smuzhiyun 		qs.drops = 0;
659*4882a593Smuzhiyun 	}
660*4882a593Smuzhiyun 	if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0)
661*4882a593Smuzhiyun 		return -1;
662*4882a593Smuzhiyun 	if (idx < q->flows_cnt)
663*4882a593Smuzhiyun 		return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
664*4882a593Smuzhiyun 	return 0;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
fq_codel_walk(struct Qdisc * sch,struct qdisc_walker * arg)667*4882a593Smuzhiyun static void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	struct fq_codel_sched_data *q = qdisc_priv(sch);
670*4882a593Smuzhiyun 	unsigned int i;
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	if (arg->stop)
673*4882a593Smuzhiyun 		return;
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	for (i = 0; i < q->flows_cnt; i++) {
676*4882a593Smuzhiyun 		if (list_empty(&q->flows[i].flowchain) ||
677*4882a593Smuzhiyun 		    arg->count < arg->skip) {
678*4882a593Smuzhiyun 			arg->count++;
679*4882a593Smuzhiyun 			continue;
680*4882a593Smuzhiyun 		}
681*4882a593Smuzhiyun 		if (arg->fn(sch, i + 1, arg) < 0) {
682*4882a593Smuzhiyun 			arg->stop = 1;
683*4882a593Smuzhiyun 			break;
684*4882a593Smuzhiyun 		}
685*4882a593Smuzhiyun 		arg->count++;
686*4882a593Smuzhiyun 	}
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun static const struct Qdisc_class_ops fq_codel_class_ops = {
690*4882a593Smuzhiyun 	.leaf		=	fq_codel_leaf,
691*4882a593Smuzhiyun 	.find		=	fq_codel_find,
692*4882a593Smuzhiyun 	.tcf_block	=	fq_codel_tcf_block,
693*4882a593Smuzhiyun 	.bind_tcf	=	fq_codel_bind,
694*4882a593Smuzhiyun 	.unbind_tcf	=	fq_codel_unbind,
695*4882a593Smuzhiyun 	.dump		=	fq_codel_dump_class,
696*4882a593Smuzhiyun 	.dump_stats	=	fq_codel_dump_class_stats,
697*4882a593Smuzhiyun 	.walk		=	fq_codel_walk,
698*4882a593Smuzhiyun };
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = {
701*4882a593Smuzhiyun 	.cl_ops		=	&fq_codel_class_ops,
702*4882a593Smuzhiyun 	.id		=	"fq_codel",
703*4882a593Smuzhiyun 	.priv_size	=	sizeof(struct fq_codel_sched_data),
704*4882a593Smuzhiyun 	.enqueue	=	fq_codel_enqueue,
705*4882a593Smuzhiyun 	.dequeue	=	fq_codel_dequeue,
706*4882a593Smuzhiyun 	.peek		=	qdisc_peek_dequeued,
707*4882a593Smuzhiyun 	.init		=	fq_codel_init,
708*4882a593Smuzhiyun 	.reset		=	fq_codel_reset,
709*4882a593Smuzhiyun 	.destroy	=	fq_codel_destroy,
710*4882a593Smuzhiyun 	.change		=	fq_codel_change,
711*4882a593Smuzhiyun 	.dump		=	fq_codel_dump,
712*4882a593Smuzhiyun 	.dump_stats =	fq_codel_dump_stats,
713*4882a593Smuzhiyun 	.owner		=	THIS_MODULE,
714*4882a593Smuzhiyun };
715*4882a593Smuzhiyun 
fq_codel_module_init(void)716*4882a593Smuzhiyun static int __init fq_codel_module_init(void)
717*4882a593Smuzhiyun {
718*4882a593Smuzhiyun 	return register_qdisc(&fq_codel_qdisc_ops);
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun 
fq_codel_module_exit(void)721*4882a593Smuzhiyun static void __exit fq_codel_module_exit(void)
722*4882a593Smuzhiyun {
723*4882a593Smuzhiyun 	unregister_qdisc(&fq_codel_qdisc_ops);
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun module_init(fq_codel_module_init)
727*4882a593Smuzhiyun module_exit(fq_codel_module_exit)
728*4882a593Smuzhiyun MODULE_AUTHOR("Eric Dumazet");
729*4882a593Smuzhiyun MODULE_LICENSE("GPL");
730*4882a593Smuzhiyun MODULE_DESCRIPTION("Fair Queue CoDel discipline");
731