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 {

Reply via email to