Projet

Général

Profil

Télécharger (51 ko) Statistiques
| Branche: | Révision:

univnautes-tools / patches / stable / 10 / fairq.RELENG_10.diff @ 4ab3b90b

1
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
2
index c99403d..ce64898 100644
3
--- a/sbin/pfctl/parse.y
4
+++ b/sbin/pfctl/parse.y
5
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
6
 #include <altq/altq_cbq.h>
7
 #include <altq/altq_priq.h>
8
 #include <altq/altq_hfsc.h>
9
+#include <altq/altq_fairq.h>
10
 
11
 #include <stdio.h>
12
 #include <unistd.h>
13
@@ -306,6 +307,7 @@ struct pool_opts {
14
 
15
 
16
 struct node_hfsc_opts	 hfsc_opts;
17
+struct node_fairq_opts	 fairq_opts;
18
 struct node_state_opt	*keep_state_defaults = NULL;
19
 
20
 int		 disallow_table(struct node_host *, const char *);
21
@@ -429,6 +431,7 @@ typedef struct {
22
 		struct table_opts	 table_opts;
23
 		struct pool_opts	 pool_opts;
24
 		struct node_hfsc_opts	 hfsc_opts;
25
+		struct node_fairq_opts	fairq_opts;
26
 	} v;
27
 	int lineno;
28
 } YYSTYPE;
29
@@ -453,8 +456,8 @@ int	parseport(char *, struct range *r, int);
30
 %token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
31
 %token	ANTISPOOF FOR INCLUDE
32
 %token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
33
-%token	ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
34
-%token	QUEUE PRIORITY QLIMIT RTABLE
35
+%token	ALTQ CBQ PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
36
+%token	QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE
37
 %token  DNPIPE DNQUEUE 
38
 %token	LOAD RULESET_OPTIMIZATION
39
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
40
@@ -503,6 +506,7 @@ int	parseport(char *, struct range *r, int);
41
 %type	<v.number>		cbqflags_list cbqflags_item
42
 %type	<v.number>		priqflags_list priqflags_item
43
 %type	<v.hfsc_opts>		hfscopts_list hfscopts_item hfsc_opts
44
+%type	<v.fairq_opts>		fairqopts_list fairqopts_item fairq_opts
45
 %type	<v.queue_bwspec>	bandwidth
46
 %type	<v.filter_opts>		filter_opts filter_opt filter_opts_l
47
 %type	<v.antispoof_opts>	antispoof_opts antispoof_opt antispoof_opts_l
48
@@ -1675,6 +1679,15 @@ scheduler	: CBQ				{
49
 			$$.qtype = ALTQT_HFSC;
50
 			$$.data.hfsc_opts = $3;
51
 		}
52
+		| FAIRQ				{
53
+			$$.qtype = ALTQT_FAIRQ;
54
+			bzero(&$$.data.fairq_opts,
55
+				sizeof(struct node_fairq_opts));
56
+		}
57
+                | FAIRQ '(' fairq_opts ')'      {
58
+                        $$.qtype = ALTQT_FAIRQ;
59
+                        $$.data.fairq_opts = $3;
60
+                }
61
 		;
62
 
63
 cbqflags_list	: cbqflags_item				{ $$ |= $1; }
64
@@ -1823,6 +1836,61 @@ hfscopts_item	: LINKSHARE bandwidth				{
65
 		}
66
 		;
67
 
68
+fairq_opts	:	{
69
+				bzero(&fairq_opts,
70
+				    sizeof(struct node_fairq_opts));
71
+			}
72
+		    fairqopts_list				{
73
+			$$ = fairq_opts;
74
+		}
75
+		;
76
+
77
+fairqopts_list	: fairqopts_item
78
+		| fairqopts_list comma fairqopts_item
79
+		;
80
+
81
+fairqopts_item	: LINKSHARE bandwidth				{
82
+			if (fairq_opts.linkshare.used) {
83
+				yyerror("linkshare already specified");
84
+				YYERROR;
85
+			}
86
+			fairq_opts.linkshare.m2 = $2;
87
+			fairq_opts.linkshare.used = 1;
88
+		}
89
+		| LINKSHARE '(' bandwidth number bandwidth ')'	{
90
+			if (fairq_opts.linkshare.used) {
91
+				yyerror("linkshare already specified");
92
+				YYERROR;
93
+			}
94
+			fairq_opts.linkshare.m1 = $3;
95
+			fairq_opts.linkshare.d = $4;
96
+			fairq_opts.linkshare.m2 = $5;
97
+			fairq_opts.linkshare.used = 1;
98
+		}
99
+		| HOGS bandwidth {
100
+			fairq_opts.hogs_bw = $2;
101
+		}
102
+		| BUCKETS number {
103
+			fairq_opts.nbuckets = $2;
104
+		}
105
+		| STRING	{
106
+			if (!strcmp($1, "default"))
107
+				fairq_opts.flags |= FARF_DEFAULTCLASS;
108
+			else if (!strcmp($1, "red"))
109
+				fairq_opts.flags |= FARF_RED;
110
+			else if (!strcmp($1, "ecn"))
111
+				fairq_opts.flags |= FARF_RED|FARF_ECN;
112
+			else if (!strcmp($1, "rio"))
113
+				fairq_opts.flags |= FARF_RIO;
114
+			else {
115
+				yyerror("unknown fairq flag \"%s\"", $1);
116
+				free($1);
117
+				YYERROR;
118
+			}
119
+			free($1);
120
+		}
121
+		;
122
+
123
 qassign		: /* empty */		{ $$ = NULL; }
124
 		| qassign_item		{ $$ = $1; }
125
 		| '{' optnl qassign_list '}'	{ $$ = $3; }
126
@@ -4939,7 +5007,8 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces,
127
 				if (n == NULL)
128
 					err(1, "expand_altq: calloc");
129
 				if (pa.scheduler == ALTQT_CBQ ||
130
-				    pa.scheduler == ALTQT_HFSC)
131
+				    pa.scheduler == ALTQT_HFSC /* ||
132
+				    pa.scheduler == ALTQT_FAIRQ */)
133
 					if (strlcpy(n->parent, qname,
134
 					    sizeof(n->parent)) >=
135
 					    sizeof(n->parent))
136
@@ -5374,6 +5443,7 @@ lookup(char *s)
137
 		{ "bitmask",		BITMASK},
138
 		{ "block",		BLOCK},
139
 		{ "block-policy",	BLOCKPOLICY},
140
+		{ "buckets",		BUCKETS},
141
 		{ "cbq",		CBQ},
142
 		{ "code",		CODE},
143
 		{ "crop",		FRAGCROP},
144
@@ -5386,6 +5456,7 @@ lookup(char *s)
145
 		{ "drop-ovl",		FRAGDROP},
146
 		{ "dscp",		DSCP},
147
 		{ "dup-to",		DUPTO},
148
+		{ "fairq",		FAIRQ},
149
 		{ "fastroute",		FASTROUTE},
150
 		{ "file",		FILENAME},
151
 		{ "fingerprints",	FINGERPRINTS},
152
@@ -5398,6 +5469,7 @@ lookup(char *s)
153
 		{ "global",		GLOBAL},
154
 		{ "group",		GROUP},
155
 		{ "hfsc",		HFSC},
156
+		{ "hogs",		HOGS},
157
 		{ "hostid",		HOSTID},
158
 		{ "icmp-type",		ICMPTYPE},
159
 		{ "icmp6-type",		ICMP6TYPE},
160
diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c
161
index 6886974..58ea61e 100644
162
--- a/sbin/pfctl/pfctl_altq.c
163
+++ b/sbin/pfctl/pfctl_altq.c
164
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
165
 #include <altq/altq_cbq.h>
166
 #include <altq/altq_priq.h>
167
 #include <altq/altq_hfsc.h>
168
+#include <altq/altq_fairq.h>
169
 
170
 #include "pfctl_parser.h"
171
 #include "pfctl.h"
172
@@ -68,6 +69,11 @@ static int	check_commit_hfsc(int, int, struct pf_altq *);
173
 static int	print_hfsc_opts(const struct pf_altq *,
174
 		    const struct node_queue_opt *);
175
 
176
+static int	eval_pfqueue_fairq(struct pfctl *, struct pf_altq *);
177
+static int	print_fairq_opts(const struct pf_altq *,
178
+		    const struct node_queue_opt *);
179
+static int	check_commit_fairq(int, int, struct pf_altq *);
180
+
181
 static void		 gsc_add_sc(struct gen_sc *, struct service_curve *);
182
 static int		 is_gsc_under_sc(struct gen_sc *,
183
 			     struct service_curve *);
