Blame view

net/sched/sch_hfsc.c 40.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  /*
   * Copyright (c) 2003 Patrick McHardy, <kaber@trash.net>
   *
   * 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.
   *
   * 2003-10-17 - Ported from altq
   */
  /*
   * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
   *
   * Permission to use, copy, modify, and distribute this software and
   * its documentation is hereby granted (including for commercial or
   * for-profit use), provided that both the copyright notice and this
   * permission notice appear in all copies of the software, derivative
   * works, or modified versions, and any portions thereof.
   *
   * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
   * WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON PROVIDES THIS
   * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
   * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
   * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
   * DAMAGE.
   *
   * Carnegie Mellon encourages (but does not require) users of this
   * software to return any improvements or extensions that they make,
   * and to grant Carnegie Mellon the rights to redistribute these
   * changes without encumbrance.
   */
  /*
   * H-FSC is described in Proceedings of SIGCOMM'97,
   * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing,
   * Real-Time and Priority Service"
   * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng.
   *
   * Oleg Cherevko <olwi@aq.ml.com.ua> added the upperlimit for link-sharing.
   * when a class has an upperlimit, the fit-time is computed from the
   * upperlimit service curve.  the link-sharing scheduler does not schedule
   * a class whose fit-time exceeds the current time.
   */
  
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
  #include <linux/compiler.h>
  #include <linux/spinlock.h>
  #include <linux/skbuff.h>
  #include <linux/string.h>
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
  #include <linux/list.h>
  #include <linux/rbtree.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
  #include <linux/rtnetlink.h>
  #include <linux/pkt_sched.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
66
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
  #include <net/pkt_sched.h>
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  #include <asm/div64.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
78
  /*
   * kernel internal service curve representation:
   *   coordinates are given by 64 bit unsigned integers.
   *   x-axis: unit is clock count.
   *   y-axis: unit is byte.
   *
   *   The service curve parameters are converted to the internal
   *   representation. The slope values are scaled to avoid overflow.
   *   the inverse slope values as well as the y-projection of the 1st
fd589a8f0   Anand Gadiyar   trivial: fix typo...
79
   *   segment are kept in order to avoid 64-bit divide operations
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
   *   that are expensive on 32-bit architectures.
   */
cc7ec456f   Eric Dumazet   net_sched: cleanups
82
  struct internal_sc {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
89
90
91
  	u64	sm1;	/* scaled slope of the 1st segment */
  	u64	ism1;	/* scaled inverse-slope of the 1st segment */
  	u64	dx;	/* the x-projection of the 1st segment */
  	u64	dy;	/* the y-projection of the 1st segment */
  	u64	sm2;	/* scaled slope of the 2nd segment */
  	u64	ism2;	/* scaled inverse-slope of the 2nd segment */
  };
  
  /* runtime service curve */
