Blame view

net/sched/sch_prio.c 7.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   * net/sched/sch_prio.c	Simple 3-band priority "scheduler".
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
10
   * Fixes:       19990609: J Hadi Salim <hadi@nortelnetworks.com>:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
   *              Init --  EINVAL when opt undefined
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/skbuff.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
20
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <net/pkt_sched.h>
cc7ec456f   Eric Dumazet   net_sched: cleanups
22
  struct prio_sched_data {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  	int bands;
25d8c0d55   John Fastabend   net: rcu-ify tcf_...
24
  	struct tcf_proto __rcu *filter_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
34
35
  	u8  prio2band[TC_PRIO_MAX+1];
  	struct Qdisc *queues[TCQ_PRIO_BANDS];
  };
  
  
  static struct Qdisc *
  prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	u32 band = skb->priority;
  	struct tcf_result res;
25d8c0d55   John Fastabend   net: rcu-ify tcf_...
36
  	struct tcf_proto *fl;
bdba91ec7   Patrick McHardy   [NET_SCHED]: Fix ...
37
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

c27f339af   Jarek Poplawski   net_sched: Add qd...
39
  	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  	if (TC_H_MAJ(skb->priority) != sch->handle) {
25d8c0d55   John Fastabend   net: rcu-ify tcf_...
41
  		fl = rcu_dereference_bh(q->filter_list);
3b3ae8802   Daniel Borkmann   net: sched: conso...
42
  		err = tc_classify(skb, fl, &res, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
  #ifdef CONFIG_NET_CLS_ACT
dbaaa07a6   Lucas Nussbaum   [NET_SCHED] sch_p...
44
  		switch (err) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
  		case TC_ACT_STOLEN:
  		case TC_ACT_QUEUED:
378a2f090   Jarek Poplawski   net_sched: Add qd...
47
  			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  		case TC_ACT_SHOT:
  			return NULL;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
50
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  #endif
25d8c0d55   John Fastabend   net: rcu-ify tcf_...
52
  		if (!fl || err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
  			if (TC_H_MAJ(band))
  				band = 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
55
  			return q->queues[q->prio2band[band & TC_PRIO_MAX]];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
  		}
  		band = res.classid;
  	}
  	band = TC_H_MIN(band) - 1;
3e5c2d3bd   Jamal Hadi Salim   [NET_SCHED]: prio...
60
  	if (band >= q->bands)
1d8ae3fde   David S. Miller   pkt_sched: Remove...
61
  		return q->queues[q->prio2band[0]];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
  	return q->queues[band];
  }
  
  static int