184
@@ -88,6 +94,8 @@ int		 eval_queue_opts(struct pf_altq *, struct node_queue_opt *,
185
 u_int32_t	 eval_bwspec(struct node_queue_bw *, u_int32_t);
186
 void		 print_hfsc_sc(const char *, u_int, u_int, u_int,
187
 		     const struct node_hfsc_sc *);
188
+void		 print_fairq_sc(const char *, u_int, u_int, u_int,
189
+		     const struct node_fairq_sc *);
190
 
191
 void
192
 pfaltq_store(struct pf_altq *a)
193
@@ -173,6 +181,10 @@ print_altq(const struct pf_altq *a, unsigned int level,
194
 		if (!print_hfsc_opts(a, qopts))
195
 			printf("hfsc ");
196
 		break;
197
+	case ALTQT_FAIRQ:
198
+		if (!print_fairq_opts(a, qopts))
199
+			printf("fairq ");
200
+		break;
201
 	}
202
 
203
 	if (bw != NULL && bw->bw_percent > 0) {
204
@@ -203,7 +215,8 @@ print_queue(const struct pf_altq *a, unsigned int level,
205
 	printf("%s ", a->qname);
206
 	if (print_interface)
207
 		printf("on %s ", a->ifname);
208
-	if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) {
209
+	if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC ||
210
+		a->scheduler == ALTQT_FAIRQ) {
211
 		if (bw != NULL && bw->bw_percent > 0) {
212
 			if (bw->bw_percent < 100)
213
 				printf("bandwidth %u%% ", bw->bw_percent);
214
@@ -224,6 +237,9 @@ print_queue(const struct pf_altq *a, unsigned int level,
215
 	case ALTQT_HFSC:
216
 		print_hfsc_opts(a, qopts);
217
 		break;
218
+	case ALTQT_FAIRQ:
219
+		print_fairq_opts(a, qopts);
220
+		break;
221
 	}
222
 }
223
 
224
@@ -294,6 +310,9 @@ check_commit_altq(int dev, int opts)
225
 			case ALTQT_HFSC:
226
 				error = check_commit_hfsc(dev, opts, altq);
227
 				break;
228
+			case ALTQT_FAIRQ:
229
+				error = check_commit_fairq(dev, opts, altq);
230
+				break;
231
 			default:
232
 				break;
233
 			}
234
@@ -342,7 +361,8 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
235
 	if (pa->qlimit == 0)
236
 		pa->qlimit = DEFAULT_QLIMIT;
237
 
238
-	if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) {
239
+	if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC ||
240
+		pa->scheduler == ALTQT_FAIRQ) {
241
 		pa->bandwidth = eval_bwspec(bw,
242
 		    parent == NULL ? 0 : parent->bandwidth);
243
 
244
@@ -388,6 +408,9 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
245
 	case ALTQT_HFSC:
246
 		error = eval_pfqueue_hfsc(pf, pa);
247
 		break;
248
+	case ALTQT_FAIRQ:
249
+		error = eval_pfqueue_fairq(pf, pa);
250
+		break;
251
 	default:
252
 		break;
253
 	}
254
@@ -800,6 +823,85 @@ err_ret:
255
 	return (-1);
256
 }
257
 
258
+/*
259
+ * FAIRQ support functions
260
+ */
261
+static int
262
+eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa)
263
+{
264
+	struct pf_altq		*altq, *parent;
265
+	struct fairq_opts	*opts;
266
+	struct service_curve	 sc;
267
+
268
+	opts = &pa->pq_u.fairq_opts;
269
+
270
+	if (pa->parent[0] == 0) {
271
+		/* root queue */
272
+		opts->lssc_m1 = pa->ifbandwidth;
273
+		opts->lssc_m2 = pa->ifbandwidth;
274
+		opts->lssc_d = 0;
275
+		return (0);
276
+	}
277
+
278
+	LIST_INIT(&lssc);
279
+
280
+	/* if link_share is not specified, use bandwidth */
281
+	if (opts->lssc_m2 == 0)
282
+		opts->lssc_m2 = pa->bandwidth;
283
+
284
+	/*
285
+	 * admission control:
286
+	 * for the real-time service curve, the sum of the service curves
287
+	 * should not exceed 80% of the interface bandwidth.  20% is reserved
288
+	 * not to over-commit the actual interface bandwidth.
289
+	 * for the link-sharing service curve, the sum of the child service
290
+	 * curve should not exceed the parent service curve.
291
+	 * for the upper-limit service curve, the assigned bandwidth should
292
+	 * be smaller than the interface bandwidth, and the upper-limit should
293
+	 * be larger than the real-time service curve when both are defined.
294
+	 */
295
+	parent = qname_to_pfaltq(pa->parent, pa->ifname);
296
+	if (parent == NULL)
297
+		errx(1, "parent %s not found for %s", pa->parent, pa->qname);
298
+
299
+	TAILQ_FOREACH(altq, &altqs, entries) {
300
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
301
+			continue;
302
+		if (altq->qname[0] == 0)  /* this is for interface */
303
+			continue;
304
+
305
+		if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0)
306
+			continue;
307
+
308
+		/* if the class has a link-sharing service curve, add it. */
309
+		if (opts->lssc_m2 != 0 && altq->pq_u.fairq_opts.lssc_m2 != 0) {
310
+			sc.m1 = altq->pq_u.fairq_opts.lssc_m1;
311
+			sc.d = altq->pq_u.fairq_opts.lssc_d;
312
+			sc.m2 = altq->pq_u.fairq_opts.lssc_m2;
313
+			gsc_add_sc(&lssc, &sc);
314
+		}
315
+	}
316
+
317
+	/* check the link-sharing service curve. */
318
+	if (opts->lssc_m2 != 0) {
319
+		sc.m1 = parent->pq_u.fairq_opts.lssc_m1;
320
+		sc.d = parent->pq_u.fairq_opts.lssc_d;
321
+		sc.m2 = parent->pq_u.fairq_opts.lssc_m2;
322
+		if (!is_gsc_under_sc(&lssc, &sc)) {
323
+			warnx("link-sharing sc exceeds parent's sc");
324
+			goto err_ret;
325
+		}
326
+	}
327
+
328
+	gsc_destroy(&lssc);
329
+
330
+	return (0);
331
+
332
+err_ret:
333
+	gsc_destroy(&lssc);
334
+	return (-1);
335
+}
336
+
337
 static int
338
 check_commit_hfsc(int dev, int opts, struct pf_altq *pa)
339
 {
340
@@ -840,6 +942,43 @@ check_commit_hfsc(int dev, int opts, struct pf_altq *pa)
341
 }
342
 
343
 static int
344
+check_commit_fairq(int dev __unused, int opts __unused, struct pf_altq *pa)
345
+{
346
+	struct pf_altq	*altq, *def = NULL;
347
+	int		 default_class;
348
+	int		 error = 0;
349
+
350
+	/* check if fairq has one default queue for this interface */
351
+	default_class = 0;
352
+	TAILQ_FOREACH(altq, &altqs, entries) {
353
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
354
+			continue;
355
+		if (altq->qname[0] == 0)  /* this is for interface */
356
+			continue;
357
+		if (altq->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) {
358
+			default_class++;
359
+			def = altq;
360
+		}
361
+	}
362
+	if (default_class != 1) {
363
+		warnx("should have one default queue on %s", pa->ifname);
364
+		return (1);
365
+	}
366
+	/* make sure the default queue is a leaf */
367
+	TAILQ_FOREACH(altq, &altqs, entries) {
368
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
369
+			continue;
370
+		if (altq->qname[0] == 0)  /* this is for interface */
371
+			continue;
372
+		if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) {
373
+			warnx("default queue is not a leaf");
374
+			error++;
375
+		}
376
+	}
377
+	return (error);
378
+}
379
+
380
+static int
381
 print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
382
 {
383
 	const struct hfsc_opts		*opts;
384
@@ -885,6 +1024,43 @@ print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
385
 		return (0);
386
 }
387
 
388
+static int
389
+print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
390
+{
391
+	const struct fairq_opts		*opts;
392
+	const struct node_fairq_sc	*loc_lssc;
393
+
394
+	opts = &a->pq_u.fairq_opts;
395
+	if (qopts == NULL)
396
+		loc_lssc = NULL;
397
+	else
398
+		loc_lssc = &qopts->data.fairq_opts.linkshare;
399
+
400
+	if (opts->flags ||
401
+	    (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
402
+	    opts->lssc_d != 0))) {
403
+		printf("fairq(");
404
+		if (opts->flags & FARF_RED)
405
+			printf(" red");
406
+		if (opts->flags & FARF_ECN)
407
+			printf(" ecn");
408
+		if (opts->flags & FARF_RIO)
409
+			printf(" rio");
410
+		if (opts->flags & FARF_CLEARDSCP)
411
+			printf(" cleardscp");
412
+		if (opts->flags & FARF_DEFAULTCLASS)
413
+			printf(" default");
414
+		if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
415
+		    opts->lssc_d != 0))
416
+			print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d,
417
+			    opts->lssc_m2, loc_lssc);
418
+		printf(" ) ");
419
+
420
+		return (1);
421
+	} else
422
+		return (0);
423
+}
424
+
425
 /*
426
  * admission control using generalized service curve
427
  */
428
@@ -1204,6 +1380,23 @@ eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
429
 			    opts->data.hfsc_opts.upperlimit.d;
430
 		}
431
 		break;
432
+	case ALTQT_FAIRQ:
433
+		pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags;
434
+		pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets;
435
+		pa->pq_u.fairq_opts.hogs_m1 =
436
+			eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw);
437
+
438
+		if (opts->data.fairq_opts.linkshare.used) {
439
+			pa->pq_u.fairq_opts.lssc_m1 =
440
+			    eval_bwspec(&opts->data.fairq_opts.linkshare.m1,
441
+			    ref_bw);
442
+			pa->pq_u.fairq_opts.lssc_m2 =
443
+			    eval_bwspec(&opts->data.fairq_opts.linkshare.m2,
444
+			    ref_bw);
445
+			pa->pq_u.fairq_opts.lssc_d =
446
+			    opts->data.fairq_opts.linkshare.d;
447
+		}
448
+		break;
449
 	default:
450
 		warnx("eval_queue_opts: unknown scheduler type %u",
451
 		    opts->qtype);
452
@@ -1249,3 +1442,27 @@ print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2,
453
 	if (d != 0)
454
 		printf(")");
455
 }