cc7ec456f   Eric Dumazet   net_sched: cleanups
92
  struct runtime_sc {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
  	u64	x;	/* current starting position on x-axis */
  	u64	y;	/* current starting position on y-axis */
  	u64	sm1;	/* scaled slope of the 1st segment */
  	u64	ism1;	/* scaled inverse-slope of the 1st segment */
  	u64	dx;	/* the x-projection of the 1st segment */
  	u64	dy;	/* the y-projection of the 1st segment */
  	u64	sm2;	/* scaled slope of the 2nd segment */
  	u64	ism2;	/* scaled inverse-slope of the 2nd segment */
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
102
  enum hfsc_class_flags {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
  	HFSC_RSC = 0x1,
  	HFSC_FSC = 0x2,
  	HFSC_USC = 0x4
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
107
  struct hfsc_class {
be0d39d52   Patrick McHardy   net-sched: sch_hf...
108
  	struct Qdisc_class_common cl_common;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
  	unsigned int	refcnt;		/* usage count */
c1a8f1f1c   Eric Dumazet   net: restore gnet...
110
  	struct gnet_stats_basic_packed bstats;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
  	struct gnet_stats_queue qstats;
  	struct gnet_stats_rate_est rate_est;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  	unsigned int	level;		/* class level in hierarchy */
  	struct tcf_proto *filter_list;	/* filter list */
  	unsigned int	filter_cnt;	/* filter count */
  
  	struct hfsc_sched *sched;	/* scheduler data */
  	struct hfsc_class *cl_parent;	/* parent class */
  	struct list_head siblings;	/* sibling classes */
  	struct list_head children;	/* child classes */
  	struct Qdisc	*qdisc;		/* leaf qdisc */
  
  	struct rb_node el_node;		/* qdisc's eligible tree member */
  	struct rb_root vt_tree;		/* active children sorted by cl_vt */
  	struct rb_node vt_node;		/* parent's vt_tree member */
  	struct rb_root cf_tree;		/* active children sorted by cl_f */
  	struct rb_node cf_node;		/* parent's cf_heap member */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
  	struct list_head dlist;		/* drop list member */
  
  	u64	cl_total;		/* total work in bytes */
  	u64	cl_cumul;		/* cumulative work in bytes done by
  					   real-time criteria */
cc7ec456f   Eric Dumazet   net_sched: cleanups
133
134
  	u64	cl_d;			/* deadline*/
  	u64	cl_e;			/* eligible time */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  	u64	cl_vt;			/* virtual time */
  	u64	cl_f;			/* time when this class will fit for
  					   link-sharing, max(myf, cfmin) */
  	u64	cl_myf;			/* my fit-time (calculated from this
  					   class's own upperlimit curve) */
  	u64	cl_myfadj;		/* my fit-time adjustment (to cancel
  					   history dependence) */
  	u64	cl_cfmin;		/* earliest children's fit-time (used
  					   with cl_myf to obtain cl_f) */
  	u64	cl_cvtmin;		/* minimal virtual time among the
  					   children fit for link-sharing
  					   (monotonic within a period) */
  	u64	cl_vtadj;		/* intra-period cumulative vt
  					   adjustment */
  	u64	cl_vtoff;		/* inter-period cumulative vt offset */
  	u64	cl_cvtmax;		/* max child's vt in the last period */
  	u64	cl_cvtoff;		/* cumulative cvtmax of all periods */
9a94b3518   Joe Perches   [PKT_SCHED]: Spel...
152
  	u64	cl_pcvtoff;		/* parent's cvtoff at initialization
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  					   time */
  
  	struct internal_sc cl_rsc;	/* internal real-time service curve */
  	struct internal_sc cl_fsc;	/* internal fair service curve */
  	struct internal_sc cl_usc;	/* internal upperlimit service curve */
  	struct runtime_sc cl_deadline;	/* deadline curve */
  	struct runtime_sc cl_eligible;	/* eligible curve */
  	struct runtime_sc cl_virtual;	/* virtual curve */
  	struct runtime_sc cl_ulimit;	/* upperlimit curve */
  
  	unsigned long	cl_flags;	/* which curves are valid */
  	unsigned long	cl_vtperiod;	/* vt period sequence number */
  	unsigned long	cl_parentperiod;/* parent's vt period sequence number*/
  	unsigned long	cl_nactive;	/* number of active children */
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
168
  struct hfsc_sched {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  	u16	defcls;				/* default class id */
  	struct hfsc_class root;			/* root class */
be0d39d52   Patrick McHardy   net-sched: sch_hf...
171
  	struct Qdisc_class_hash clhash;		/* class hash */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
  	struct rb_root eligible;		/* eligible tree */
  	struct list_head droplist;		/* active leaf class list (for
  						   dropping) */
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
175
  	struct qdisc_watchdog watchdog;		/* watchdog timer */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  #define	HT_INFINITY	0xffffffffffffffffULL	/* infinite time value */
  
  
  /*
   * eligible tree holds backlogged classes being sorted by their eligible times.
   * there is one eligible tree per hfsc instance.
   */
  
  static void
  eltree_insert(struct hfsc_class *cl)
  {
  	struct rb_node **p = &cl->sched->eligible.rb_node;
  	struct rb_node *parent = NULL;
  	struct hfsc_class *cl1;
  
  	while (*p != NULL) {
  		parent = *p;
  		cl1 = rb_entry(parent, struct hfsc_class, el_node);
  		if (cl->cl_e >= cl1->cl_e)
  			p = &parent->rb_right;
  		else
  			p = &parent->rb_left;
  	}
  	rb_link_node(&cl->el_node, parent, p);
  	rb_insert_color(&cl->el_node, &cl->sched->eligible);
  }
  
  static inline void
  eltree_remove(struct hfsc_class *cl)
  {
  	rb_erase(&cl->el_node, &cl->sched->eligible);
  }
  
  static inline void
  eltree_update(struct hfsc_class *cl)
  {
  	eltree_remove(cl);
  	eltree_insert(cl);
  }
  
  /* find the class with the minimum deadline among the eligible classes */
  static inline struct hfsc_class *
  eltree_get_mindl(struct hfsc_sched *q, u64 cur_time)
  {
  	struct hfsc_class *p, *cl = NULL;
  	struct rb_node *n;
  
  	for (n = rb_first(&q->eligible); n != NULL; n = rb_next(n)) {
  		p = rb_entry(n, struct hfsc_class, el_node);
  		if (p->cl_e > cur_time)
  			break;
  		if (cl == NULL || p->cl_d < cl->cl_d)
  			cl = p;
  	}
  	return cl;
  }
  
  /* find the class with minimum eligible time among the eligible classes */
  static inline struct hfsc_class *
  eltree_get_minel(struct hfsc_sched *q)
  {
  	struct rb_node *n;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
239

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  	n = rb_first(&q->eligible);
  	if (n == NULL)
  		return NULL;
  	return rb_entry(n, struct hfsc_class, el_node);
  }
  
  /*
   * vttree holds holds backlogged child classes being sorted by their virtual
   * time. each intermediate class has one vttree.
   */
  static void
  vttree_insert(struct hfsc_class *cl)
  {
  	struct rb_node **p = &cl->cl_parent->vt_tree.rb_node;
  	struct rb_node *parent = NULL;
  	struct hfsc_class *cl1;
  
  	while (*p != NULL) {
  		parent = *p;
  		cl1 = rb_entry(parent, struct hfsc_class, vt_node);
  		if (cl->cl_vt >= cl1->cl_vt)
  			p = &parent->rb_right;
  		else
  			p = &parent->rb_left;
  	}
  	rb_link_node(&cl->vt_node, parent, p);
  	rb_insert_color(&cl->vt_node, &cl->cl_parent->vt_tree);
  }
  
  static inline void
  vttree_remove(struct hfsc_class *cl)
  {
  	rb_erase(&cl->vt_node, &cl->cl_parent->vt_tree);
  }
  
  static inline void
  vttree_update(struct hfsc_class *cl)
  {
  	vttree_remove(cl);
  	vttree_insert(cl);
  }
  
  static inline struct hfsc_class *
  vttree_firstfit(struct hfsc_class *cl, u64 cur_time)
  {
  	struct hfsc_class *p;
  	struct rb_node *n;
  
  	for (n = rb_first(&cl->vt_tree); n != NULL; n = rb_next(n)) {
  		p = rb_entry(n, struct hfsc_class, vt_node);
  		if (p->cl_f <= cur_time)
  			return p;
  	}
  	return NULL;
  }
  
  /*
   * get the leaf class with the minimum vt in the hierarchy
   */
  static struct hfsc_class *
  vttree_get_minvt(struct hfsc_class *cl, u64 cur_time)
  {
  	/* if root-class's cfmin is bigger than cur_time nothing to do */
  	if (cl->cl_cfmin > cur_time)
  		return NULL;
  
  	while (cl->level > 0) {
  		cl = vttree_firstfit(cl, cur_time);
  		if (cl == NULL)
  			return NULL;
  		/*
  		 * update parent's cl_cvtmin.
  		 */
  		if (cl->cl_parent->cl_cvtmin < cl->cl_vt)
  			cl->cl_parent->cl_cvtmin = cl->cl_vt;
  	}
  	return cl;
  }
  
  static void
  cftree_insert(struct hfsc_class *cl)
  {
  	struct rb_node **p = &cl->cl_parent->cf_tree.rb_node;
  	struct rb_node *parent = NULL;
  	struct hfsc_class *cl1;
  
  	while (*p != NULL) {
  		parent = *p;
  		cl1 = rb_entry(parent, struct hfsc_class, cf_node);
  		if (cl->cl_f >= cl1->cl_f)
  			p = &parent->rb_right;
  		else
  			p = &parent->rb_left;
  	}
  	rb_link_node(&cl->cf_node, parent, p);
  	rb_insert_color(&cl->cf_node, &cl->cl_parent->cf_tree);
  }
  
  static inline void
  cftree_remove(struct hfsc_class *cl)
  {
  	rb_erase(&cl->cf_node, &cl->cl_parent->cf_tree);
  }
  
  static inline void
  cftree_update(struct hfsc_class *cl)
  {
  	cftree_remove(cl);
  	cftree_insert(cl);
  }
  
  /*
   * service curve support functions
   *
   *  external service curve parameters
   *	m: bps
   *	d: us
   *  internal service curve parameters
   *	sm: (bytes/psched_us) << SM_SHIFT
   *	ism: (psched_us/byte) << ISM_SHIFT
   *	dx: psched_us
   *
728bf0982   Jarek Poplawski   pkt_sched: Use PS...
362
   * The clock source resolution with ktime and PSCHED_SHIFT 10 is 1.024us.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
367
   *
   * sm and ism are scaled in order to keep effective digits.
   * SM_SHIFT and ISM_SHIFT are selected to keep at least 4 effective
   * digits in decimal using the following table.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
   *  bits/sec      100Kbps     1Mbps     10Mbps     100Mbps    1Gbps
   *  ------------+-------------------------------------------------------
641b9e0e8   Patrick McHardy   [NET_SCHED]: Use ...
370
   *  bytes/1.024us 12.8e-3    128e-3     1280e-3    12800e-3   128000e-3
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
   *
641b9e0e8   Patrick McHardy   [NET_SCHED]: Use ...
372
   *  1.024us/byte  78.125     7.8125     0.78125    0.078125   0.0078125
728bf0982   Jarek Poplawski   pkt_sched: Use PS...
373
374
   *
   * So, for PSCHED_SHIFT 10 we need: SM_SHIFT 20, ISM_SHIFT 18.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
   */
728bf0982   Jarek Poplawski   pkt_sched: Use PS...
376
377
  #define	SM_SHIFT	(30 - PSCHED_SHIFT)
  #define	ISM_SHIFT	(8 + PSCHED_SHIFT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  
  #define	SM_MASK		((1ULL << SM_SHIFT) - 1)
  #define	ISM_MASK	((1ULL << ISM_SHIFT) - 1)
  
  static inline u64
  seg_x2y(u64 x, u64 sm)
  {
  	u64 y;
  
  	/*
  	 * compute
  	 *	y = x * sm >> SM_SHIFT
  	 * but divide it for the upper and lower bits to avoid overflow
  	 */
  	y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT);
  	return y;
  }
  
  static inline u64
  seg_y2x(u64 y, u64 ism)
  {
  	u64 x;
  
  	if (y == 0)
  		x = 0;
  	else if (ism == HT_INFINITY)
  		x = HT_INFINITY;
  	else {
  		x = (y >> ISM_SHIFT) * ism
  		    + (((y & ISM_MASK) * ism) >> ISM_SHIFT);
  	}
  	return x;
  }
  
  /* Convert m (bps) into sm (bytes/psched us) */
  static u64
  m2sm(u32 m)
  {
  	u64 sm;
  
  	sm = ((u64)m << SM_SHIFT);
00c04af9d   Patrick McHardy   [NET_SCHED]: kill...
419
420
  	sm += PSCHED_TICKS_PER_SEC - 1;
  	do_div(sm, PSCHED_TICKS_PER_SEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
  	return sm;
  }
  
  /* convert m (bps) into ism (psched us/byte) */
  static u64
  m2ism(u32 m)
  {
  	u64 ism;
  
  	if (m == 0)
  		ism = HT_INFINITY;
  	else {
00c04af9d   Patrick McHardy   [NET_SCHED]: kill...
433
  		ism = ((u64)PSCHED_TICKS_PER_SEC << ISM_SHIFT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
440
441
442
443
444
  		ism += m - 1;
  		do_div(ism, m);
  	}
  	return ism;
  }
  
  /* convert d (us) into dx (psched us) */
  static u64
  d2dx(u32 d)
  {
  	u64 dx;
00c04af9d   Patrick McHardy   [NET_SCHED]: kill...
445
  	dx = ((u64)d * PSCHED_TICKS_PER_SEC);
538e43a4b   Patrick McHardy   [PKT_SCHED]: Use ...
446
447
  	dx += USEC_PER_SEC - 1;
  	do_div(dx, USEC_PER_SEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
  	return dx;
  }
  
  /* convert sm (bytes/psched us) into m (bps) */
  static u32
  sm2m(u64 sm)
  {
  	u64 m;
00c04af9d   Patrick McHardy   [NET_SCHED]: kill...
456
  	m = (sm * PSCHED_TICKS_PER_SEC) >> SM_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
462
463
464
  	return (u32)m;
  }
  
  /* convert dx (psched us) into d (us) */
  static u32
  dx2d(u64 dx)
  {
  	u64 d;
538e43a4b   Patrick McHardy   [PKT_SCHED]: Use ...
465
  	d = dx * USEC_PER_SEC;
00c04af9d   Patrick McHardy   [NET_SCHED]: kill...
466
  	do_div(d, PSCHED_TICKS_PER_SEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  	return (u32)d;
  }
  
  static void
  sc2isc(struct tc_service_curve *sc, struct internal_sc *isc)
  {
  	isc->sm1  = m2sm(sc->m1);
  	isc->ism1 = m2ism(sc->m1);
  	isc->dx   = d2dx(sc->d);
  	isc->dy   = seg_x2y(isc->dx, isc->sm1);
  	isc->sm2  = m2sm(sc->m2);
  	isc->ism2 = m2ism(sc->m2);
  }
  
  /*
   * initialize the runtime service curve with the given internal
   * service curve starting at (x, y).
   */
  static void
  rtsc_init(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
  {
  	rtsc->x	   = x;
  	rtsc->y    = y;
  	rtsc->sm1  = isc->sm1;
  	rtsc->ism1 = isc->ism1;
  	rtsc->dx   = isc->dx;
  	rtsc->dy   = isc->dy;
  	rtsc->sm2  = isc->sm2;
  	rtsc->ism2 = isc->ism2;
  }
  
  /*
   * calculate the y-projection of the runtime service curve by the
   * given x-projection value
   */
  static u64
  rtsc_y2x(struct runtime_sc *rtsc, u64 y)
  {
  	u64 x;
  
  	if (y < rtsc->y)
  		x = rtsc->x;
  	else if (y <= rtsc->y + rtsc->dy) {
  		/* x belongs to the 1st segment */
  		if (rtsc->dy == 0)
  			x = rtsc->x + rtsc->dx;
  		else
  			x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1);
  	} else {
  		/* x belongs to the 2nd segment */
  		x = rtsc->x + rtsc->dx
  		    + seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2);
  	}
  	return x;
  }
  
  static u64
  rtsc_x2y(struct runtime_sc *rtsc, u64 x)
  {
  	u64 y;
  
  	if (x <= rtsc->x)
  		y = rtsc->y;
  	else if (x <= rtsc->x + rtsc->dx)
  		/* y belongs to the 1st segment */
  		y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1);
  	else
  		/* y belongs to the 2nd segment */
  		y = rtsc->y + rtsc->dy
  		    + seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2);
  	return y;
  }
  
  /*
   * update the runtime service curve by taking the minimum of the current
   * runtime service curve and the service curve starting at (x, y).
   */
  static void
  rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
  {
  	u64 y1, y2, dx, dy;
  	u32 dsm;
  
  	if (isc->sm1 <= isc->sm2) {
  		/* service curve is convex */
  		y1 = rtsc_x2y(rtsc, x);
  		if (y1 < y)
  			/* the current rtsc is smaller */
  			return;
  		rtsc->x = x;
  		rtsc->y = y;
  		return;
  	}
  
  	/*
  	 * service curve is concave
  	 * compute the two y values of the current rtsc
  	 *	y1: at x
  	 *	y2: at (x + dx)
  	 */
  	y1 = rtsc_x2y(rtsc, x);
  	if (y1 <= y) {
  		/* rtsc is below isc, no change to rtsc */
  		return;
  	}
  
  	y2 = rtsc_x2y(rtsc, x + isc->dx);
  	if (y2 >= y + isc->dy) {
  		/* rtsc is above isc, replace rtsc by isc */
  		rtsc->x = x;
  		rtsc->y = y;
  		rtsc->dx = isc->dx;
  		rtsc->dy = isc->dy;
  		return;
  	}
  
  	/*
  	 * the two curves intersect
  	 * compute the offsets (dx, dy) using the reverse
  	 * function of seg_x2y()
  	 *	seg_x2y(dx, sm1) == seg_x2y(dx, sm2) + (y1 - y)
  	 */
  	dx = (y1 - y) << SM_SHIFT;
  	dsm = isc->sm1 - isc->sm2;
  	do_div(dx, dsm);
  	/*
  	 * check if (x, y1) belongs to the 1st segment of rtsc.
  	 * if so, add the offset.
  	 */
  	if (rtsc->x + rtsc->dx > x)
  		dx += rtsc->x + rtsc->dx - x;
  	dy = seg_x2y(dx, isc->sm1);
  
  	rtsc->x = x;
  	rtsc->y = y;
  	rtsc->dx = dx;
  	rtsc->dy = dy;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
  }
  
  static void
  init_ed(struct hfsc_class *cl, unsigned int next_len)
  {
3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
609
  	u64 cur_time = psched_get_time();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  
  	/* update the deadline curve */
  	rtsc_min(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
  
  	/*
  	 * update the eligible curve.
  	 * for concave, it is equal to the deadline curve.
  	 * for convex, it is a linear curve with slope m2.
  	 */
  	cl->cl_eligible = cl->cl_deadline;
  	if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
  		cl->cl_eligible.dx = 0;
  		cl->cl_eligible.dy = 0;
  	}
  
  	/* compute e and d */
  	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
  	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
  
  	eltree_insert(cl);
  }
  
  static void
  update_ed(struct hfsc_class *cl, unsigned int next_len)
  {
  	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
  	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
  
  	eltree_update(cl);
  }
  
  static inline void
  update_d(struct hfsc_class *cl, unsigned int next_len)
  {
  	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
  }
  
  static inline void
  update_cfmin(struct hfsc_class *cl)
  {
  	struct rb_node *n = rb_first(&cl->cf_tree);
  	struct hfsc_class *p;
  
  	if (n == NULL) {
  		cl->cl_cfmin = 0;
  		return;
  	}
  	p = rb_entry(n, struct hfsc_class, cf_node);
  	cl->cl_cfmin = p->cl_f;
  }
  
  static void
  init_vf(struct hfsc_class *cl, unsigned int len)
  {
  	struct hfsc_class *max_cl;
  	struct rb_node *n;
  	u64 vt, f, cur_time;
  	int go_active;
  
  	cur_time = 0;
  	go_active = 1;
  	for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
  		if (go_active && cl->cl_nactive++ == 0)
  			go_active = 1;
  		else
  			go_active = 0;
  
  		if (go_active) {
  			n = rb_last(&cl->cl_parent->vt_tree);
  			if (n != NULL) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
680
  				max_cl = rb_entry(n, struct hfsc_class, vt_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
  				/*
  				 * set vt to the average of the min and max
  				 * classes.  if the parent's period didn't
  				 * change, don't decrease vt of the class.
  				 */
  				vt = max_cl->cl_vt;
  				if (cl->cl_parent->cl_cvtmin != 0)
  					vt = (cl->cl_parent->cl_cvtmin + vt)/2;
  
  				if (cl->cl_parent->cl_vtperiod !=
  				    cl->cl_parentperiod || vt > cl->cl_vt)
  					cl->cl_vt = vt;
  			} else {
  				/*
  				 * first child for a new parent backlog period.
  				 * add parent's cvtmax to cvtoff to make a new
  				 * vt (vtoff + vt) larger than the vt in the
  				 * last period for all children.
  				 */
  				vt = cl->cl_parent->cl_cvtmax;
  				cl->cl_parent->cl_cvtoff += vt;
  				cl->cl_parent->cl_cvtmax = 0;
  				cl->cl_parent->cl_cvtmin = 0;
  				cl->cl_vt = 0;
  			}
  
  			cl->cl_vtoff = cl->cl_parent->cl_cvtoff -
  							cl->cl_pcvtoff;
  
  			/* update the virtual curve */
  			vt = cl->cl_vt + cl->cl_vtoff;
  			rtsc_min(&cl->cl_virtual, &cl->cl_fsc, vt,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
713
  						      cl->cl_total);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
  			if (cl->cl_virtual.x == vt) {
  				cl->cl_virtual.x -= cl->cl_vtoff;
  				cl->cl_vtoff = 0;
  			}
  			cl->cl_vtadj = 0;
  
  			cl->cl_vtperiod++;  /* increment vt period */
  			cl->cl_parentperiod = cl->cl_parent->cl_vtperiod;
  			if (cl->cl_parent->cl_nactive == 0)
  				cl->cl_parentperiod++;
  			cl->cl_f = 0;
  
  			vttree_insert(cl);
  			cftree_insert(cl);
  
  			if (cl->cl_flags & HFSC_USC) {
  				/* class has upper limit curve */
  				if (cur_time == 0)
3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
732
  					cur_time = psched_get_time();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
  
  				/* update the ulimit curve */
  				rtsc_min(&cl->cl_ulimit, &cl->cl_usc, cur_time,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
736
  					 cl->cl_total);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
  				/* compute myf */
  				cl->cl_myf = rtsc_y2x(&cl->cl_ulimit,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
739
  						      cl->cl_total);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
745
746
747
  				cl->cl_myfadj = 0;
  			}
  		}
  
  		f = max(cl->cl_myf, cl->cl_cfmin);
  		if (f != cl->cl_f) {
  			cl->cl_f = f;
  			cftree_update(cl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
  		}
3b2eb6131   Michal Soltys   net/sched/sch_hfs...
749
  		update_cfmin(cl->cl_parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
  	}
  }
  
  static void
  update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
  {
  	u64 f; /* , myf_bound, delta; */
  	int go_passive = 0;
  
  	if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC)
  		go_passive = 1;
  
  	for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
  		cl->cl_total += len;
  
  		if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0)
  			continue;
  
  		if (go_passive && --cl->cl_nactive == 0)
  			go_passive = 1;
  		else
  			go_passive = 0;
  
  		if (go_passive) {
  			/* no more active child, going passive */
  
  			/* update cvtmax of the parent class */
  			if (cl->cl_vt > cl->cl_parent->cl_cvtmax)
  				cl->cl_parent->cl_cvtmax = cl->cl_vt;
  
  			/* remove this class from the vt tree */
  			vttree_remove(cl);
  
  			cftree_remove(cl);
  			update_cfmin(cl->cl_parent);
  
  			continue;
  		}
  
  		/*
  		 * update vt and f
  		 */
  		cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total)
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
793
  			    - cl->cl_vtoff + cl->cl_vtadj;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
  
  		/*
  		 * if vt of the class is smaller than cvtmin,
  		 * the class was skipped in the past due to non-fit.
  		 * if so, we need to adjust vtadj.
  		 */
  		if (cl->cl_vt < cl->cl_parent->cl_cvtmin) {
  			cl->cl_vtadj += cl->cl_parent->cl_cvtmin - cl->cl_vt;
  			cl->cl_vt = cl->cl_parent->cl_cvtmin;
  		}
  
  		/* update the vt tree */
  		vttree_update(cl);
  
  		if (cl->cl_flags & HFSC_USC) {
  			cl->cl_myf = cl->cl_myfadj + rtsc_y2x(&cl->cl_ulimit,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
810
  							      cl->cl_total);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
  #if 0
  			/*
  			 * This code causes classes to stay way under their
  			 * limit when multiple classes are used at gigabit
  			 * speed. needs investigation. -kaber
  			 */
  			/*
  			 * if myf lags behind by more than one clock tick
  			 * from the current time, adjust myfadj to prevent
  			 * a rate-limited class from going greedy.
  			 * in a steady state under rate-limiting, myf
  			 * fluctuates within one clock tick.
  			 */
  			myf_bound = cur_time - PSCHED_JIFFIE2US(1);
  			if (cl->cl_myf < myf_bound) {
  				delta = cur_time - cl->cl_myf;
  				cl->cl_myfadj += delta;
  				cl->cl_myf += delta;
  			}
  #endif
  		}
  
  		f = max(cl->cl_myf, cl->cl_cfmin);
  		if (f != cl->cl_f) {
  			cl->cl_f = f;
  			cftree_update(cl);
  			update_cfmin(cl->cl_parent);
  		}
  	}
  }
  
  static void
  set_active(struct hfsc_class *cl, unsigned int len)
  {
  	if (cl->cl_flags & HFSC_RSC)
  		init_ed(cl, len);
  	if (cl->cl_flags & HFSC_FSC)
  		init_vf(cl, len);
  
  	list_add_tail(&cl->dlist, &cl->sched->droplist);
  }
  
  static void
  set_passive(struct hfsc_class *cl)
  {
  	if (cl->cl_flags & HFSC_RSC)
  		eltree_remove(cl);
  
  	list_del(&cl->dlist);
  
  	/*
  	 * vttree is now handled in update_vf() so that update_vf(cl, 0, 0)
  	 * needs to be called explicitly to remove a class from vttree.
  	 */
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
  static unsigned int
  qdisc_peek_len(struct Qdisc *sch)
  {
  	struct sk_buff *skb;
  	unsigned int len;
03c05f0d4   Jarek Poplawski   pkt_sched: Use qd...
871
  	skb = sch->ops->peek(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
  	if (skb == NULL) {
b00355db3   Jarek Poplawski   pkt_sched: sch_hf...
873
  		qdisc_warn_nonwc("qdisc_peek_len", sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
  		return 0;
  	}
0abf77e55   Jussi Kivilinna   net_sched: Add ac...
876
  	len = qdisc_pkt_len(skb);
03c05f0d4   Jarek Poplawski   pkt_sched: Use qd...
877

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
879
880
881
882
883
884
885
886
  	return len;
  }
  
  static void
  hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl)
  {
  	unsigned int len = cl->qdisc->q.qlen;
  
  	qdisc_reset(cl->qdisc);
f973b913e   Patrick McHardy   [NET_SCHED]: Fix ...
887
  	qdisc_tree_decrease_qlen(cl->qdisc, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
891
892
893
894
895
896
897
898
  }
  
  static void
  hfsc_adjust_levels(struct hfsc_class *cl)
  {
  	struct hfsc_class *p;
  	unsigned int level;
  
  	do {
  		level = 0;
  		list_for_each_entry(p, &cl->children, siblings) {
210525d65   Patrick McHardy   [NET_SCHED]: HFSC...
899
900
  			if (p->level >= level)
  				level = p->level + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
  		}
210525d65   Patrick McHardy   [NET_SCHED]: HFSC...
902
  		cl->level = level;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
904
  	} while ((cl = cl->cl_parent) != NULL);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
906
907
908
  static inline struct hfsc_class *
  hfsc_find_class(u32 classid, struct Qdisc *sch)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
909
  	struct Qdisc_class_common *clc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910

be0d39d52   Patrick McHardy   net-sched: sch_hf...
911
912
913
914
  	clc = qdisc_class_find(&q->clhash, classid);
  	if (clc == NULL)
  		return NULL;
  	return container_of(clc, struct hfsc_class, cl_common);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
917
918
  }
  
  static void
  hfsc_change_rsc(struct hfsc_class *cl, struct tc_service_curve *rsc,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
919
  		u64 cur_time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
  {
  	sc2isc(rsc, &cl->cl_rsc);
  	rtsc_init(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
  	cl->cl_eligible = cl->cl_deadline;
  	if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
  		cl->cl_eligible.dx = 0;
  		cl->cl_eligible.dy = 0;
  	}
  	cl->cl_flags |= HFSC_RSC;
  }
  
  static void
  hfsc_change_fsc(struct hfsc_class *cl, struct tc_service_curve *fsc)
  {
  	sc2isc(fsc, &cl->cl_fsc);
  	rtsc_init(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
  	cl->cl_flags |= HFSC_FSC;
  }
  
  static void
  hfsc_change_usc(struct hfsc_class *cl, struct tc_service_curve *usc,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
941
  		u64 cur_time)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
945
946
  {
  	sc2isc(usc, &cl->cl_usc);
  	rtsc_init(&cl->cl_ulimit, &cl->cl_usc, cur_time, cl->cl_total);
  	cl->cl_flags |= HFSC_USC;
  }
27a3421e4   Patrick McHardy   [NET_SCHED]: Use ...
947
948
949
950
951
  static const struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = {
  	[TCA_HFSC_RSC]	= { .len = sizeof(struct tc_service_curve) },
  	[TCA_HFSC_FSC]	= { .len = sizeof(struct tc_service_curve) },
  	[TCA_HFSC_USC]	= { .len = sizeof(struct tc_service_curve) },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
  static int
  hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
954
  		  struct nlattr **tca, unsigned long *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl = (struct hfsc_class *)*arg;
  	struct hfsc_class *parent = NULL;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
959
960
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_HFSC_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  	struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
  	u64 cur_time;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
963
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
965
  	if (opt == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
  		return -EINVAL;
27a3421e4   Patrick McHardy   [NET_SCHED]: Use ...
967
  	err = nla_parse_nested(tb, TCA_HFSC_MAX, opt, hfsc_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
968
969
  	if (err < 0)
  		return err;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
970
  	if (tb[TCA_HFSC_RSC]) {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
971
  		rsc = nla_data(tb[TCA_HFSC_RSC]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
973
974
  		if (rsc->m1 == 0 && rsc->m2 == 0)
  			rsc = NULL;
  	}
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
975
  	if (tb[TCA_HFSC_FSC]) {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
976
  		fsc = nla_data(tb[TCA_HFSC_FSC]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
977
978
979
  		if (fsc->m1 == 0 && fsc->m2 == 0)
  			fsc = NULL;
  	}
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
980
  	if (tb[TCA_HFSC_USC]) {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
981
  		usc = nla_data(tb[TCA_HFSC_USC]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
983
984
985
986
987
  		if (usc->m1 == 0 && usc->m2 == 0)
  			usc = NULL;
  	}
  
  	if (cl != NULL) {
  		if (parentid) {
be0d39d52   Patrick McHardy   net-sched: sch_hf...
988
989
  			if (cl->cl_parent &&
  			    cl->cl_parent->cl_common.classid != parentid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
993
  				return -EINVAL;
  			if (cl->cl_parent == NULL && parentid != TC_H_ROOT)
  				return -EINVAL;
  		}
3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
994
  		cur_time = psched_get_time();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995

71bcb09a5   Stephen Hemminger   tc: check for err...
996
997
998
999
1000
1001
1002
  		if (tca[TCA_RATE]) {
  			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
  					      qdisc_root_sleeping_lock(sch),
  					      tca[TCA_RATE]);
  			if (err)
  				return err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
  		sch_tree_lock(sch);
  		if (rsc != NULL)
  			hfsc_change_rsc(cl, rsc, cur_time);
  		if (fsc != NULL)
  			hfsc_change_fsc(cl, fsc);
  		if (usc != NULL)
  			hfsc_change_usc(cl, usc, cur_time);
  
  		if (cl->qdisc->q.qlen != 0) {
  			if (cl->cl_flags & HFSC_RSC)
  				update_ed(cl, qdisc_peek_len(cl->qdisc));
  			if (cl->cl_flags & HFSC_FSC)
  				update_vf(cl, 0, cur_time);
  		}
  		sch_tree_unlock(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  		return 0;
  	}
  
  	if (parentid == TC_H_ROOT)
  		return -EEXIST;
  
  	parent = &q->root;
  	if (parentid) {
  		parent = hfsc_find_class(parentid, sch);
  		if (parent == NULL)
  			return -ENOENT;
  	}
  
  	if (classid == 0 || TC_H_MAJ(classid ^ sch->handle) != 0)
  		return -EINVAL;
  	if (hfsc_find_class(classid, sch))
  		return -EEXIST;
  
  	if (rsc == NULL && fsc == NULL)
  		return -EINVAL;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1038
  	cl = kzalloc(sizeof(struct hfsc_class), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
  	if (cl == NULL)
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041

71bcb09a5   Stephen Hemminger   tc: check for err...
1042
1043
1044
1045
1046
1047
1048
1049
1050
  	if (tca[TCA_RATE]) {
  		err = gen_new_estimator(&cl->bstats, &cl->rate_est,
  					qdisc_root_sleeping_lock(sch),
  					tca[TCA_RATE]);
  		if (err) {
  			kfree(cl);
  			return err;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
1055
1056
  	if (rsc != NULL)
  		hfsc_change_rsc(cl, rsc, 0);
  	if (fsc != NULL)
  		hfsc_change_fsc(cl, fsc);
  	if (usc != NULL)
  		hfsc_change_usc(cl, usc, 0);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1057
  	cl->cl_common.classid = classid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
  	cl->refcnt    = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
1060
  	cl->sched     = q;
  	cl->cl_parent = parent;
3511c9132   Changli Gao   net_sched: remove...
1061
  	cl->qdisc = qdisc_create_dflt(sch->dev_queue,
bb949fbd1   David S. Miller   netdev: Create ne...
1062
  				      &pfifo_qdisc_ops, classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
1064
  	if (cl->qdisc == NULL)
  		cl->qdisc = &noop_qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
1069
  	INIT_LIST_HEAD(&cl->children);
  	cl->vt_tree = RB_ROOT;
  	cl->cf_tree = RB_ROOT;
  
  	sch_tree_lock(sch);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1070
  	qdisc_class_hash_insert(&q->clhash, &cl->cl_common);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
1072
1073
1074
1075
1076
  	list_add_tail(&cl->siblings, &parent->children);
  	if (parent->level == 0)
  		hfsc_purge_queue(sch, parent);
  	hfsc_adjust_levels(parent);
  	cl->cl_pcvtoff = parent->cl_cvtoff;
  	sch_tree_unlock(sch);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1077
  	qdisc_class_hash_grow(sch, &q->clhash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
  	*arg = (unsigned long)cl;
  	return 0;
  }
  
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
1085
  hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
ff31ab56c   Patrick McHardy   net-sched: change...
1086
  	tcf_destroy_chain(&cl->filter_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
  	qdisc_destroy(cl->qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
  	gen_kill_estimator(&cl->bstats, &cl->rate_est);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
  	if (cl != &q->root)
  		kfree(cl);
  }
  
  static int
  hfsc_delete_class(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  
  	if (cl->level > 0 || cl->filter_cnt > 0 || cl == &q->root)
  		return -EBUSY;
  
  	sch_tree_lock(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
1104
  	list_del(&cl->siblings);
  	hfsc_adjust_levels(cl->cl_parent);
c38c83cb7   Patrick McHardy   [NET_SCHED]: sch_...
1105

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
  	hfsc_purge_queue(sch, cl);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1107
  	qdisc_class_hash_remove(&q->clhash, &cl->cl_common);
c38c83cb7   Patrick McHardy   [NET_SCHED]: sch_...
1108

7cd0a6387   Jarek Poplawski   pkt_sched: Change...
1109
1110
1111
1112
1113
  	BUG_ON(--cl->refcnt == 0);
  	/*
  	 * This shouldn't happen: we "hold" one cops->get() when called
  	 * from tc_ctl_tclass; the destroy method is done from cops->put().
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114
1115
1116
1117
1118
1119
1120
1121
1122
  
  	sch_tree_unlock(sch);
  	return 0;
  }
  
  static struct hfsc_class *
  hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
a2f792271   Patrick McHardy   net_sched: sch_hf...
1123
  	struct hfsc_class *head, *cl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1124
1125
1126
1127
1128
1129
1130
1131
  	struct tcf_result res;
  	struct tcf_proto *tcf;
  	int result;
  
  	if (TC_H_MAJ(skb->priority ^ sch->handle) == 0 &&
  	    (cl = hfsc_find_class(skb->priority, sch)) != NULL)
  		if (cl->level == 0)
  			return cl;
c27f339af   Jarek Poplawski   net_sched: Add qd...
1132
  	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
a2f792271   Patrick McHardy   net_sched: sch_hf...
1133
  	head = &q->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1134
1135
1136
1137
1138
  	tcf = q->root.filter_list;
  	while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) {
  #ifdef CONFIG_NET_CLS_ACT
  		switch (result) {
  		case TC_ACT_QUEUED:
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1139
  		case TC_ACT_STOLEN:
378a2f090   Jarek Poplawski   net_sched: Add qd...
1140
  			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1141
  		case TC_ACT_SHOT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
1143
  			return NULL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
  #endif
cc7ec456f   Eric Dumazet   net_sched: cleanups
1145
1146
1147
1148
  		cl = (struct hfsc_class *)res.class;
  		if (!cl) {
  			cl = hfsc_find_class(res.classid, sch);
  			if (!cl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1149
  				break; /* filter selected invalid classid */
a2f792271   Patrick McHardy   net_sched: sch_hf...
1150
1151
  			if (cl->level >= head->level)
  				break; /* filter may only point downwards */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1152
1153
1154
1155
1156
1157
1158
  		}
  
  		if (cl->level == 0)
  			return cl; /* hit leaf class */
  
  		/* apply inner filter chain */
  		tcf = cl->filter_list;
a2f792271   Patrick McHardy   net_sched: sch_hf...
1159
  		head = cl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
  	}
  
  	/* classification failed, try default class */
  	cl = hfsc_find_class(TC_H_MAKE(TC_H_MAJ(sch->handle), q->defcls), sch);
  	if (cl == NULL || cl->level > 0)
  		return NULL;
  
  	return cl;
  }
  
  static int
  hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1172
  		 struct Qdisc **old)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1173
1174
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1175
1176
1177
  	if (cl->level > 0)
  		return -EINVAL;
  	if (new == NULL) {
3511c9132   Changli Gao   net_sched: remove...
1178
  		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1179
  					cl->cl_common.classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
1182
1183
1184
1185
  		if (new == NULL)
  			new = &noop_qdisc;
  	}
  
  	sch_tree_lock(sch);
  	hfsc_purge_queue(sch, cl);
b94c8afcb   Patrick McHardy   pkt_sched: remove...
1186
1187
  	*old = cl->qdisc;
  	cl->qdisc = new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1188
1189
1190
1191
1192
1193
1194
1195
  	sch_tree_unlock(sch);
  	return 0;
  }
  
  static struct Qdisc *
  hfsc_class_leaf(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
5b9a9ccfa   Patrick McHardy   net_sched: remove...
1196
  	if (cl->level == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1197
1198
1199
1200
  		return cl->qdisc;
  
  	return NULL;
  }
f973b913e   Patrick McHardy   [NET_SCHED]: Fix ...
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
  static void
  hfsc_qlen_notify(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  
  	if (cl->qdisc->q.qlen == 0) {
  		update_vf(cl, 0, 0);
  		set_passive(cl);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
  static unsigned long
  hfsc_get_class(struct Qdisc *sch, u32 classid)
  {
  	struct hfsc_class *cl = hfsc_find_class(classid, sch);
  
  	if (cl != NULL)
  		cl->refcnt++;
  
  	return (unsigned long)cl;
  }
  
  static void
  hfsc_put_class(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  
  	if (--cl->refcnt == 0)
  		hfsc_destroy_class(sch, cl);
  }
  
  static unsigned long
  hfsc_bind_tcf(struct Qdisc *sch, unsigned long parent, u32 classid)
  {
  	struct hfsc_class *p = (struct hfsc_class *)parent;
  	struct hfsc_class *cl = hfsc_find_class(classid, sch);
  
  	if (cl != NULL) {
  		if (p != NULL && p->level <= cl->level)
  			return 0;
  		cl->filter_cnt++;
  	}
  
  	return (unsigned long)cl;
  }
  
  static void
  hfsc_unbind_tcf(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  
  	cl->filter_cnt--;
  }
  
  static struct tcf_proto **
  hfsc_tcf_chain(struct Qdisc *sch, unsigned long arg)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  
  	if (cl == NULL)
  		cl = &q->root;
  
  	return &cl->filter_list;
  }
  
  static int
  hfsc_dump_sc(struct sk_buff *skb, int attr, struct internal_sc *sc)
  {
  	struct tc_service_curve tsc;
  
  	tsc.m1 = sm2m(sc->sm1);
  	tsc.d  = dx2d(sc->dx);
  	tsc.m2 = sm2m(sc->sm2);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1274
  	NLA_PUT(skb, attr, sizeof(tsc), &tsc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275
1276
  
  	return skb->len;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1277
   nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1278
1279
  	return -1;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
1280
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1281
1282
1283
1284
  hfsc_dump_curves(struct sk_buff *skb, struct hfsc_class *cl)
  {
  	if ((cl->cl_flags & HFSC_RSC) &&
  	    (hfsc_dump_sc(skb, TCA_HFSC_RSC, &cl->cl_rsc) < 0))
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1285
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286
1287
1288
  
  	if ((cl->cl_flags & HFSC_FSC) &&
  	    (hfsc_dump_sc(skb, TCA_HFSC_FSC, &cl->cl_fsc) < 0))
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1289
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
1292
  
  	if ((cl->cl_flags & HFSC_USC) &&
  	    (hfsc_dump_sc(skb, TCA_HFSC_USC, &cl->cl_usc) < 0))
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1293
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
  
  	return skb->len;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1296
   nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1297
1298
1299
1300
1301
  	return -1;
  }
  
  static int
  hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1302
  		struct tcmsg *tcm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1303
1304
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1305
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1306

be0d39d52   Patrick McHardy   net-sched: sch_hf...
1307
1308
1309
  	tcm->tcm_parent = cl->cl_parent ? cl->cl_parent->cl_common.classid :
  					  TC_H_ROOT;
  	tcm->tcm_handle = cl->cl_common.classid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
1311
  	if (cl->level == 0)
  		tcm->tcm_info = cl->qdisc->handle;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1312
1313
1314
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1315
  	if (hfsc_dump_curves(skb, cl) < 0)
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1316
  		goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1317
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
  	return skb->len;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1319
   nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1320
  	nla_nest_cancel(skb, nest);
bc3ed28ca   Thomas Graf   netlink: Improve ...
1321
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
  }
  
  static int
  hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
  	struct gnet_dump *d)
  {
  	struct hfsc_class *cl = (struct hfsc_class *)arg;
  	struct tc_hfsc_stats xstats;
  
  	cl->qstats.qlen = cl->qdisc->q.qlen;
f5a59b733   Eric Dumazet   sch_hfsc: report ...
1332
  	cl->qstats.backlog = cl->qdisc->qstats.backlog;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1333
1334
1335
1336
1337
1338
  	xstats.level   = cl->level;
  	xstats.period  = cl->cl_vtperiod;
  	xstats.work    = cl->cl_total;
  	xstats.rtwork  = cl->cl_cumul;
  
  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
d250a5f90   Eric Dumazet   pkt_sched: gen_es...
1339
  	    gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
  	    gnet_stats_copy_queue(d, &cl->qstats) < 0)
  		return -1;
  
  	return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
  }
  
  
  
  static void
  hfsc_walk(struct Qdisc *sch, struct qdisc_walker *arg)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1352
  	struct hlist_node *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1353
1354
1355
1356
1357
  	struct hfsc_class *cl;
  	unsigned int i;
  
  	if (arg->stop)
  		return;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1358
1359
1360
  	for (i = 0; i < q->clhash.hashsize; i++) {
  		hlist_for_each_entry(cl, n, &q->clhash.hash[i],
  				     cl_common.hnode) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
  			if (arg->count < arg->skip) {
  				arg->count++;
  				continue;
  			}
  			if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
  				arg->stop = 1;
  				return;
  			}
  			arg->count++;
  		}
  	}
  }
  
  static void
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1375
  hfsc_schedule_watchdog(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1376
1377
1378
1379
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl;
  	u64 next_time = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1380

cc7ec456f   Eric Dumazet   net_sched: cleanups
1381
1382
  	cl = eltree_get_minel(q);
  	if (cl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
1384
1385
1386
1387
  		next_time = cl->cl_e;
  	if (q->root.cl_cfmin != 0) {
  		if (next_time == 0 || next_time > q->root.cl_cfmin)
  			next_time = q->root.cl_cfmin;
  	}
3d50f2310   Patrick McHardy   [NET_SCHED]: sch_...
1388
  	WARN_ON(next_time == 0);
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1389
  	qdisc_watchdog_schedule(&q->watchdog, next_time);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1390
1391
1392
  }
  
  static int
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1393
  hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1394
1395
1396
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct tc_hfsc_qopt *qopt;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1397
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1398

1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1399
  	if (opt == NULL || nla_len(opt) < sizeof(*qopt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1400
  		return -EINVAL;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1401
  	qopt = nla_data(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1403
  	q->defcls = qopt->defcls;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1404
1405
1406
  	err = qdisc_class_hash_init(&q->clhash);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1407
1408
  	q->eligible = RB_ROOT;
  	INIT_LIST_HEAD(&q->droplist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409

be0d39d52   Patrick McHardy   net-sched: sch_hf...
1410
  	q->root.cl_common.classid = sch->handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1411
  	q->root.refcnt  = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1412
  	q->root.sched   = q;
3511c9132   Changli Gao   net_sched: remove...
1413
  	q->root.qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
9f9afec48   Patrick McHardy   [NET_SCHED]: Set ...
1414
  					  sch->handle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1415
1416
  	if (q->root.qdisc == NULL)
  		q->root.qdisc = &noop_qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1417
1418
1419
  	INIT_LIST_HEAD(&q->root.children);
  	q->root.vt_tree = RB_ROOT;
  	q->root.cf_tree = RB_ROOT;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1420
1421
  	qdisc_class_hash_insert(&q->clhash, &q->root.cl_common);
  	qdisc_class_hash_grow(sch, &q->clhash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422

ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1423
  	qdisc_watchdog_init(&q->watchdog, sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1424
1425
1426
1427
1428
  
  	return 0;
  }
  
  static int
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1429
  hfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1430
1431
1432
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct tc_hfsc_qopt *qopt;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1433
  	if (opt == NULL || nla_len(opt) < sizeof(*qopt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1434
  		return -EINVAL;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1435
  	qopt = nla_data(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
  
  	sch_tree_lock(sch);
  	q->defcls = qopt->defcls;
  	sch_tree_unlock(sch);
  
  	return 0;
  }
  
  static void
  hfsc_reset_class(struct hfsc_class *cl)
  {
  	cl->cl_total        = 0;
  	cl->cl_cumul        = 0;
  	cl->cl_d            = 0;
  	cl->cl_e            = 0;
  	cl->cl_vt           = 0;
  	cl->cl_vtadj        = 0;
  	cl->cl_vtoff        = 0;
  	cl->cl_cvtmin       = 0;
  	cl->cl_cvtmax       = 0;
  	cl->cl_cvtoff       = 0;
  	cl->cl_pcvtoff      = 0;
  	cl->cl_vtperiod     = 0;
  	cl->cl_parentperiod = 0;
  	cl->cl_f            = 0;
  	cl->cl_myf          = 0;
  	cl->cl_myfadj       = 0;
  	cl->cl_cfmin        = 0;
  	cl->cl_nactive      = 0;
  
  	cl->vt_tree = RB_ROOT;
  	cl->cf_tree = RB_ROOT;
  	qdisc_reset(cl->qdisc);
  
  	if (cl->cl_flags & HFSC_RSC)
  		rtsc_init(&cl->cl_deadline, &cl->cl_rsc, 0, 0);
  	if (cl->cl_flags & HFSC_FSC)
  		rtsc_init(&cl->cl_virtual, &cl->cl_fsc, 0, 0);
  	if (cl->cl_flags & HFSC_USC)
  		rtsc_init(&cl->cl_ulimit, &cl->cl_usc, 0, 0);
  }
  
  static void
  hfsc_reset_qdisc(struct Qdisc *sch)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1483
  	struct hlist_node *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1484
  	unsigned int i;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1485
1486
  	for (i = 0; i < q->clhash.hashsize; i++) {
  		hlist_for_each_entry(cl, n, &q->clhash.hash[i], cl_common.hnode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
1488
  			hfsc_reset_class(cl);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1489
1490
  	q->eligible = RB_ROOT;
  	INIT_LIST_HEAD(&q->droplist);
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1491
  	qdisc_watchdog_cancel(&q->watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1492
1493
1494
1495
1496
1497
1498
  	sch->q.qlen = 0;
  }
  
  static void
  hfsc_destroy_qdisc(struct Qdisc *sch)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1499
1500
  	struct hlist_node *n, *next;
  	struct hfsc_class *cl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1501
  	unsigned int i;
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1502
1503
  	for (i = 0; i < q->clhash.hashsize; i++) {
  		hlist_for_each_entry(cl, n, &q->clhash.hash[i], cl_common.hnode)
a4aebb83c   Patrick McHardy   net-sched: fix fi...
1504
1505
  			tcf_destroy_chain(&cl->filter_list);
  	}
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1506
1507
1508
  	for (i = 0; i < q->clhash.hashsize; i++) {
  		hlist_for_each_entry_safe(cl, n, next, &q->clhash.hash[i],
  					  cl_common.hnode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1509
1510
  			hfsc_destroy_class(sch, cl);
  	}
be0d39d52   Patrick McHardy   net-sched: sch_hf...
1511
  	qdisc_class_hash_destroy(&q->clhash);
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1512
  	qdisc_watchdog_cancel(&q->watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1513
1514
1515
1516
1517
1518
  }
  
  static int
  hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1519
  	unsigned char *b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1520
  	struct tc_hfsc_qopt qopt;
f5a59b733   Eric Dumazet   sch_hfsc: report ...
1521
1522
1523
1524
1525
1526
1527
1528
1529
  	struct hfsc_class *cl;
  	struct hlist_node *n;
  	unsigned int i;
  
  	sch->qstats.backlog = 0;
  	for (i = 0; i < q->clhash.hashsize; i++) {
  		hlist_for_each_entry(cl, n, &q->clhash.hash[i], cl_common.hnode)
  			sch->qstats.backlog += cl->qdisc->qstats.backlog;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1530
1531
  
  	qopt.defcls = q->defcls;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1532
  	NLA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1533
  	return skb->len;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1534
   nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1535
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1536
1537
1538
1539
1540
1541
1542
  	return -1;
  }
  
  static int
  hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
  {
  	struct hfsc_class *cl;
dc0a0011c   Ingo Molnar   pkt_sched: fix wa...
1543
  	int uninitialized_var(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1544
1545
1546
  
  	cl = hfsc_classify(skb, sch, &err);
  	if (cl == NULL) {
c27f339af   Jarek Poplawski   net_sched: Add qd...
1547
  		if (err & __NET_XMIT_BYPASS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1548
1549
1550
1551
  			sch->qstats.drops++;
  		kfree_skb(skb);
  		return err;
  	}
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
1552
  	err = qdisc_enqueue(skb, cl->qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1553
  	if (unlikely(err != NET_XMIT_SUCCESS)) {
378a2f090   Jarek Poplawski   net_sched: Add qd...
1554
1555
1556
1557
  		if (net_xmit_drop_count(err)) {
  			cl->qstats.drops++;
  			sch->qstats.drops++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1558
1559
1560
1561
  		return err;
  	}
  
  	if (cl->qdisc->q.qlen == 1)
0abf77e55   Jussi Kivilinna   net_sched: Add ac...
1562
  		set_active(cl, qdisc_pkt_len(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1563

bfe0d0298   Eric Dumazet   net_sched: factor...
1564
  	bstats_update(&cl->bstats, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
  	sch->q.qlen++;
  
  	return NET_XMIT_SUCCESS;
  }
  
  static struct sk_buff *
  hfsc_dequeue(struct Qdisc *sch)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl;
  	struct sk_buff *skb;
  	u64 cur_time;
  	unsigned int next_len;
  	int realtime = 0;
  
  	if (sch->q.qlen == 0)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1582

3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
1583
  	cur_time = psched_get_time();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1584
1585
1586
1587
1588
1589
  
  	/*
  	 * if there are eligible classes, use real-time criteria.
  	 * find the class with the minimum deadline among
  	 * the eligible classes.
  	 */
cc7ec456f   Eric Dumazet   net_sched: cleanups
1590
1591
  	cl = eltree_get_mindl(q, cur_time);
  	if (cl) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1592
1593
1594
1595
1596
1597
1598
1599
1600
  		realtime = 1;
  	} else {
  		/*
  		 * use link-sharing criteria
  		 * get the class with the minimum vt in the hierarchy
  		 */
  		cl = vttree_get_minvt(&q->root, cur_time);
  		if (cl == NULL) {
  			sch->qstats.overlimits++;
ed2b229a9   Patrick McHardy   [NET_SCHED]: sch_...
1601
  			hfsc_schedule_watchdog(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1602
1603
1604
  			return NULL;
  		}
  	}
77be155cb   Jarek Poplawski   pkt_sched: Add pe...
1605
  	skb = qdisc_dequeue_peeked(cl->qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1606
  	if (skb == NULL) {
b00355db3   Jarek Poplawski   pkt_sched: sch_hf...
1607
  		qdisc_warn_nonwc("HFSC", cl->qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1608
1609
  		return NULL;
  	}
0abf77e55   Jussi Kivilinna   net_sched: Add ac...
1610
  	update_vf(cl, qdisc_pkt_len(skb), cur_time);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1611
  	if (realtime)
0abf77e55   Jussi Kivilinna   net_sched: Add ac...
1612
  		cl->cl_cumul += qdisc_pkt_len(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
  
  	if (cl->qdisc->q.qlen != 0) {
  		if (cl->cl_flags & HFSC_RSC) {
  			/* update ed */
  			next_len = qdisc_peek_len(cl->qdisc);
  			if (realtime)
  				update_ed(cl, next_len);
  			else
  				update_d(cl, next_len);
  		}
  	} else {
  		/* the class becomes passive */
  		set_passive(cl);
  	}
fd245a4ad   Eric Dumazet   net_sched: move T...
1627
  	qdisc_unthrottled(sch);
9190b3b32   Eric Dumazet   net_sched: accura...
1628
  	qdisc_bstats_update(sch, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1629
1630
1631
1632
  	sch->q.qlen--;
  
  	return skb;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
  static unsigned int
  hfsc_drop(struct Qdisc *sch)
  {
  	struct hfsc_sched *q = qdisc_priv(sch);
  	struct hfsc_class *cl;
  	unsigned int len;
  
  	list_for_each_entry(cl, &q->droplist, dlist) {
  		if (cl->qdisc->ops->drop != NULL &&
  		    (len = cl->qdisc->ops->drop(cl->qdisc)) > 0) {
  			if (cl->qdisc->q.qlen == 0) {
  				update_vf(cl, 0, 0);
  				set_passive(cl);
  			} else {
  				list_move_tail(&cl->dlist, &q->droplist);
  			}
  			cl->qstats.drops++;
  			sch->qstats.drops++;
  			sch->q.qlen--;
  			return len;
  		}
  	}
  	return 0;
  }
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1657
  static const struct Qdisc_class_ops hfsc_class_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658
1659
1660
1661
  	.change		= hfsc_change_class,
  	.delete		= hfsc_delete_class,
  	.graft		= hfsc_graft_class,
  	.leaf		= hfsc_class_leaf,
f973b913e   Patrick McHardy   [NET_SCHED]: Fix ...
1662
  	.qlen_notify	= hfsc_qlen_notify,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
1664
1665
1666
1667
1668
1669
1670
1671
  	.get		= hfsc_get_class,
  	.put		= hfsc_put_class,
  	.bind_tcf	= hfsc_bind_tcf,
  	.unbind_tcf	= hfsc_unbind_tcf,
  	.tcf_chain	= hfsc_tcf_chain,
  	.dump		= hfsc_dump_class,
  	.dump_stats	= hfsc_dump_class_stats,
  	.walk		= hfsc_walk
  };
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1672
  static struct Qdisc_ops hfsc_qdisc_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1673
1674
1675
1676
1677
1678
1679
1680
  	.id		= "hfsc",
  	.init		= hfsc_init_qdisc,
  	.change		= hfsc_change_qdisc,
  	.reset		= hfsc_reset_qdisc,
  	.destroy	= hfsc_destroy_qdisc,
  	.dump		= hfsc_dump_qdisc,
  	.enqueue	= hfsc_enqueue,
  	.dequeue	= hfsc_dequeue,
77be155cb   Jarek Poplawski   pkt_sched: Add pe...
1681
  	.peek		= qdisc_peek_dequeued,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
  	.drop		= hfsc_drop,
  	.cl_ops		= &hfsc_class_ops,
  	.priv_size	= sizeof(struct hfsc_sched),
  	.owner		= THIS_MODULE
  };
  
  static int __init
  hfsc_init(void)
  {
  	return register_qdisc(&hfsc_qdisc_ops);
  }
  
  static void __exit
  hfsc_cleanup(void)
  {
  	unregister_qdisc(&hfsc_qdisc_ops);
  }
  
  MODULE_LICENSE("GPL");
  module_init(hfsc_init);
  module_exit(hfsc_cleanup);