520ac30f4   Eric Dumazet   net_sched: drop p...
66
  prio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
  {
  	struct Qdisc *qdisc;
  	int ret;
  
  	qdisc = prio_classify(skb, sch, &ret);
  #ifdef CONFIG_NET_CLS_ACT
  	if (qdisc == NULL) {
29f1df6cc   Jamal Hadi Salim   [PKT_SCHED]: Fix ...
74

c27f339af   Jarek Poplawski   net_sched: Add qd...
75
  		if (ret & __NET_XMIT_BYPASS)
25331d6ce   John Fastabend   net: sched: imple...
76
  			qdisc_qstats_drop(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
  		kfree_skb(skb);
  		return ret;
  	}
  #endif
520ac30f4   Eric Dumazet   net_sched: drop p...
81
  	ret = qdisc_enqueue(skb, qdisc, to_free);
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
82
  	if (ret == NET_XMIT_SUCCESS) {
6529d75ad   WANG Cong   sch_prio: update ...
83
  		qdisc_qstats_backlog_inc(sch, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
  		sch->q.qlen++;
  		return NET_XMIT_SUCCESS;
  	}
378a2f090   Jarek Poplawski   net_sched: Add qd...
87
  	if (net_xmit_drop_count(ret))
25331d6ce   John Fastabend   net: sched: imple...
88
  		qdisc_qstats_drop(sch);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
89
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  }
48a8f519e   Patrick McHardy   pkt_sched: Add ->...
91
92
93
94
95
96
97
98
99
100
101
102
103
  static struct sk_buff *prio_peek(struct Qdisc *sch)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	int prio;
  
  	for (prio = 0; prio < q->bands; prio++) {
  		struct Qdisc *qdisc = q->queues[prio];
  		struct sk_buff *skb = qdisc->ops->peek(qdisc);
  		if (skb)
  			return skb;
  	}
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104

cc7ec456f   Eric Dumazet   net_sched: cleanups
105
  static struct sk_buff *prio_dequeue(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  	struct prio_sched_data *q = qdisc_priv(sch);
  	int prio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
  
  	for (prio = 0; prio < q->bands; prio++) {
1d8ae3fde   David S. Miller   pkt_sched: Remove...
111
  		struct Qdisc *qdisc = q->queues[prio];
3557619f0   Florian Westphal   net_sched: prio: ...
112
  		struct sk_buff *skb = qdisc_dequeue_peeked(qdisc);
1d8ae3fde   David S. Miller   pkt_sched: Remove...
113
  		if (skb) {
9190b3b32   Eric Dumazet   net_sched: accura...
114
  			qdisc_bstats_update(sch, skb);
6529d75ad   WANG Cong   sch_prio: update ...
115
  			qdisc_qstats_backlog_dec(sch, skb);
1d8ae3fde   David S. Miller   pkt_sched: Remove...
116
117
  			sch->q.qlen--;
  			return skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
  		}
  	}
  	return NULL;
  
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  static void
cc7ec456f   Eric Dumazet   net_sched: cleanups
124
  prio_reset(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
  {
  	int prio;
  	struct prio_sched_data *q = qdisc_priv(sch);
cc7ec456f   Eric Dumazet   net_sched: cleanups
128
  	for (prio = 0; prio < q->bands; prio++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  		qdisc_reset(q->queues[prio]);
6529d75ad   WANG Cong   sch_prio: update ...
130
  	sch->qstats.backlog = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
133
134
  	sch->q.qlen = 0;
  }
  
  static void
cc7ec456f   Eric Dumazet   net_sched: cleanups
135
  prio_destroy(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
  {
  	int prio;
  	struct prio_sched_data *q = qdisc_priv(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139

ff31ab56c   Patrick McHardy   net-sched: change...
140
  	tcf_destroy_chain(&q->filter_list);
cc7ec456f   Eric Dumazet   net_sched: cleanups
141
  	for (prio = 0; prio < q->bands; prio++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
  		qdisc_destroy(q->queues[prio]);
  }
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
144
  static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
3d7c8257d   Eric Dumazet   net_sched: prio: ...
147
148
  	struct Qdisc *queues[TCQ_PRIO_BANDS];
  	int oldbands = q->bands, i;
d62733c8e   Peter P Waskiewicz Jr   [SCHED]: Qdisc ch...
149
  	struct tc_prio_qopt *qopt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150

1d8ae3fde   David S. Miller   pkt_sched: Remove...
151
152
153
  	if (nla_len(opt) < sizeof(*qopt))
  		return -EINVAL;
  	qopt = nla_data(opt);
d62733c8e   Peter P Waskiewicz Jr   [SCHED]: Qdisc ch...
154

1d8ae3fde   David S. Miller   pkt_sched: Remove...
155
  	if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  		return -EINVAL;
cc7ec456f   Eric Dumazet   net_sched: cleanups
157
  	for (i = 0; i <= TC_PRIO_MAX; i++) {
1d8ae3fde   David S. Miller   pkt_sched: Remove...
158
  		if (qopt->priomap[i] >= qopt->bands)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
  			return -EINVAL;
  	}
3d7c8257d   Eric Dumazet   net_sched: prio: ...
161
162
163
164
165
166
167
168
169
170
  	/* Before commit, make sure we can allocate all new qdiscs */
  	for (i = oldbands; i < qopt->bands; i++) {
  		queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
  					      TC_H_MAKE(sch->handle, i + 1));
  		if (!queues[i]) {
  			while (i > oldbands)
  				qdisc_destroy(queues[--i]);
  			return -ENOMEM;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	sch_tree_lock(sch);
1d8ae3fde   David S. Miller   pkt_sched: Remove...
172
  	q->bands = qopt->bands;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
3d7c8257d   Eric Dumazet   net_sched: prio: ...
174
  	for (i = q->bands; i < oldbands; i++) {
b94c8afcb   Patrick McHardy   pkt_sched: remove...
175
  		struct Qdisc *child = q->queues[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176

3d7c8257d   Eric Dumazet   net_sched: prio: ...
177
178
179
  		qdisc_tree_reduce_backlog(child, child->q.qlen,
  					  child->qstats.backlog);
  		qdisc_destroy(child);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  	}
cbdf45116   Eric Dumazet   net_sched: prio: ...
181

3d7c8257d   Eric Dumazet   net_sched: prio: ...
182
183
  	for (i = oldbands; i < q->bands; i++)
  		q->queues[i] = queues[i];
cbdf45116   Eric Dumazet   net_sched: prio: ...
184

3d7c8257d   Eric Dumazet   net_sched: prio: ...
185
  	sch_tree_unlock(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
  	return 0;
  }
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
188
  static int prio_init(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  {
3d7c8257d   Eric Dumazet   net_sched: prio: ...
190
  	if (!opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192

3d7c8257d   Eric Dumazet   net_sched: prio: ...
193
  	return prio_tune(sch, opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
  }
  
  static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
199
  	unsigned char *b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
  	struct tc_prio_qopt opt;
  
  	opt.bands = q->bands;
cc7ec456f   Eric Dumazet   net_sched: cleanups
203
  	memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX + 1);
d62733c8e   Peter P Waskiewicz Jr   [SCHED]: Qdisc ch...
204

1b34ec43c   David S. Miller   pkt_sched: Stop u...
205
206
  	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
  		goto nla_put_failure;
d62733c8e   Peter P Waskiewicz Jr   [SCHED]: Qdisc ch...
207

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  	return skb->len;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
209
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
210
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
  	return -1;
  }
  
  static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
  		      struct Qdisc **old)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	unsigned long band = arg - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
  	if (new == NULL)
  		new = &noop_qdisc;
86a7996cc   WANG Cong   net_sched: introd...
221
  	*old = qdisc_replace(sch, new, &q->queues[band]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
228
229
  	return 0;
  }
  
  static struct Qdisc *
  prio_leaf(struct Qdisc *sch, unsigned long arg)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	unsigned long band = arg - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  	return q->queues[band];
  }
  
  static unsigned long prio_get(struct Qdisc *sch, u32 classid)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	unsigned long band = TC_H_MIN(classid);
  
  	if (band - 1 >= q->bands)
  		return 0;
  	return band;
  }
  
  static unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
  {
  	return prio_get(sch, classid);
  }
  
  
  static void prio_put(struct Qdisc *q, unsigned long cl)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
  static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
  			   struct tcmsg *tcm)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	tcm->tcm_handle |= TC_H_MIN(cl);
5b9a9ccfa   Patrick McHardy   net_sched: remove...
257
  	tcm->tcm_info = q->queues[cl-1]->handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
  	return 0;
  }
2cf6c36cb   Jarek Poplawski   [NET_SCHED] sch_p...
260
261
262
263
264
265
266
  static int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
  				 struct gnet_dump *d)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	struct Qdisc *cl_q;
  
  	cl_q = q->queues[cl - 1];
edb09eb17   Eric Dumazet   net: sched: do no...
267
268
  	if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
  				  d, NULL, &cl_q->bstats) < 0 ||
b0ab6f927   John Fastabend   net: sched: enabl...
269
  	    gnet_stats_copy_queue(d, NULL, &cl_q->qstats, cl_q->q.qlen) < 0)
2cf6c36cb   Jarek Poplawski   [NET_SCHED] sch_p...
270
271
272
273
  		return -1;
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
280
281
282
283
284
285
286
  static void prio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  	int prio;
  
  	if (arg->stop)
  		return;
  
  	for (prio = 0; prio < q->bands; prio++) {
  		if (arg->count < arg->skip) {
  			arg->count++;
  			continue;
  		}
cc7ec456f   Eric Dumazet   net_sched: cleanups
287
  		if (arg->fn(sch, prio + 1, arg) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
293
  			arg->stop = 1;
  			break;
  		}
  		arg->count++;
  	}
  }
25d8c0d55   John Fastabend   net: rcu-ify tcf_...
294
295
  static struct tcf_proto __rcu **prio_find_tcf(struct Qdisc *sch,
  					      unsigned long cl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
301
302
  {
  	struct prio_sched_data *q = qdisc_priv(sch);
  
  	if (cl)
  		return NULL;
  	return &q->filter_list;
  }
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
303
  static const struct Qdisc_class_ops prio_class_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
  	.graft		=	prio_graft,
  	.leaf		=	prio_leaf,
  	.get		=	prio_get,
  	.put		=	prio_put,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
  	.walk		=	prio_walk,
  	.tcf_chain	=	prio_find_tcf,
  	.bind_tcf	=	prio_bind,
  	.unbind_tcf	=	prio_put,
  	.dump		=	prio_dump_class,
2cf6c36cb   Jarek Poplawski   [NET_SCHED] sch_p...
313
  	.dump_stats	=	prio_dump_class_stats,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
  };
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
315
  static struct Qdisc_ops prio_qdisc_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
321
  	.next		=	NULL,
  	.cl_ops		=	&prio_class_ops,
  	.id		=	"prio",
  	.priv_size	=	sizeof(struct prio_sched_data),
  	.enqueue	=	prio_enqueue,
  	.dequeue	=	prio_dequeue,
48a8f519e   Patrick McHardy   pkt_sched: Add ->...
322
  	.peek		=	prio_peek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
329
330
331
332
  	.init		=	prio_init,
  	.reset		=	prio_reset,
  	.destroy	=	prio_destroy,
  	.change		=	prio_tune,
  	.dump		=	prio_dump,
  	.owner		=	THIS_MODULE,
  };
  
  static int __init prio_module_init(void)
  {
1d8ae3fde   David S. Miller   pkt_sched: Remove...
333
  	return register_qdisc(&prio_qdisc_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
335
  static void __exit prio_module_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
  {
  	unregister_qdisc(&prio_qdisc_ops);
  }
  
  module_init(prio_module_init)
  module_exit(prio_module_exit)
  
  MODULE_LICENSE("GPL");