456
+
457
+void
458
+print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2,
459
+    const struct node_fairq_sc *sc)
460
+{
461
+	printf(" %s", scname);
462
+
463
+	if (d != 0) {
464
+		printf("(");
465
+		if (sc != NULL && sc->m1.bw_percent > 0)
466
+			printf("%u%%", sc->m1.bw_percent);
467
+		else
468
+			printf("%s", rate2str((double)m1));
469
+		printf(" %u", d);
470
+	}
471
+
472
+	if (sc != NULL && sc->m2.bw_percent > 0)
473
+		printf(" %u%%", sc->m2.bw_percent);
474
+	else
475
+		printf(" %s", rate2str((double)m2));
476
+
477
+	if (d != 0)
478
+		printf(")");
479
+}
480
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
481
index 4560d66..f2b0a9b 100644
482
--- a/sbin/pfctl/pfctl_parser.h
483
+++ b/sbin/pfctl/pfctl_parser.h
484
@@ -150,12 +150,27 @@ struct node_hfsc_opts {
485
 	int			flags;
486
 };
487
 
488
+struct node_fairq_sc {
489
+	struct node_queue_bw	m1;	/* slope of 1st segment; bps */
490
+	u_int			d;	/* x-projection of m1; msec */
491
+	struct node_queue_bw	m2;	/* slope of 2nd segment; bps */
492
+	u_int8_t		used;
493
+};
494
+
495
+struct node_fairq_opts {
496
+	struct node_fairq_sc	linkshare;
497
+	struct node_queue_bw	hogs_bw;
498
+	u_int			nbuckets;
499
+	int			flags;
500
+};
501
+
502
 struct node_queue_opt {
503
 	int			 qtype;
504
 	union {
505
 		struct cbq_opts		cbq_opts;
506
 		struct priq_opts	priq_opts;
507
 		struct node_hfsc_opts	hfsc_opts;
508
+		struct node_fairq_opts	fairq_opts;
509
 	}			 data;
510
 };
511
 
512
diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c
513
index 95371e4..4087d71 100644
514
--- a/sbin/pfctl/pfctl_qstats.c
515
+++ b/sbin/pfctl/pfctl_qstats.c
516
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
517
 #include <altq/altq_cbq.h>
518
 #include <altq/altq_priq.h>
519
 #include <altq/altq_hfsc.h>
520
+#include <altq/altq_fairq.h>
521
 
522
 #include "pfctl.h"
523
 #include "pfctl_parser.h"
524
@@ -46,6 +47,7 @@ union class_stats {
525
 	class_stats_t		cbq_stats;
526
 	struct priq_classstats	priq_stats;
527
 	struct hfsc_classstats	hfsc_stats;
528
+	struct fairq_classstats fairq_stats;
529
 };
530
 
531
 #define AVGN_MAX	8
532
@@ -77,6 +79,7 @@ void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
533
 void			 print_cbqstats(struct queue_stats);
534
 void			 print_priqstats(struct queue_stats);
535
 void			 print_hfscstats(struct queue_stats);
536
+void			 print_fairqstats(struct queue_stats);
537
 void			 pfctl_free_altq_node(struct pf_altq_node *);
538
 void			 pfctl_print_altq_nodestat(int,
539
 			    const struct pf_altq_node *);
540
@@ -317,6 +320,9 @@ pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
541
 	case ALTQT_HFSC:
542
 		print_hfscstats(a->qstats);
543
 		break;
544
+	case ALTQT_FAIRQ:
545
+		print_fairqstats(a->qstats);
546
+		break;
547
 	}
548
 }
549
 
550
@@ -382,6 +388,26 @@ print_hfscstats(struct queue_stats cur)
551
 }
552
 
553
 void
