This is the last bit required in order to actually be able to
use FQ-CoDel. It might require some polish, but I think there's
nothing exceptionally ugly barring the statistics interface.
fqcodel_stats is constructed to have a few fields "compatible"
with the hfsc_class_stats so that byte and packet counters can
be extracted in the exactly the same way.
The man page will follow.
OK?
diff --git sbin/pfctl/parse.y sbin/pfctl/parse.y
index 4f49b102609..b4cd978f6d3 100644
--- sbin/pfctl/parse.y
+++ sbin/pfctl/parse.y
@@ -304,19 +304,31 @@ struct node_sc {
struct node_queue_bw m1;
u_int d;
struct node_queue_bw m2;
};
+struct node_fq {
+ u_int flows;
+ u_int quantum;
+ u_int target;
+ u_int interval;
+};
+
struct queue_opts {
int marker;
#define QOM_BWSPEC 0x01
#define QOM_PARENT 0x02
#define QOM_DEFAULT 0x04
#define QOM_QLIMIT 0x08
+#define QOM_FLOWS 0x10
+#define QOM_QUANTUM 0x20
+#define QOM_INTERVAL 0x40
+#define QOM_TARGET 0x80
struct node_sc realtime;
struct node_sc linkshare;
struct node_sc upperlimit;
+ struct node_fq flowqueue;
char *parent;
int flags;
u_int qlimit;
} queue_opts;
@@ -457,11 +469,11 @@ int parseport(char *, struct range *r, int);
%token REASSEMBLE ANCHOR
%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
%token SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
%token ANTISPOOF FOR INCLUDE MATCHES
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT PROBABILITY
-%token WEIGHT BANDWIDTH
+%token WEIGHT BANDWIDTH FLOWS QUANTUM INTERVAL TARGET
%token QUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT
%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE
@@ -1317,10 +1329,15 @@ queue_opts_l : queue_opts_l queue_opt
queue_opt : BANDWIDTH scspec optscs {
if (queue_opts.marker & QOM_BWSPEC) {
yyerror("bandwidth cannot be respecified");
YYERROR;
}
+ if (queue_opts.marker & QOM_FLOWS) {
+ yyerror("bandwidth cannot be specified for "
+ "a flow queue");
+ YYERROR;
+ }
queue_opts.marker |= QOM_BWSPEC;
queue_opts.linkshare = $2;
queue_opts.realtime= $3.realtime;
queue_opts.upperlimit = $3.upperlimit;
}
@@ -1336,13 +1353,13 @@ queue_opt : BANDWIDTH scspec optscs
{
if (queue_opts.marker & QOM_DEFAULT) {
yyerror("default cannot be respecified");
YYERROR;
}
queue_opts.marker |= QOM_DEFAULT;
- queue_opts.flags |= HFSC_DEFAULTCLASS;
+ queue_opts.flags |= PFQS_DEFAULT;
}
- | QLIMIT NUMBER {
+ | QLIMIT NUMBER {
if (queue_opts.marker & QOM_QLIMIT) {
yyerror("qlimit cannot be respecified");
YYERROR;
}
if ($2 < 0 || $2 > 65535) {
@@ -1350,10 +1367,83 @@ queue_opt : BANDWIDTH scspec optscs
{
YYERROR;
}
queue_opts.marker |= QOM_QLIMIT;
queue_opts.qlimit = $2;
}
+ | FLOWS NUMBER {
+ if (queue_opts.marker & QOM_FLOWS) {
+ yyerror("number of flows cannot be
respecified");
+ YYERROR;
+ }
+ if (queue_opts.marker & QOM_BWSPEC) {
+ yyerror("bandwidth cannot be specified for "
+ "a flow queue");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 32767) {
+ yyerror("number of flows out of range: "
+ "max 32767");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_FLOWS;
+ queue_opts.flags |= PFQS_FQCODEL;
+ queue_opts.flowqueue.flows = $2;
+ }
+ | QUANTUM NUMBER {
+ if (queue_opts.marker & QOM_QUANTUM) {
+ yyerror("quantum cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 65535) {
+ yyerror("quantum out of range: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_QUANTUM;
+ queue_opts.flowqueue.quantum = $2;
+ }
+ | INTERVAL STRING {
+ u_long ul;
+ char *cp;
+
+ if (queue_opts.marker & QOM_INTERVAL) {
+ yyerror("interval cannot be respecified");
+ YYERROR;
+ }
+
+ ul = strtoul($2, &cp, 10);
+ if (cp == NULL || strcmp(cp, "ms")) {
+ yyerror("interval must be in ms");
+ YYERROR;
+ }
+ if (ul < 0 || ul > 65535) {
+ yyerror("interval out of range: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_INTERVAL;
+ queue_opts.flowqueue.interval = ul * 1000;
+ }
+ | TARGET STRING {
+ u_long ul;
+ char *cp;
+
+ if (queue_opts.marker & QOM_TARGET) {
+ yyerror("target cannot be respecified");
+ YYERROR;
+ }
+
+ ul = strtoul($2, &cp, 10);
+ if (cp == NULL || strcmp(cp, "ms")) {
+ yyerror("target must be in ms");
+ YYERROR;
+ }
+ if (ul < 0 || ul > 65535) {
+ yyerror("target out of range: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_TARGET;
+ queue_opts.flowqueue.target = ul * 1000;
+ }
;
optscs : /* nada */ {
}
@@ -4296,10 +4386,14 @@ expand_queue(char *qname, struct node_if *interfaces,
struct queue_opts *opts)
{
struct pf_queuespec qspec;
LOOP_THROUGH(struct node_if, interface, interfaces,
bzero(&qspec, sizeof(qspec));
+ if ((opts->flags & PFQS_FQCODEL) && opts->parent) {
+ yyerror("discipline doesn't support hierarchy");
+ return (1);
+ }
if (strlcpy(qspec.qname, qname, sizeof(qspec.qname)) >=
sizeof(qspec.qname)) {
yyerror("queuename too long");
return (1);
}
@@ -4329,10 +4423,15 @@ expand_queue(char *qname, struct node_if *interfaces,
struct queue_opts *opts)
qspec.upperlimit.m1.percent = opts->upperlimit.m1.bw_percent;
qspec.upperlimit.m2.absolute = opts->upperlimit.m2.bw_absolute;
qspec.upperlimit.m2.percent = opts->upperlimit.m2.bw_percent;
qspec.upperlimit.d = opts->upperlimit.d;
+ qspec.flowqueue.flows = opts->flowqueue.flows;
+ qspec.flowqueue.quantum = opts->flowqueue.quantum;
+ qspec.flowqueue.interval = opts->flowqueue.interval;
+ qspec.flowqueue.target = opts->flowqueue.target;
+
qspec.flags = opts->flags;
qspec.qlimit = opts->qlimit;
if (pfctl_add_queue(pf, &qspec)) {
yyerror("cannot add queue");
@@ -4983,10 +5082,11 @@ lookup(char *s)
{ "dup-to", DUPTO},
{ "file", FILENAME},
{ "fingerprints", FINGERPRINTS},
{ "flags", FLAGS},
{ "floating", FLOATING},
+ { "flows", FLOWS},
{ "flush", FLUSH},
{ "for", FOR},
{ "fragment", FRAGMENT},
{ "from", FROM},
{ "global", GLOBAL},
@@ -4997,10 +5097,11 @@ lookup(char *s)
{ "if-bound", IFBOUND},
{ "in", IN},
{ "include", INCLUDE},
{ "inet", INET},
{ "inet6", INET6},
+ { "interval", INTERVAL},
{ "keep", KEEP},
{ "label", LABEL},
{ "least-states", LEASTSTATES},
{ "limit", LIMIT},
{ "load", LOAD},
@@ -5034,10 +5135,11 @@ lookup(char *s)
{ "port", PORT},
{ "prio", PRIO},
{ "probability", PROBABILITY},
{ "proto", PROTO},
{ "qlimit", QLIMIT},
+ { "quantum", QUANTUM},
{ "queue", QUEUE},
{ "quick", QUICK},
{ "random", RANDOM},
{ "random-id", RANDOMID},
{ "rdomain", RDOMAIN},
@@ -5068,10 +5170,11 @@ lookup(char *s)
{ "sticky-address", STICKYADDRESS},
{ "synproxy", SYNPROXY},
{ "table", TABLE},
{ "tag", TAG},
{ "tagged", TAGGED},
+ { "target", TARGET},
{ "timeout", TIMEOUT},
{ "to", TO},
{ "tos", TOS},
{ "ttl", TTL},
{ "urpf-failed", URPFFAILED},
diff --git sbin/pfctl/pfctl.c sbin/pfctl/pfctl.c
index 9c39f37bb14..467b31f3d10 100644
--- sbin/pfctl/pfctl.c
+++ sbin/pfctl/pfctl.c
@@ -1418,11 +1418,12 @@ pfctl_check_qassignments(struct pf_ruleset *rs)
/* main ruleset: need find_childqs to populate qi->children */
if (rs->anchor->path[0] == 0) {
TAILQ_FOREACH(qi, &rootqs, entries) {
flags = pfctl_find_childqs(qi);
- if (!(flags & HFSC_DEFAULTCLASS))
+ if (!(qi->qs.flags & PFQS_FQCODEL) &&
+ !(flags & PFQS_DEFAULT))
errx(1, "no default queue specified");
}
}
TAILQ_FOREACH(r, rs->rules.active.ptr, entries) {
diff --git sbin/pfctl/pfctl_parser.c sbin/pfctl/pfctl_parser.c
index e241b11f6fc..052fb2599fa 100644
--- sbin/pfctl/pfctl_parser.c
+++ sbin/pfctl/pfctl_parser.c
@@ -1203,13 +1203,23 @@ print_queuespec(struct pf_queuespec *q)
printf("queue %s", q->qname);
if (q->parent[0] && q->parent[0] != '_')
printf(" parent %s", q->parent);
else if (q->ifname[0])
printf(" on %s", q->ifname);
- print_scspec(" bandwidth ", &q->linkshare);
- print_scspec(", min ", &q->realtime);
- print_scspec(", max ", &q->upperlimit);
+ if (q->flowqueue.flows > 0) {
+ printf(" flows %u", q->flowqueue.flows);
+ if (q->flowqueue.quantum > 0)
+ printf(" quantum %u", q->flowqueue.quantum);
+ if (q->flowqueue.interval > 0)
+ printf(" interval %ums", q->flowqueue.interval / 1000);
+ if (q->flowqueue.target > 0)
+ printf(" target %ums", q->flowqueue.target / 1000);
+ } else {
+ print_scspec(" bandwidth ", &q->linkshare);
+ print_scspec(", min ", &q->realtime);
+ print_scspec(", max ", &q->upperlimit);
+ }
if (q->flags & HFSC_DEFAULTCLASS)
printf(" default");
if (q->qlimit)
printf(" qlimit %u", q->qlimit);
printf("\n");
diff --git sbin/pfctl/pfctl_queue.c sbin/pfctl/pfctl_queue.c
index 6e59fff7d22..341effc1284 100644
--- sbin/pfctl/pfctl_queue.c
+++ sbin/pfctl/pfctl_queue.c
@@ -24,25 +24,30 @@
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <err.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/hfsc.h>
+#include <net/fq_codel.h>
#include "pfctl.h"
#include "pfctl_parser.h"
#define AVGN_MAX 8
#define STAT_INTERVAL 5
struct queue_stats {
- struct hfsc_class_stats data;
+ union {
+ struct hfsc_class_stats hfsc;
+ struct fqcodel_stats fqc;
+ } data;
int avgn;
double avg_bytes;
double avg_packets;
u_int64_t prev_bytes;
u_int64_t prev_packets;
@@ -194,18 +199,37 @@ pfctl_print_queue_node(int dev, struct pfctl_queue_node
*node, int opts)
}
void
pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node)
{
+ struct hfsc_class_stats *stats =
+ (struct hfsc_class_stats *)&node->qstats.data.hfsc;
+ struct fqcodel_stats *fqstats =
+ (struct fqcodel_stats *)&node->qstats.data.fqc;
+
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
- (unsigned long long)node->qstats.data.xmit_cnt.packets,
- (unsigned long long)node->qstats.data.xmit_cnt.bytes,
- (unsigned long long)node->qstats.data.drop_cnt.packets,
- (unsigned long long)node->qstats.data.drop_cnt.bytes);
- printf(" [ qlength: %3d/%3d ]\n", node->qstats.data.qlength,
- node->qstats.data.qlimit);
+ (unsigned long long)stats->xmit_cnt.packets,
+ (unsigned long long)stats->xmit_cnt.bytes,
+ (unsigned long long)stats->drop_cnt.packets,
+ (unsigned long long)stats->drop_cnt.bytes);
+ if (node->qs.flags & PFQS_FQCODEL) {
+ double avg = 0, dev = 0;
+
+ if (fqstats->flows > 0) {
+ avg = (double)fqstats->qlensum / (double)fqstats->flows;
+ dev = sqrt(fmax(0, (double)fqstats->qlensumsq /
+ (double)fqstats->flows - avg * avg));
+ }
+
+ printf(" [ qlength: %3d/%3d min/max/avg/dev: "
+ "%3d/%3d/%6.2f/%6.2f flows: %4d ]\n",
+ stats->qlength, stats->qlimit, fqstats->minqlen,
+ fqstats->maxqlen, avg, dev, fqstats->flows);
+ } else
+ printf(" [ qlength: %3d/%3d ]\n", stats->qlength,
+ stats->qlimit);
if (node->qstats.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
@@ -214,23 +238,26 @@ pfctl_print_queue_nodestat(int dev, const struct
pfctl_queue_node *node)
}
void
update_avg(struct queue_stats *s)
{
+ struct hfsc_class_stats *stats =
+ (struct hfsc_class_stats *)&s->data;
+
if (s->avgn > 0) {
- if (s->data.xmit_cnt.bytes >= s->prev_bytes)
+ if (stats->xmit_cnt.bytes >= s->prev_bytes)
s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) +
- (s->data.xmit_cnt.bytes - s->prev_bytes)) /
+ (stats->xmit_cnt.bytes - s->prev_bytes)) /
s->avgn;
- if (s->data.xmit_cnt.packets >= s->prev_packets)
+ if (stats->xmit_cnt.packets >= s->prev_packets)
s->avg_packets = ((s->avg_packets * (s->avgn - 1)) +
- (s->data.xmit_cnt.packets - s->prev_packets)) /
+ (stats->xmit_cnt.packets - s->prev_packets)) /
s->avgn;
}
- s->prev_bytes = s->data.xmit_cnt.bytes;
- s->prev_packets = s->data.xmit_cnt.packets;
+ s->prev_bytes = stats->xmit_cnt.bytes;
+ s->prev_packets = stats->xmit_cnt.packets;
if (s->avgn < AVGN_MAX)
s->avgn++;
}
#define R2S_BUFS 8
diff --git sys/conf/files sys/conf/files
index e9d35078631..510f5b2b7e1 100644
--- sys/conf/files
+++ sys/conf/files
@@ -762,10 +762,11 @@ file tmpfs/tmpfs_vnops.c tmpfs
file tmpfs/tmpfs_specops.c tmpfs
file tmpfs/tmpfs_fifoops.c tmpfs & fifo
file net/art.c art
file net/bpf.c bpfilter needs-count
file net/bpf_filter.c bpfilter
+file net/fq_codel.c pf
file net/if.c
file net/ifq.c
file net/if_ethersubr.c ether
needs-flag
file net/if_etherip.c etherip needs-flag
file net/if_spppsubr.c sppp
diff --git sys/net/fq_codel.c sys/net/fq_codel.c
index 96a7cd3beaa..b990e63188f 100644
--- sys/net/fq_codel.c
+++ sys/net/fq_codel.c
@@ -138,10 +138,28 @@ static const struct ifq_ops fqcodel_ops = {
fqcodel_free,
};
const struct ifq_ops * const ifq_fqcodel_ops = &fqcodel_ops;
+void *fqcodel_pf_alloc(struct ifnet *);
+int fqcodel_pf_addqueue(void *, struct pf_queuespec *);
+void fqcodel_pf_free(void *);
+int fqcodel_pf_qstats(struct pf_queuespec *, void *, int *);
+
+/*
+ * pf queue glue.
+ */
+
+static const struct pfq_ops fqcodel_pf_ops = {
+ fqcodel_pf_alloc,
+ fqcodel_pf_addqueue,
+ fqcodel_pf_free,
+ fqcodel_pf_qstats
+};
+
+const struct pfq_ops * const pfq_fqcodel_ops = &fqcodel_pf_ops;
+
/* Default aggregate queue depth */
static const unsigned int fqcodel_qlimit = 1024;
/* Packet drop threshold */
static const unsigned int fqcodel_threshold = 64;
@@ -665,10 +683,138 @@ fqcodel_purge(struct ifqueue *ifq, struct mbuf_list *ml)
for (i = 0; i < fqc->nflows; i++)
codel_purge(&fqc->flows[i].cd, ml);
}
+void *
+fqcodel_pf_alloc(struct ifnet *ifp)
+{
+ struct fqcodel *fqc;
+
+ fqc = malloc(sizeof(struct fqcodel), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ return (fqc);
+}
+
+int
+fqcodel_pf_addqueue(void *arg, struct pf_queuespec *qs)
+{
+ struct ifnet *ifp = qs->kif->pfik_ifp;
+ struct fqcodel *fqc = arg;
+
+ KASSERT(qs->parent_qid == 0);
+
+ if (qs->flowqueue.flows == 0 || qs->flowqueue.flows > M_FLOWID_MASK)
+ return (EINVAL);
+
+ fqc->nflows = qs->flowqueue.flows;
+ fqc->quantum = qs->flowqueue.quantum;
+ if (qs->qlimit > 0)
+ fqc->qlimit = qs->qlimit;
+ else
+ fqc->qlimit = fqcodel_qlimit;
+ if (fqc->quantum > 0)
+ fqc->flags |= FQCF_FIXED_QUANTUM;
+ else
+ fqc->quantum = ifp->if_mtu + max_linkhdr;
+
+ codel_initparams(&fqc->cparams, qs->flowqueue.target,
+ qs->flowqueue.interval, fqc->quantum);
+
+ fqc->flows = mallocarray(fqc->nflows, sizeof(struct flow),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+#ifdef FQCODEL_DEBUG
+ {
+ unsigned int i;
+
+ for (i = 0; i < fqc->nflows; i++)
+ fqc->flows[i].id = i;
+ }
+#endif
+
+ DPRINTF("fq-codel on %s: %d queues %d deep, quantum %d target %lums "
+ "interval %lums\n", ifp->if_xname, fqc->nflows, fqc->qlimit,
+ fqc->quantum, fqc->cparams.target.tv_usec / 1000,
+ fqc->cparams.interval.tv_usec / 1000);
+
+ return (0);
+}
+
+void
+fqcodel_pf_free(void *arg)
+{
+ struct fqcodel *fqc = arg;
+
+ codel_freeparams(&fqc->cparams);
+ free(fqc->flows, M_DEVBUF, fqc->nflows * sizeof(struct flow));
+ free(fqc, M_DEVBUF, sizeof(struct fqcodel));
+}
+
+int
+fqcodel_pf_qstats(struct pf_queuespec *qs, void *ubuf, int *nbytes)
+{
+ struct ifnet *ifp = qs->kif->pfik_ifp;
+ struct fqcodel_stats stats;
+ struct fqcodel *fqc;
+ unsigned int i, qlen;
+ int error = 0;
+
+ if (ifp == NULL)
+ return (EBADF);
+
+ if (*nbytes < sizeof(stats))
+ return (EINVAL);
+
+ memset(&stats, 0, sizeof(stats));
+
+ /* XXX: multi-q? */
+ fqc = ifq_q_enter(&ifp->if_snd, ifq_fqcodel_ops);
+ if (fqc == NULL)
+ return (EBADF);
+
+ stats.xmit_cnt = fqc->xmit_cnt;
+ stats.drop_cnt = fqc->drop_cnt;
+
+ stats.qlength = ifq_len(&ifp->if_snd);
+ stats.qlimit = fqc->qlimit;
+
+ stats.target = fqc->cparams.target.tv_sec * 1000000 +
+ fqc->cparams.target.tv_usec;
+ stats.interval = fqc->cparams.interval.tv_sec * 1000000 +
+ fqc->cparams.interval.tv_usec;
+
+ stats.flows = stats.maxqlen = stats.minqlen = 0;
+ stats.qlensum = stats.qlensumsq = 0;
+
+ for (i = 0; i < fqc->nflows; i++) {
+ qlen = codel_qlength(&fqc->flows[i].cd);
+ if (qlen == 0)
+ continue;
+ if (stats.minqlen == 0)
+ stats.minqlen = qlen;
+ else
+ stats.minqlen = MIN(stats.minqlen, qlen);
+ if (stats.maxqlen == 0)
+ stats.maxqlen = qlen;
+ else
+ stats.maxqlen = MAX(stats.maxqlen, qlen);
+ stats.flows++;
+
+ stats.qlensum += qlen;
+ stats.qlensumsq += (uint64_t)qlen * (uint64_t)qlen;
+ }
+
+ ifq_q_leave(&ifp->if_snd, fqc);
+
+ if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
+ return (error);
+
+ *nbytes = sizeof(stats);
+ return (0);
+}
+
unsigned int
fqcodel_idx(unsigned int nqueues, const struct mbuf *m)
{
return (0);
}
diff --git sys/net/fq_codel.h sys/net/fq_codel.h
index 7d79f5bcc7e..7b4d0844187 100644
--- sys/net/fq_codel.h
+++ sys/net/fq_codel.h
@@ -44,8 +44,9 @@ struct fqcodel_stats {
uint64_t qlensumsq; /* sum of squared queue lenghts */
};
#ifdef _KERNEL
extern const struct ifq_ops * const ifq_fqcodel_ops;
+extern const struct pfq_ops * const pfq_fqcodel_ops;
#endif /* _KERNEL */
#endif /* _NET_FQ_CODEL_H_ */
diff --git sys/net/pf_ioctl.c sys/net/pf_ioctl.c
index dec229af8d1..79eaedf4137 100644
--- sys/net/pf_ioctl.c
+++ sys/net/pf_ioctl.c
@@ -59,10 +59,11 @@
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/hfsc.h>
+#include <net/fq_codel.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
@@ -596,12 +597,17 @@ pf_create_queues(void)
continue;
qif = malloc(sizeof(*qif), M_TEMP, M_WAITOK);
qif->ifp = ifp;
- qif->ifqops = ifq_hfsc_ops;
- qif->pfqops = pfq_hfsc_ops;
+ if (q->flags & PFQS_FQCODEL) {
+ qif->ifqops = ifq_fqcodel_ops;
+ qif->pfqops = pfq_fqcodel_ops;
+ } else {
+ qif->ifqops = ifq_hfsc_ops;
+ qif->pfqops = pfq_hfsc_ops;
+ }
qif->disc = qif->pfqops->pfq_alloc(ifp);
qif->next = list;
list = qif;
@@ -1086,11 +1092,16 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
struct proc *p)
if (qs == NULL) {
error = EBUSY;
break;
}
bcopy(qs, &pq->queue, sizeof(pq->queue));
- error = pfq_hfsc_ops->pfq_qstats(qs, pq->buf, &nbytes);
+ if (qs->flags & PFQS_FQCODEL)
+ error = pfq_fqcodel_ops->pfq_qstats(qs, pq->buf,
+ &nbytes);
+ else
+ error = pfq_hfsc_ops->pfq_qstats(qs, pq->buf,
+ &nbytes);
if (error == 0)
pq->nbytes = nbytes;
break;
}
diff --git sys/net/pfvar.h sys/net/pfvar.h
index 5eba2776b39..06d864d2d8e 100644
--- sys/net/pfvar.h
+++ sys/net/pfvar.h
@@ -1308,25 +1308,37 @@ struct pf_queue_scspec {
struct pf_queue_bwspec m1;
struct pf_queue_bwspec m2;
u_int d;
};
+struct pf_queue_fqspec {
+ u_int flows;
+ u_int quantum;
+ u_int target;
+ u_int interval;
+};
+
struct pf_queuespec {
TAILQ_ENTRY(pf_queuespec) entries;
char qname[PF_QNAME_SIZE];
char parent[PF_QNAME_SIZE];
char ifname[IFNAMSIZ];
struct pf_queue_scspec realtime;
struct pf_queue_scspec linkshare;
struct pf_queue_scspec upperlimit;
+ struct pf_queue_fqspec flowqueue;
struct pfi_kif *kif;
u_int flags;
u_int qlimit;
u_int32_t qid;
u_int32_t parent_qid;
};
+#define PFQS_HFSC 0x0000
+#define PFQS_FQCODEL 0x0001
+#define PFQS_DEFAULT 0x1000
+
struct priq_opts {
int flags;
};
struct hfsc_opts {