554
+print_fairqstats(struct queue_stats cur)
555
+{
556
+	printf("  [ pkts: %10llu  bytes: %10llu  "
557
+	    "dropped pkts: %6llu bytes: %6llu ]\n",
558
+	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
559
+	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
560
+	    (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
561
+	    (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
562
+	printf("  [ qlength: %3d/%3d ]\n",
563
+	    cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
564
+
565
+	if (cur.avgn < 2)
566
+		return;
567
+
568
+	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
569
+	    cur.avg_packets / STAT_INTERVAL,
570
+	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
571
+}
572
+
573
+void
574
 pfctl_free_altq_node(struct pf_altq_node *node)
575
 {
576
 	while (node != NULL) {
577
@@ -421,6 +447,10 @@ update_avg(struct pf_altq_node *a)
578
 		b = qs->data.hfsc_stats.xmit_cnt.bytes;
579
 		p = qs->data.hfsc_stats.xmit_cnt.packets;
580
 		break;
581
+	case ALTQT_FAIRQ:
582
+		b = qs->data.fairq_stats.xmit_cnt.bytes;
583
+		p = qs->data.fairq_stats.xmit_cnt.packets;
584
+		break;
585
 	default:
586
 		b = 0;
587
 		p = 0;
588
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
589
index 3c36b58..6c11047 100644
590
--- a/sys/conf/NOTES
591
+++ b/sys/conf/NOTES
592
@@ -694,6 +694,7 @@ options 	ALTQ_CBQ	# Class Based Queueing
593
 options 	ALTQ_RED	# Random Early Detection
594
 options 	ALTQ_RIO	# RED In/Out
595
 options 	ALTQ_HFSC	# Hierarchical Packet Scheduler
596
+options		ALTQ_FAIRQ	# Fair Packet Scheduler
597
 options 	ALTQ_CDNR	# Traffic conditioner
598
 options 	ALTQ_PRIQ	# Priority Queueing
599
 options 	ALTQ_NOPCC	# Required if the TSC is unusable
600
diff --git a/sys/conf/files b/sys/conf/files
601
index 7a3a0c5..434ffaf 100644
602
--- a/sys/conf/files
603
+++ b/sys/conf/files
604
@@ -286,6 +286,7 @@ compat/freebsd32/freebsd32_sysent.c	optional compat_freebsd32
605
 contrib/altq/altq/altq_cbq.c		optional altq
606
 contrib/altq/altq/altq_cdnr.c		optional altq
607
 contrib/altq/altq/altq_hfsc.c		optional altq
608
+contrib/altq/altq/altq_fairq.c		optional altq
609
 contrib/altq/altq/altq_priq.c		optional altq
610
 contrib/altq/altq/altq_red.c		optional altq
611
 contrib/altq/altq/altq_rio.c		optional altq
612
diff --git a/sys/conf/options b/sys/conf/options
613
index e56d25c..55030c3 100644
614
--- a/sys/conf/options
615
+++ b/sys/conf/options
616
@@ -379,6 +379,7 @@ ALTQ_CBQ		opt_altq.h
617
 ALTQ_CDNR		opt_altq.h
618
 ALTQ_DEBUG		opt_altq.h
619
 ALTQ_HFSC		opt_altq.h
620
+ALTQ_FAIRQ		opt_altq.h
621
 ALTQ_NOPCC		opt_altq.h
622
 ALTQ_PRIQ		opt_altq.h
623
 ALTQ_RED		opt_altq.h
624
diff --git a/sys/contrib/altq/altq/altq.h b/sys/contrib/altq/altq/altq.h
625
index c740ed3..6200ac5 100644
626
--- a/sys/contrib/altq/altq/altq.h
627
+++ b/sys/contrib/altq/altq/altq.h
628
@@ -63,7 +63,8 @@
629
 #define	ALTQT_BLUE		10	/* blue */
630
 #define	ALTQT_PRIQ		11	/* priority queue */
631
 #define	ALTQT_JOBS		12	/* JoBS */
632
-#define	ALTQT_MAX		13	/* should be max discipline type + 1 */
633
+#define ALTQT_FAIRQ		13	/* fairq */
634
+#define	ALTQT_MAX		14	/* should be max discipline type + 1 */
635
 
636
 #ifdef ALTQ3_COMPAT
637
 struct	altqreq {
638
diff --git a/sys/contrib/altq/altq/altq_fairq.c b/sys/contrib/altq/altq/altq_fairq.c
639
new file mode 100644
640
index 0000000..2267bfd
641
--- /dev/null
642
+++ b/sys/contrib/altq/altq/altq_fairq.c
643
@@ -0,0 +1,905 @@
644
+/*
645
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
646
+ * 
647
+ * This code is derived from software contributed to The DragonFly Project
648
+ * by Matthew Dillon <dillon@backplane.com>
649
+ * 
650
+ * Redistribution and use in source and binary forms, with or without
651
+ * modification, are permitted provided that the following conditions
652
+ * are met:
653
+ * 
654
+ * 1. Redistributions of source code must retain the above copyright
655
+ *    notice, this list of conditions and the following disclaimer.
656
+ * 2. Redistributions in binary form must reproduce the above copyright
657
+ *    notice, this list of conditions and the following disclaimer in
658
+ *    the documentation and/or other materials provided with the
659
+ *    distribution.
660
+ * 3. Neither the name of The DragonFly Project nor the names of its
661
+ *    contributors may be used to endorse or promote products derived
662
+ *    from this software without specific, prior written permission.
663
+ * 
664
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
665
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
666
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
667
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
668
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
669
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
670
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
671
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
672
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
673
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
674
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
675
+ * SUCH DAMAGE.
676
+ * 
677
+ * $DragonFly: src/sys/net/altq/altq_fairq.c,v 1.1 2008/04/06 18:58:15 dillon Exp $
678
+ */
679
+/*
680
+ * Matt: I gutted altq_priq.c and used it as a skeleton on which to build
681
+ * fairq.  The fairq algorithm is completely different then priq, of course,
682
+ * but because I used priq's skeleton I believe I should include priq's
683
+ * copyright.
684
+ *
685
+ * Copyright (C) 2000-2003
686
+ *	Sony Computer Science Laboratories Inc.  All rights reserved.
687
+ *
688
+ * Redistribution and use in source and binary forms, with or without
689
+ * modification, are permitted provided that the following conditions
690
+ * are met:
691
+ * 1. Redistributions of source code must retain the above copyright
692
+ *    notice, this list of conditions and the following disclaimer.
693
+ * 2. Redistributions in binary form must reproduce the above copyright
694
+ *    notice, this list of conditions and the following disclaimer in the
695
+ *    documentation and/or other materials provided with the distribution.
696
+ *
697
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
698
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
699
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
700
+ * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
701
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
702
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
703
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
704
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
705
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
706
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
707
+ * SUCH DAMAGE.
708
+ */
709
+
710
+/*
711
+ * FAIRQ - take traffic classified by keep state (hashed into
712
+ * mbuf->m_pkthdr.altq_state_hash) and bucketize it.  Fairly extract
713
+ * the first packet from each bucket in a round-robin fashion.
714
+ *
715
+ * TODO - better overall qlimit support (right now it is per-bucket).
716
+ *	- NOTE: red etc is per bucket, not overall.
717
+ *	- better service curve support.
718
+ *
719
+ * EXAMPLE:
720
+ *
721
+ *  altq on em0 fairq bandwidth 650Kb queue { std, bulk }
722
+ *  queue std  priority 3 bandwidth 400Kb \
723
+ *	fairq (buckets 64, default, hogs 1Kb) qlimit 50
724
+ *  queue bulk priority 2 bandwidth 100Kb \
725
+ *	fairq (buckets 64, hogs 1Kb) qlimit 50
726
+ *
727
+ *  pass out on em0 from any to any keep state queue std
728
+ *  pass out on em0 inet proto tcp ..... port ... keep state queue bulk
729
+ */
730
+#include "opt_altq.h"
731
+#include "opt_inet.h"
732
+#include "opt_inet6.h"
733
+
734
+#ifdef ALTQ_FAIRQ  /* fairq is enabled in the kernel conf */
735
+
736
+#include <sys/param.h>
737
+#include <sys/malloc.h>
738
+#include <sys/mbuf.h>
739
+#include <sys/socket.h>
740
+#include <sys/sockio.h>
741
+#include <sys/systm.h>
742
+#include <sys/proc.h>
743
+#include <sys/errno.h>
744
+#include <sys/kernel.h>
745
+#include <sys/queue.h>
746
+
747
+#include <net/if.h>
748
+#include <netinet/in.h>
749
+
750
+#include <net/pfvar.h>
751
+#include <altq/altq.h>
752
+#include <altq/altq_fairq.h>
753
+
754
+/*
755
+ * function prototypes
756
+ */
757
+static int	fairq_clear_interface(struct fairq_if *);
758
+static int	fairq_request(struct ifaltq *, int, void *);
759
+static void	fairq_purge(struct fairq_if *);
760
+static struct fairq_class *fairq_class_create(struct fairq_if *, int, int, u_int, struct fairq_opts *, int);
761
+static int	fairq_class_destroy(struct fairq_class *);
762
+static int	fairq_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
763
+static struct mbuf *fairq_dequeue(struct ifaltq *, int);
764
+
765
+static int	fairq_addq(struct fairq_class *, struct mbuf *, u_int32_t);
766
+static struct mbuf *fairq_getq(struct fairq_class *, uint64_t);
767
+static struct mbuf *fairq_pollq(struct fairq_class *, uint64_t, int *);
768
+static fairq_bucket_t *fairq_selectq(struct fairq_class *, int);
769
+static void	fairq_purgeq(struct fairq_class *);
770
+
771
+static void	get_class_stats(struct fairq_classstats *, struct fairq_class *);
772
+static struct fairq_class *clh_to_clp(struct fairq_if *, uint32_t);
773
+
774
+int
775
+fairq_pfattach(struct pf_altq *a)
776
+{
777
+	struct ifnet *ifp;
778
+	int error;
779
+
780
+	if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
781
+		return (EINVAL);
782
+
783
+	error = altq_attach(&ifp->if_snd, ALTQT_FAIRQ, a->altq_disc,
784
+	    fairq_enqueue, fairq_dequeue, fairq_request, NULL, NULL);
785
+
786
+	return (error);
787
+}
788
+
789
+int
790
+fairq_add_altq(struct pf_altq *a)
791
+{
792
+	struct fairq_if *pif;
793
+	struct ifnet *ifp;
794
+
795
+	if ((ifp = ifunit(a->ifname)) == NULL)
796
+		return (EINVAL);
797
+	if (!ALTQ_IS_READY(&ifp->if_snd))
798
+		return (ENODEV);
799
+
800
+
801
+	MALLOC(pif, struct fairq_if *, sizeof(struct fairq_if),
802
+		M_DEVBUF, M_WAITOK);
803
+	if (pif == NULL)
804
+		return (ENOMEM);
805
+	bzero(pif, sizeof(struct fairq_if));
806
+	pif->pif_bandwidth = a->ifbandwidth;
807
+	pif->pif_maxpri = -1;
808
+	pif->pif_ifq = &ifp->if_snd;
809
+
810
+	/* keep the state in pf_altq */
811
+	a->altq_disc = pif;
812
+
813
+	return (0);
814
+}
815
+
816
+int
817
+fairq_remove_altq(struct pf_altq *a)
818
+{
819
+	struct fairq_if *pif;
820
+
821
+	if ((pif = a->altq_disc) == NULL)
822
+		return (EINVAL);
823
+	a->altq_disc = NULL;
824
+
825
+	fairq_clear_interface(pif);
826
+
827
+	FREE(pif, M_DEVBUF);
828
+	return (0);
829
+}
830
+
831
+int
832
+fairq_add_queue(struct pf_altq *a)
833
+{
834
+	struct fairq_if *pif;
835
+	struct fairq_class *cl;
836
+
837
+	if ((pif = a->altq_disc) == NULL)
838
+		return (EINVAL);
839
+
840
+	/* check parameters */
841
+	if (a->priority >= FAIRQ_MAXPRI)
842
+		return (EINVAL);
843
+	if (a->qid == 0)
844
+		return (EINVAL);
845
+	if (pif->pif_classes[a->priority] != NULL)
846
+		return (EBUSY);
847
+	if (clh_to_clp(pif, a->qid) != NULL)
848
+		return (EBUSY);
849
+
850
+	cl = fairq_class_create(pif, a->priority, a->qlimit, a->bandwidth,
851
+			       &a->pq_u.fairq_opts, a->qid);
852
+	if (cl == NULL)
853
+		return (ENOMEM);
854
+
855
+	return (0);
856
+}
857
+
858
+int
859
+fairq_remove_queue(struct pf_altq *a)
860
+{
861
+	struct fairq_if *pif;
862
+	struct fairq_class *cl;
863
+
864
+	if ((pif = a->altq_disc) == NULL)
865
+		return (EINVAL);
866
+
867
+	if ((cl = clh_to_clp(pif, a->qid)) == NULL)
868
+		return (EINVAL);
869
+
870
+	return (fairq_class_destroy(cl));
871
+}
872
+
873
+int
874
+fairq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
875
+{
876
+	struct fairq_if *pif;
877
+	struct fairq_class *cl;
878
+	struct fairq_classstats stats;
879
+	int error = 0;
880
+
881
+	if ((pif = altq_lookup(a->ifname, ALTQT_FAIRQ)) == NULL)
882
+		return (EBADF);
883
+
884
+	if ((cl = clh_to_clp(pif, a->qid)) == NULL)
885
+		return (EINVAL);
886
+
887
+	if (*nbytes < sizeof(stats))
888
+		return (EINVAL);
889
+
890
+	get_class_stats(&stats, cl);
891
+
892
+	if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
893
+		return (error);
894
+	*nbytes = sizeof(stats);
895
+	return (0);
896
+}
897
+
898
+/*
899
+ * bring the interface back to the initial state by discarding
900
+ * all the filters and classes.
901
+ */
902
+static int
903
+fairq_clear_interface(struct fairq_if *pif)
904
+{
905
+	struct fairq_class *cl;
906
+	int pri;
907
+
908
+	/* clear out the classes */
909
+	for (pri = 0; pri <= pif->pif_maxpri; pri++) {
910
+		if ((cl = pif->pif_classes[pri]) != NULL)
911
+			fairq_class_destroy(cl);
912
+	}
913
+
914
+	return (0);
915
+}
916
+
917
+static int
918
+fairq_request(struct ifaltq *ifq, int req, void *arg)
919
+{
920
+	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
921
+
922
+	IFQ_LOCK_ASSERT(ifq);
923
+
924
+	switch (req) {
925
+	case ALTRQ_PURGE:
926
+		fairq_purge(pif);
927
+		break;
928
+	}
929
+	return (0);
930
+}
931
+
932
+/* discard all the queued packets on the interface */
933
+static void
934
+fairq_purge(struct fairq_if *pif)
935
+{
936
+	struct fairq_class *cl;
937
+	int pri;
938
+
939
+	for (pri = 0; pri <= pif->pif_maxpri; pri++) {
940
+		if ((cl = pif->pif_classes[pri]) != NULL && cl->cl_head)
941
+			fairq_purgeq(cl);
942
+	}
943
+	if (ALTQ_IS_ENABLED(pif->pif_ifq))
944
+		pif->pif_ifq->ifq_len = 0;
945
+}
946
+
947
+static struct fairq_class *
948
+fairq_class_create(struct fairq_if *pif, int pri, int qlimit,
949
+		   u_int bandwidth, struct fairq_opts *opts, int qid)
950
+{
951
+	struct fairq_class *cl;
952
+	int flags = opts->flags;
953
+	u_int nbuckets = opts->nbuckets;
954
+	int i, s;
955
+
956
+#ifndef ALTQ_RED
957
+	if (flags & FARF_RED) {
958
+#ifdef ALTQ_DEBUG
959
+		printf("fairq_class_create: RED not configured for FAIRQ!\n");
960
+#endif
961
+		return (NULL);
962
+	}
963
+#endif
964
+	if (nbuckets == 0)
965
+		nbuckets = 256;
966
+	if (nbuckets > FAIRQ_MAX_BUCKETS)
967
+		nbuckets = FAIRQ_MAX_BUCKETS;
968
+	/* enforce power-of-2 size */
969
+	while ((nbuckets ^ (nbuckets - 1)) != ((nbuckets << 1) - 1))
970
+		++nbuckets;
971
+
972
+	if ((cl = pif->pif_classes[pri]) != NULL) {
973
+		/* modify the class instead of creating a new one */
974
+		s = splimp();
975
+		IFQ_LOCK(cl->cl_pif->pif_ifq);
976
+		if (cl->cl_head)
977
+			fairq_purgeq(cl);
978
+		IFQ_UNLOCK(cl->cl_pif->pif_ifq);
979
+		splx(s);
980
+#ifdef ALTQ_RIO
981
+		if (cl->cl_qtype == Q_RIO)
982
+			rio_destroy((rio_t *)cl->cl_red);
983
+#endif
984
+#ifdef ALTQ_RED
985
+		if (cl->cl_qtype == Q_RED)
986
+			red_destroy(cl->cl_red);
987
+#endif
988
+	} else {
989
+		MALLOC(cl, struct fairq_class *, sizeof(struct fairq_class),
990
+			M_DEVBUF, M_WAITOK);
991
+		if (cl == NULL)
992
+			goto err_ret;
993
+		bzero(cl, sizeof(struct fairq_class));
994
+		cl->cl_nbuckets = nbuckets;
995
+		cl->cl_nbucket_mask = nbuckets - 1;
996
+
997
+		MALLOC(cl->cl_buckets, struct fairq_bucket *, 
998
+			sizeof(struct fairq_bucket) * cl->cl_nbuckets,
999
+			M_DEVBUF, M_WAITOK);
1000
+		if (cl->cl_buckets == NULL)
1001
+			goto err_buckets;
1002
+		bzero(cl->cl_buckets, sizeof(struct fairq_bucket) *
1003
+			cl->cl_nbuckets);
1004
+		cl->cl_head = NULL;
1005
+	}
1006
+
1007
+	pif->pif_classes[pri] = cl;
1008
+	if (flags & FARF_DEFAULTCLASS)
1009
+		pif->pif_default = cl;
1010
+	if (qlimit == 0)
1011
+		qlimit = 50;  /* use default */
1012
+	cl->cl_qlimit = qlimit;
1013
+	for (i = 0; i < cl->cl_nbuckets; ++i) {
1014
+		qlimit(&cl->cl_buckets[i].queue) = qlimit;
1015
+	}
1016
+	cl->cl_bandwidth = bandwidth / 8;
1017
+	cl->cl_qtype = Q_DROPTAIL;
1018
+	cl->cl_flags = flags & FARF_USERFLAGS;
1019
+	cl->cl_pri = pri;
1020
+	if (pri > pif->pif_maxpri)
1021
+		pif->pif_maxpri = pri;
1022
+	cl->cl_pif = pif;
1023
+	cl->cl_handle = qid;
1024
+	cl->cl_hogs_m1 = opts->hogs_m1 / 8;
1025
+	cl->cl_lssc_m1 = opts->lssc_m1 / 8;	/* NOT YET USED */
1026
+
1027
+#ifdef ALTQ_RED
1028
+	if (flags & (FARF_RED|FARF_RIO)) {
1029
+		int red_flags, red_pkttime;
1030
+
1031
+		red_flags = 0;
1032
+		if (flags & FARF_ECN)
1033
+			red_flags |= REDF_ECN;
1034
+#ifdef ALTQ_RIO
1035
+		if (flags & FARF_CLEARDSCP)
1036
+			red_flags |= RIOF_CLEARDSCP;
1037
+#endif
1038
+		if (pif->pif_bandwidth < 8)
1039
+			red_pkttime = 1000 * 1000 * 1000; /* 1 sec */
1040
+		else
1041
+			red_pkttime = (int64_t)pif->pif_ifq->altq_ifp->if_mtu
1042
+			  * 1000 * 1000 * 1000 / (pif->pif_bandwidth / 8);
1043
+#ifdef ALTQ_RIO
1044
+		if (flags & FARF_RIO) {
1045
+			cl->cl_red = (red_t *)rio_alloc(0, NULL,
1046
+						red_flags, red_pkttime);
1047
+			if (cl->cl_red != NULL)
1048
+				cl->cl_qtype = Q_RIO;
1049
+		} else
1050
+#endif
1051
+		if (flags & FARF_RED) {
1052
+			cl->cl_red = red_alloc(0, 0,
1053
+			    cl->cl_qlimit * 10/100,
1054
+			    cl->cl_qlimit * 30/100,
1055
+			    red_flags, red_pkttime);
1056
+			if (cl->cl_red != NULL)
1057
+				cl->cl_qtype = Q_RED;
1058
+		}
1059
+	}
1060
+#endif /* ALTQ_RED */
1061
+
1062
+	return (cl);
1063
+
1064
+err_buckets:
1065
+	if (cl->cl_buckets != NULL)
1066
+		FREE(cl->cl_buckets, M_DEVBUF);
1067
+err_ret:
1068
+        if (cl->cl_red != NULL) {
1069
+#ifdef ALTQ_RIO
1070
+                if (cl->cl_qtype == Q_RIO)
1071
+                        rio_destroy((rio_t *)cl->cl_red);
1072
+#endif
1073
+#ifdef ALTQ_RED
1074
+               if (cl->cl_qtype == Q_RED)
1075
+                       red_destroy(cl->cl_red);
1076
+#endif
1077
+        }
1078
+        if (cl != NULL)
1079
+                FREE(cl, M_DEVBUF);
1080
+        return (NULL);
1081
+}
1082
+
1083
+static int
1084
+fairq_class_destroy(struct fairq_class *cl)
1085
+{
1086
+	struct fairq_if *pif;
1087
+	int pri, s;
1088
+
1089
+	s = splimp();
1090
+	IFQ_LOCK(cl->cl_pif->pif_ifq);
1091
+
1092
+	if (cl->cl_head)
1093
+		fairq_purgeq(cl);
1094
+
1095
+	pif = cl->cl_pif;
1096
+	pif->pif_classes[cl->cl_pri] = NULL;
1097
+	if (pif->pif_poll_cache == cl)
1098
+		pif->pif_poll_cache = NULL;
1099
+	if (pif->pif_maxpri == cl->cl_pri) {
1100
+		for (pri = cl->cl_pri; pri >= 0; pri--)
1101
+			if (pif->pif_classes[pri] != NULL) {
1102
+				pif->pif_maxpri = pri;
1103
+				break;
1104
+			}
1105
+		if (pri < 0)
1106
+			pif->pif_maxpri = -1;
1107
+	}
1108
+	IFQ_UNLOCK(cl->cl_pif->pif_ifq);
1109
+	splx(s);
1110
+
1111
+	if (cl->cl_red != NULL) {
1112
+#ifdef ALTQ_RIO
1113
+		if (cl->cl_qtype == Q_RIO)
1114
+			rio_destroy((rio_t *)cl->cl_red);
1115
+#endif
1116
+#ifdef ALTQ_RED
1117
+		if (cl->cl_qtype == Q_RED)
1118
+			red_destroy(cl->cl_red);
1119
+#endif
1120
+	}
1121
+	FREE(cl->cl_buckets, M_DEVBUF);
1122
+	cl->cl_head = NULL;	/* sanity */
1123
+	cl->cl_polled = NULL;	/* sanity */
1124
+	cl->cl_buckets = NULL;	/* sanity */
1125
+	FREE(cl, M_DEVBUF);
1126
+
1127
+	return (0);
1128
+}
1129
+
1130
+/*
1131
+ * fairq_enqueue is an enqueue function to be registered to
1132
+ * (*altq_enqueue) in struct ifaltq.
1133
+ */
1134
+static int
1135
+fairq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
1136
+{
1137
+	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
1138
+	struct fairq_class *cl = NULL; /* Make compiler happy */
1139
+	struct pf_mtag *t;
1140
+	u_int32_t qid_hash = 0;
1141
+	int len;
1142
+
1143
+	IFQ_LOCK_ASSERT(ifq);
1144
+
1145
+	/* grab class set by classifier */
1146
+	if ((m->m_flags & M_PKTHDR) == 0) {
1147
+		/* should not happen */
1148
+		printf("altq: packet for %s does not have pkthdr\n",
1149
+			ifq->altq_ifp->if_xname);
1150
+		m_freem(m);
1151
+		return (ENOBUFS);
1152
+	}
1153
+
1154
+	if ((t = pf_find_mtag(m)) != NULL) {
1155
+		cl = clh_to_clp(pif, t->qid);
1156
+		qid_hash = t->qid_hash;
1157
+	}
1158
+	if (cl == NULL) {
1159
+		cl = pif->pif_default;
1160
+		if (cl == NULL) {
1161
+			m_freem(m);
1162
+			return (ENOBUFS);
1163
+		}
1164
+	}
1165
+	cl->cl_flags |= FARF_HAS_PACKETS;
1166
+	cl->cl_pktattr = NULL;
1167
+	len = m_pktlen(m);
1168
+	if (fairq_addq(cl, m, qid_hash) != 0) {
1169
+		/* drop occurred.  mbuf was freed in fairq_addq. */
1170
+		PKTCNTR_ADD(&cl->cl_dropcnt, len);
1171
+		return (ENOBUFS);
1172
+	}
1173
+	IFQ_INC_LEN(ifq);
1174
+
1175
+	return (0);
1176
+}
1177
+
1178
+/*
1179
+ * fairq_dequeue is a dequeue function to be registered to
1180
+ * (*altq_dequeue) in struct ifaltq.
1181
+ *
1182
+ * note: ALTDQ_POLL returns the next packet without removing the packet
1183
+ *	from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
1184
+ *	ALTDQ_REMOVE must return the same packet if called immediately
1185
+ *	after ALTDQ_POLL.
1186
+ */
1187
+static struct mbuf *
1188
+fairq_dequeue(struct ifaltq *ifq, int op)
1189
+{
1190
+	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
1191
+	struct fairq_class *cl;
1192
+	struct fairq_class *best_cl;
1193
+	struct mbuf *best_m;
1194
+	struct mbuf *m = NULL;
1195
+	uint64_t cur_time = read_machclk();
1196
+	int pri;
1197
+	int hit_limit;
1198
+
1199
+	IFQ_LOCK_ASSERT(ifq);
1200
+
1201
+	if (IFQ_IS_EMPTY(ifq)) {
1202
+		return (NULL);
1203
+	}
1204
+
1205
+	if (pif->pif_poll_cache && op == ALTDQ_REMOVE) {
1206
+		best_cl = pif->pif_poll_cache;
1207
+		m = fairq_getq(best_cl, cur_time);
1208
+		pif->pif_poll_cache = NULL;
1209
+		if (m) {
1210
+			IFQ_DEC_LEN(ifq);
1211
+			PKTCNTR_ADD(&best_cl->cl_xmitcnt, m_pktlen(m));
1212
+			return (m);
1213
+		}
1214
+	} else {
1215
+		best_cl = NULL;
1216
+		best_m = NULL;
1217
+
1218
+		for (pri = pif->pif_maxpri;  pri >= 0; pri--) {
1219
+			if ((cl = pif->pif_classes[pri]) == NULL)
1220
+				continue;
1221
+			if ((cl->cl_flags & FARF_HAS_PACKETS) == 0)
1222
+				continue;
1223
+			m = fairq_pollq(cl, cur_time, &hit_limit);
1224
+			if (m == NULL) {
1225
+				cl->cl_flags &= ~FARF_HAS_PACKETS;
1226
+				continue;
1227
+			}
1228
+
1229
+			/*
1230
+			 * Only override the best choice if we are under
1231
+			 * the BW limit.
1232
+			 */
1233
+			if (hit_limit == 0 || best_cl == NULL) {
1234
+				best_cl = cl;
1235
+				best_m = m;
1236
+			}
1237
+
1238
+			/*
1239
+			 * Remember the highest priority mbuf in case we
1240
+			 * do not find any lower priority mbufs.
1241
+			 */
1242
+			if (hit_limit)
1243
+				continue;
1244
+			break;
1245
+		}
1246
+		if (op == ALTDQ_POLL) {
1247
+			pif->pif_poll_cache = best_cl;
1248
+			m = best_m;
1249
+		} else if (best_cl) {
1250
+			m = fairq_getq(best_cl, cur_time);
1251
+			if (m != NULL) {
1252
+				IFQ_DEC_LEN(ifq);
1253
+				PKTCNTR_ADD(&best_cl->cl_xmitcnt, m_pktlen(m));
1254
+			}
1255
+		} 
1256
+		return (m);
1257
+	}
1258
+	return (NULL);
1259
+}
1260
+
1261
+static int
1262
+fairq_addq(struct fairq_class *cl, struct mbuf *m, u_int32_t bucketid)
1263
+{
1264
+	fairq_bucket_t *b;
1265
+	u_int hindex;
1266
+	uint64_t bw;
1267
+
1268
+	/*
1269
+	 * If the packet doesn't have any keep state put it on the end of
1270
+	 * our queue.  XXX this can result in out of order delivery.
1271
+	 */
1272
+	if (bucketid == 0) {
1273
+		if (cl->cl_head)
1274
+			b = cl->cl_head->prev;
1275
+		else
1276
+			b = &cl->cl_buckets[0];
1277
+	} else {
1278
+		hindex = bucketid & cl->cl_nbucket_mask;
1279
+		b = &cl->cl_buckets[hindex];
1280
+	}
1281
+
1282
+	/*
1283
+	 * Add the bucket to the end of the circular list of active buckets.
1284
+	 *
1285
+	 * As a special case we add the bucket to the beginning of the list
1286
+	 * instead of the end if it was not previously on the list and if
1287
+	 * its traffic is less then the hog level.
1288
+	 */
1289
+	if (b->in_use == 0) {
1290
+		b->in_use = 1;
1291
+		if (cl->cl_head == NULL) {
1292
+			cl->cl_head = b;
1293
+			b->next = b;
1294
+			b->prev = b;
1295
+		} else {
1296
+			b->next = cl->cl_head;
1297
+			b->prev = cl->cl_head->prev;
1298
+			b->prev->next = b;
1299
+			b->next->prev = b;
1300
+
1301
+			if (b->bw_delta && cl->cl_hogs_m1) {
1302
+				bw = b->bw_bytes * machclk_freq / b->bw_delta;
1303
+				if (bw < cl->cl_hogs_m1)
1304
+					cl->cl_head = b;
1305
+			}
1306
+		}
1307
+	}
1308
+
1309
+#ifdef ALTQ_RIO
1310
+	if (cl->cl_qtype == Q_RIO)
1311
+		return rio_addq((rio_t *)cl->cl_red, &b->queue, m, cl->cl_pktattr);
1312
+#endif
1313
+#ifdef ALTQ_RED
1314
+	if (cl->cl_qtype == Q_RED)
1315
+		return red_addq(cl->cl_red, &b->queue, m, cl->cl_pktattr);
1316
+#endif
1317
+	if (qlen(&b->queue) >= qlimit(&b->queue)) {
1318
+		m_freem(m);
1319
+		return (-1);
1320
+	}
1321
+
1322
+	if (cl->cl_flags & FARF_CLEARDSCP)
1323
+		write_dsfield(m, cl->cl_pktattr, 0);
1324
+
1325
+	_addq(&b->queue, m);
1326
+
1327
+	return (0);
1328
+}
1329
+
1330
+static struct mbuf *
1331
+fairq_getq(struct fairq_class *cl, uint64_t cur_time)
1332
+{
1333
+	fairq_bucket_t *b;
1334
+	struct mbuf *m;
1335
+
1336
+	b = fairq_selectq(cl, 0);
1337
+	if (b == NULL)
1338
+		m = NULL;
1339
+#ifdef ALTQ_RIO
1340
+	else if (cl->cl_qtype == Q_RIO)
1341
+		m = rio_getq((rio_t *)cl->cl_red, &b->queue);
1342
+#endif
1343
+#ifdef ALTQ_RED
1344
+	else if (cl->cl_qtype == Q_RED)
1345
+		m = red_getq(cl->cl_red, &b->queue);
1346
+#endif
1347
+	else
1348
+		m = _getq(&b->queue);
1349
+
1350
+	/*
1351
+	 * Calculate the BW change
1352
+	 */
1353
+	if (m != NULL) {
1354
+		uint64_t delta;
1355
+
1356
+		/*
1357
+		 * Per-class bandwidth calculation
1358
+		 */
1359
+		delta = (cur_time - cl->cl_last_time);
1360
+		if (delta > machclk_freq * 8)
1361
+			delta = machclk_freq * 8;
1362
+		cl->cl_bw_delta += delta;
1363
+		cl->cl_bw_bytes += m->m_pkthdr.len;
1364
+		cl->cl_last_time = cur_time;
1365
+		cl->cl_bw_delta -= cl->cl_bw_delta >> 3;
1366
+		cl->cl_bw_bytes -= cl->cl_bw_bytes >> 3;
1367
+
1368
+		/*
1369
+		 * Per-bucket bandwidth calculation
1370
+		 */
1371
+		delta = (cur_time - b->last_time);
1372
+		if (delta > machclk_freq * 8)
1373
+			delta = machclk_freq * 8;
1374
+		b->bw_delta += delta;
1375
+		b->bw_bytes += m->m_pkthdr.len;
1376
+		b->last_time = cur_time;
1377
+		b->bw_delta -= b->bw_delta >> 3;
1378
+		b->bw_bytes -= b->bw_bytes >> 3;
1379
+	}
1380
+	return(m);
1381
+}
1382
+
1383
+/*
1384
+ * Figure out what the next packet would be if there were no limits.  If
1385
+ * this class hits its bandwidth limit *hit_limit is set to no-zero, otherwise
1386
+ * it is set to 0.  A non-NULL mbuf is returned either way.
1387
+ */
1388
+static struct mbuf *
1389
+fairq_pollq(struct fairq_class *cl, uint64_t cur_time, int *hit_limit)
1390
+{
1391
+	fairq_bucket_t *b;
1392
+	struct mbuf *m;
1393
+	uint64_t delta;
1394
+	uint64_t bw;
1395
+
1396
+	*hit_limit = 0;
1397
+	b = fairq_selectq(cl, 1);
1398
+	if (b == NULL)
1399
+		return(NULL);
1400
+	m = qhead(&b->queue);
1401
+
1402
+	/*
1403
+	 * Did this packet exceed the class bandwidth?  Calculate the
1404
+	 * bandwidth component of the packet.
1405
+	 *
1406
+	 * - Calculate bytes per second
1407
+	 */
1408
+	delta = cur_time - cl->cl_last_time;
1409
+	if (delta > machclk_freq * 8)
1410
+		delta = machclk_freq * 8;
1411
+	cl->cl_bw_delta += delta;
1412
+	cl->cl_last_time = cur_time;
1413
+	if (cl->cl_bw_delta) {
1414
+		bw = cl->cl_bw_bytes * machclk_freq / cl->cl_bw_delta;
1415
+
1416
+		if (bw > cl->cl_bandwidth)
1417
+			*hit_limit = 1;
1418
+#if 0
1419
+		printf("BW %6lld relative to %6u %d queue %p\n",
1420
+			bw, cl->cl_bandwidth, *hit_limit, b);
1421
+#endif
1422
+	}
1423
+	return(m);
1424
+}
1425
+
1426
+/*
1427
+ * Locate the next queue we want to pull a packet out of.  This code
1428
+ * is also responsible for removing empty buckets from the circular list.
1429
+ */
1430
+static
1431
+fairq_bucket_t *
1432
+fairq_selectq(struct fairq_class *cl, int ispoll)
1433
+{
1434
+	fairq_bucket_t *b;
1435
+	uint64_t bw;
1436
+
1437
+	if (ispoll == 0 && cl->cl_polled) {
1438
+		b = cl->cl_polled;
1439
+		cl->cl_polled = NULL;
1440
+		return(b);
1441
+	}
1442
+
1443
+	while ((b = cl->cl_head) != NULL) {
1444
+		/*
1445
+		 * Remove empty queues from consideration
1446
+		 */
1447
+		if (qempty(&b->queue)) {
1448
+			b->in_use = 0;
1449
+			cl->cl_head = b->next;
1450
+			if (cl->cl_head == b) {
1451
+				cl->cl_head = NULL;
1452
+			} else {
1453
+				b->next->prev = b->prev;
1454
+				b->prev->next = b->next;
1455
+			}
1456
+			continue;
1457
+		}
1458
+
1459
+		/*
1460
+		 * Advance the round robin.  Queues with bandwidths less
1461
+		 * then the hog bandwidth are allowed to burst.
1462
+		 */
1463
+		if (cl->cl_hogs_m1 == 0) {
1464
+			cl->cl_head = b->next;
1465
+		} else if (b->bw_delta) {
1466
+			bw = b->bw_bytes * machclk_freq / b->bw_delta;
1467
+			if (bw >= cl->cl_hogs_m1) {
1468
+				cl->cl_head = b->next;
1469
+			}
1470
+			/*
1471
+			 * XXX TODO - 
1472
+			 */
1473
+		}
1474
+
1475
+		/*
1476
+		 * Return bucket b.
1477
+		 */
1478
+		break;
1479
+	}
1480
+	if (ispoll)
1481
+		cl->cl_polled = b;
1482
+	return(b);
1483
+}
1484
+
1485
+static void
1486
+fairq_purgeq(struct fairq_class *cl)
1487
+{
1488
+	fairq_bucket_t *b;
1489
+	struct mbuf *m;
1490
+
1491
+	while ((b = fairq_selectq(cl, 0)) != NULL) {
1492
+		while ((m = _getq(&b->queue)) != NULL) {
1493
+			PKTCNTR_ADD(&cl->cl_dropcnt, m_pktlen(m));
1494
+			m_freem(m);
1495
+		}
1496
+		ASSERT(qlen(&b->queue) == 0);
1497
+	}
1498
+}
1499
+
1500
+static void
1501
+get_class_stats(struct fairq_classstats *sp, struct fairq_class *cl)
1502
+{
1503
+	fairq_bucket_t *b;
1504
+
1505
+	sp->class_handle = cl->cl_handle;
1506
+	sp->qlimit = cl->cl_qlimit;
1507
+	sp->xmit_cnt = cl->cl_xmitcnt;
1508
+	sp->drop_cnt = cl->cl_dropcnt;
1509
+	sp->qtype = cl->cl_qtype;
1510
+	sp->qlength = 0;
1511
+
1512
+	if (cl->cl_head) {
1513
+		b = cl->cl_head;
1514
+		do {
1515
+			sp->qlength += qlen(&b->queue);
1516
+			b = b->next;
1517
+		} while (b != cl->cl_head);
1518
+	}
1519
+
1520
+#ifdef ALTQ_RED
1521
+	if (cl->cl_qtype == Q_RED)
1522
+		red_getstats(cl->cl_red, &sp->red[0]);
1523
+#endif
1524
+#ifdef ALTQ_RIO
1525
+	if (cl->cl_qtype == Q_RIO)
1526
+		rio_getstats((rio_t *)cl->cl_red, &sp->red[0]);
1527
+#endif
1528
+}
1529
+
1530
+/* convert a class handle to the corresponding class pointer */
1531
+static struct fairq_class *
1532
+clh_to_clp(struct fairq_if *pif, uint32_t chandle)
1533
+{
1534
+	struct fairq_class *cl;
1535
+	int idx;
1536
+
1537
+	if (chandle == 0)
1538
+		return (NULL);
1539
+
1540
+	for (idx = pif->pif_maxpri; idx >= 0; idx--)
1541
+		if ((cl = pif->pif_classes[idx]) != NULL &&
1542
+		    cl->cl_handle == chandle)
1543
+			return (cl);
1544
+
1545
+	return (NULL);
1546
+}
1547
+
1548
+#endif /* ALTQ_FAIRQ */
1549
diff --git a/sys/contrib/altq/altq/altq_fairq.h b/sys/contrib/altq/altq/altq_fairq.h
1550
new file mode 100644
1551
index 0000000..e4675e0
1552
--- /dev/null
1553
+++ b/sys/contrib/altq/altq/altq_fairq.h
1554
@@ -0,0 +1,136 @@
1555
+/*
1556
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
1557
+ * 
1558
+ * This code is derived from software contributed to The DragonFly Project
1559
+ * by Matthew Dillon <dillon@backplane.com>
1560
+ * 
1561
+ * Redistribution and use in source and binary forms, with or without
1562
+ * modification, are permitted provided that the following conditions
1563
+ * are met:
1564
+ * 
1565
+ * 1. Redistributions of source code must retain the above copyright
1566
+ *    notice, this list of conditions and the following disclaimer.
1567
+ * 2. Redistributions in binary form must reproduce the above copyright
1568
+ *    notice, this list of conditions and the following disclaimer in
1569
+ *    the documentation and/or other materials provided with the
1570
+ *    distribution.
1571
+ * 3. Neither the name of The DragonFly Project nor the names of its
1572
+ *    contributors may be used to endorse or promote products derived
1573
+ *    from this software without specific, prior written permission.
1574
+ * 
1575
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1576
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1577
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1578
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
1579
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1580
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
1581
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1582
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
1583
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1584
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
1585
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1586
+ * SUCH DAMAGE.
1587
+ * 
1588
+ * $DragonFly: src/sys/net/altq/altq_fairq.h,v 1.1 2008/04/06 18:58:15 dillon Exp $
1589
+ */
1590
+
1591
+#ifndef _ALTQ_ALTQ_FAIRQ_H_
1592
+#define	_ALTQ_ALTQ_FAIRQ_H_
1593
+
1594
+#include <altq/altq.h>
1595
+#include <altq/altq_classq.h>
1596
+#include <altq/altq_red.h>
1597
+#include <altq/altq_rio.h>
1598
+#include <altq/altq_rmclass.h>
1599
+
1600
+#define	FAIRQ_MAX_BUCKETS	2048	/* maximum number of sorting buckets */
1601
+#define	FAIRQ_MAXPRI		RM_MAXPRIO
1602
+#define FAIRQ_BITMAP_WIDTH	(sizeof(fairq_bitmap_t)*8)
1603
+#define FAIRQ_BITMAP_MASK	(FAIRQ_BITMAP_WIDTH - 1)
1604
+
1605
+/* fairq class flags */
1606
+#define	FARF_RED		0x0001	/* use RED */
1607
+#define	FARF_ECN		0x0002  /* use RED/ECN */
1608
+#define	FARF_RIO		0x0004  /* use RIO */
1609
+#define	FARF_CLEARDSCP		0x0010  /* clear diffserv codepoint */
1610
+#define	FARF_DEFAULTCLASS	0x1000	/* default class */
1611
+
1612
+#define FARF_HAS_PACKETS	0x2000	/* might have queued packets */
1613
+
1614
+#define FARF_USERFLAGS		(FARF_RED|FARF_ECN|FARF_RIO|FARF_CLEARDSCP| \
1615
+				 FARF_DEFAULTCLASS)
1616
+
1617
+/* special class handles */
1618
+#define	FAIRQ_NULLCLASS_HANDLE	0
1619
+
1620
+typedef u_int	fairq_bitmap_t;
1621
+
1622
+struct fairq_classstats {
1623
+	uint32_t		class_handle;
1624
+
1625
+	u_int			qlength;
1626
+	u_int			qlimit;
1627
+	struct pktcntr		xmit_cnt;  /* transmitted packet counter */
1628
+	struct pktcntr		drop_cnt;  /* dropped packet counter */
1629
+
1630
+	/* red and rio related info */
1631
+	int			qtype;
1632
+	struct redstats		red[3];	/* rio has 3 red stats */
1633
+};
1634
+
1635
+#ifdef _KERNEL
1636
+
1637
+typedef struct fairq_bucket {
1638
+	struct fairq_bucket *next;	/* circular list */
1639
+	struct fairq_bucket *prev;	/* circular list */
1640
+	class_queue_t	queue;		/* the actual queue */
1641
+	uint64_t	bw_bytes;	/* statistics used to calculate bw */
1642
+	uint64_t	bw_delta;	/* statistics used to calculate bw */
1643
+	uint64_t	last_time;
1644
+	int		in_use;
1645
+} fairq_bucket_t;
1646
+
1647
+struct fairq_class {
1648
+	uint32_t	cl_handle;	/* class handle */
1649
+	u_int		cl_nbuckets;	/* (power of 2) */
1650
+	u_int		cl_nbucket_mask; /* bucket mask */
1651
+	fairq_bucket_t	*cl_buckets;
1652
+	fairq_bucket_t	*cl_head;	/* head of circular bucket list */
1653
+	fairq_bucket_t	*cl_polled;
1654
+	struct red	*cl_red;	/* RED state */
1655
+	u_int		cl_hogs_m1;
1656
+	u_int		cl_lssc_m1;
1657
+	u_int		cl_bandwidth;
1658
+	uint64_t	cl_bw_bytes;
1659
+	uint64_t	cl_bw_delta;
1660
+	uint64_t	cl_last_time;
1661
+	int		cl_qtype;	/* rollup */
1662
+	int		cl_qlimit;
1663
+	int		cl_pri;		/* priority */
1664
+	int		cl_flags;	/* class flags */
1665
+	struct fairq_if	*cl_pif;	/* back pointer to pif */
1666
+	struct altq_pktattr *cl_pktattr; /* saved header used by ECN */
1667
+
1668
+	/* round robin index */
1669
+
1670
+	/* statistics */
1671
+	struct pktcntr  cl_xmitcnt;	/* transmitted packet counter */
1672
+	struct pktcntr  cl_dropcnt;	/* dropped packet counter */
1673
+};
1674
+
1675
+/*
1676
+ * fairq interface state
1677
+ */
1678
+struct fairq_if {
1679
+	struct fairq_if		*pif_next;	/* interface state list */
1680
+	struct ifaltq		*pif_ifq;	/* backpointer to ifaltq */
1681
+	u_int			pif_bandwidth;	/* link bandwidth in bps */
1682
+	int			pif_maxpri;	/* max priority in use */
1683
+	struct fairq_class	*pif_poll_cache;/* cached poll */
1684
+	struct fairq_class	*pif_default;	/* default class */
1685
+	struct fairq_class	*pif_classes[FAIRQ_MAXPRI]; /* classes */
1686
+};
1687
+
1688
+#endif /* _KERNEL */
1689
+
1690
+#endif /* _ALTQ_ALTQ_FAIRQ_H_ */
1691
diff --git a/sys/contrib/altq/altq/altq_subr.c b/sys/contrib/altq/altq/altq_subr.c
1692
index 16b796a..d59751a 100644
1693
--- a/sys/contrib/altq/altq/altq_subr.c
1694
+++ b/sys/contrib/altq/altq/altq_subr.c
1695
@@ -537,6 +537,11 @@ altq_pfattach(struct pf_altq *a)
1696
 		error = hfsc_pfattach(a);
1697
 		break;
1698
 #endif
1699
+#ifdef ALTQ_FAIRQ
1700
+	case ALTQT_FAIRQ:
1701
+		error = fairq_pfattach(a);
1702
+		break;
1703
+#endif
1704
 	default:
1705
 		error = ENXIO;
1706
 	}
1707
@@ -612,6 +617,11 @@ altq_add(struct pf_altq *a)
1708
 		error = hfsc_add_altq(a);
1709
 		break;
1710
 #endif
1711
+#ifdef ALTQ_FAIRQ
1712
+        case ALTQT_FAIRQ:
1713
+                error = fairq_add_altq(a);
1714
+                break;
1715
+#endif
1716
 	default:
1717
 		error = ENXIO;
1718
 	}
1719
@@ -648,6 +658,11 @@ altq_remove(struct pf_altq *a)
1720
 		error = hfsc_remove_altq(a);
1721
 		break;
1722
 #endif
1723
+#ifdef ALTQ_FAIRQ
1724
+        case ALTQT_FAIRQ:
1725
+                error = fairq_remove_altq(a);
1726
+                break;
1727
+#endif
1728
 	default:
1729
 		error = ENXIO;
1730
 	}
1731
@@ -681,6 +696,11 @@ altq_add_queue(struct pf_altq *a)
1732
 		error = hfsc_add_queue(a);
1733
 		break;
1734
 #endif
1735
+#ifdef ALTQ_FAIRQ
1736
+        case ALTQT_FAIRQ:
1737
+                error = fairq_add_queue(a);
1738
+                break;
1739
+#endif
1740
 	default:
1741
 		error = ENXIO;
1742
 	}
1743
@@ -714,6 +734,11 @@ altq_remove_queue(struct pf_altq *a)
1744
 		error = hfsc_remove_queue(a);
1745
 		break;
1746
 #endif
1747
+#ifdef ALTQ_FAIRQ
1748
+        case ALTQT_FAIRQ:
1749
+                error = fairq_remove_queue(a);
1750
+                break;
1751
+#endif
1752
 	default:
1753
 		error = ENXIO;
1754
 	}
1755
@@ -747,6 +772,11 @@ altq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
1756
 		error = hfsc_getqstats(a, ubuf, nbytes);
1757
 		break;
1758
 #endif
1759
+#ifdef ALTQ_FAIRQ
1760
+        case ALTQT_FAIRQ:
1761
+                error = fairq_getqstats(a, ubuf, nbytes);
1762
+                break;
1763
+#endif
1764
 	default:
1765
 		error = ENXIO;
1766
 	}
1767
diff --git a/sys/contrib/altq/altq/altq_var.h b/sys/contrib/altq/altq/altq_var.h
1768
index 956ee16..eb603ea 100644
1769
--- a/sys/contrib/altq/altq/altq_var.h
1770
+++ b/sys/contrib/altq/altq/altq_var.h
1771
@@ -257,5 +257,12 @@ int	hfsc_add_queue(struct pf_altq *);
1772
 int	hfsc_remove_queue(struct pf_altq *);
1773
 int	hfsc_getqstats(struct pf_altq *, void *, int *);
1774
 
1775
+int	fairq_pfattach(struct pf_altq *);
1776
+int	fairq_add_altq(struct pf_altq *);
1777
+int	fairq_remove_altq(struct pf_altq *);
1778
+int	fairq_add_queue(struct pf_altq *);
1779
+int	fairq_remove_queue(struct pf_altq *);
1780
+int	fairq_getqstats(struct pf_altq *, void *, int *);
1781
+
1782
 #endif /* _KERNEL */
1783
 #endif /* _ALTQ_ALTQ_VAR_H_ */
1784
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
1785
index 686b68b..29c219e 100644
1786
--- a/sys/netpfil/pf/pf.c
1787
+++ b/sys/netpfil/pf/pf.c
1788
@@ -552,6 +552,21 @@ pf_hashsrc(struct pf_addr *addr, sa_family_t af)
1789
 	return (h & V_pf_srchashmask);
1790
 }
1791
 
1792
+#ifdef ALTQ
1793
+/* XXX: revisit this with ALTQ_WFQ/dummynet */
1794
+static int
1795
+pf_state_hash(struct pf_state *s)
1796
+{
1797
+	u_int32_t hv = (intptr_t)s / sizeof(*s);
1798
+
1799
+	hv ^= crc32(&s->src, sizeof(s->src));
1800
+	hv ^= crc32(&s->dst, sizeof(s->dst));
1801
+	if (hv == 0)
1802
+		hv = 1;
1803
+	return (hv);
1804
+}
1805
+#endif
1806
+
1807
 #ifdef INET6
1808
 void
1809
 pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af)
1810
@@ -6149,6 +6164,8 @@ done:
1811
 			action = PF_DROP;
1812
 			REASON_SET(&reason, PFRES_MEMORY);
1813
 		}
1814
+		if (s)
1815
+			pd.pf_mtag->qid_hash = pf_state_hash(s);
1816
 		if (pqid || (pd.tos & IPTOS_LOWDELAY))
1817
 			pd.pf_mtag->qid = r->pqid;
1818
 		else
1819
@@ -6636,6 +6653,8 @@ done:
1820
 			action = PF_DROP;
1821
 			REASON_SET(&reason, PFRES_MEMORY);
1822
 		}
1823
+		if (s)
1824
+			pd.pf_mtag->qid_hash = pf_state_hash(s);
1825
 		if (pd.tos & IPTOS_LOWDELAY)
1826
 			pd.pf_mtag->qid = r->pqid;
1827
 		else
1828
diff --git a/sys/netpfil/pf/pf_altq.h b/sys/netpfil/pf/pf_altq.h
1829
index eda0965..db681fb 100644
1830
--- a/sys/netpfil/pf/pf_altq.h
1831
+++ b/sys/netpfil/pf/pf_altq.h
1832
@@ -65,6 +65,20 @@ struct hfsc_opts {
1833
 	int		flags;
1834
 };
1835
 
1836
+/*
1837
+ * XXX this needs some work
1838
+ */
1839
+struct fairq_opts {
1840
+	u_int           nbuckets;
1841
+	u_int           hogs_m1;
1842
+	int             flags;
1843
+
1844
+	/* link sharing service curve */
1845
+	u_int           lssc_m1;
1846
+	u_int           lssc_d;
1847
+	u_int           lssc_m2;
1848
+};
1849
+
1850
 struct pf_altq {
1851
 	char			 ifname[IFNAMSIZ];
1852
 
1853
@@ -91,6 +105,7 @@ struct pf_altq {
1854
 		struct cbq_opts		 cbq_opts;
1855
 		struct priq_opts	 priq_opts;
1856
 		struct hfsc_opts	 hfsc_opts;
1857
+		struct fairq_opts        fairq_opts;
1858
 	} pq_u;
1859
 
1860
 	uint32_t		 qid;		/* return value */
1861
diff --git a/sys/netpfil/pf/pf_mtag.h b/sys/netpfil/pf/pf_mtag.h
1862
index baff00a..adb89d0 100644
1863
--- a/sys/netpfil/pf/pf_mtag.h
1864
+++ b/sys/netpfil/pf/pf_mtag.h
1865
@@ -43,9 +43,11 @@
1866
 struct pf_mtag {
1867
 	void		*hdr;		/* saved hdr pos in mbuf, for ECN */
1868
 	u_int32_t	 qid;		/* queue id */
1869
+	u_int32_t	 qid_hash;	/* queue hashid used by WFQ like algos */
1870
 	u_int16_t	 tag;		/* tag id */
1871
 	u_int8_t	 flags;
1872
 	u_int8_t	 routed;
1873
+	u_int8_t	 af;		/* for ECN */
1874
 };
1875
 
1876
 static __inline struct pf_mtag *
(17-17/67)