From c548a571ceaeae698d02b862b4170d015a265ba7 Mon Sep 17 00:00:00 2001 From: Luiz Otavio O Souza Date: Wed, 11 Nov 2015 13:59:04 -0600 Subject: MFC r284777: ALTQ FAIRQ discipline import from DragonFLY Differential Revision: https://reviews.freebsd.org/D2847 Reviewed by: glebius, wblock(manpage) Approved by: gnn(mentor) Obtained from: pfSense Sponsored by: Netgate TAG: FAIRQ --- sbin/pfctl/parse.y | 75 +++++++++++++++- sbin/pfctl/pfctl_altq.c | 221 +++++++++++++++++++++++++++++++++++++++++++++- sbin/pfctl/pfctl_parser.h | 15 ++++ sbin/pfctl/pfctl_qstats.c | 30 +++++++ 4 files changed, 337 insertions(+), 4 deletions(-) (limited to 'sbin') diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 3edb634..2f98692 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -317,6 +318,7 @@ struct pool_opts { struct node_hfsc_opts hfsc_opts; +struct node_fairq_opts fairq_opts; struct node_state_opt *keep_state_defaults = NULL; int disallow_table(struct node_host *, const char *); @@ -440,6 +442,7 @@ typedef struct { struct table_opts table_opts; struct pool_opts pool_opts; struct node_hfsc_opts hfsc_opts; + struct node_fairq_opts fairq_opts; } v; int lineno; } YYSTYPE; @@ -464,8 +467,8 @@ int parseport(char *, struct range *r, int); %token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR INCLUDE %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY -%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT RTABLE +%token ALTQ CBQ PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT +%token QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE %token DNPIPE DNQUEUE %token LOAD RULESET_OPTIMIZATION %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE @@ -515,6 +518,7 @@ int parseport(char *, struct range *r, int); %type cbqflags_list cbqflags_item %type priqflags_list priqflags_item %type hfscopts_list hfscopts_item hfsc_opts +%type fairqopts_list fairqopts_item fairq_opts %type bandwidth %type filter_opts filter_opt filter_opts_l %type antispoof_opts antispoof_opt antispoof_opts_l @@ -1697,6 +1701,15 @@ scheduler : CBQ { $$.qtype = ALTQT_HFSC; $$.data.hfsc_opts = $3; } + | FAIRQ { + $$.qtype = ALTQT_FAIRQ; + bzero(&$$.data.fairq_opts, + sizeof(struct node_fairq_opts)); + } + | FAIRQ '(' fairq_opts ')' { + $$.qtype = ALTQT_FAIRQ; + $$.data.fairq_opts = $3; + } ; cbqflags_list : cbqflags_item { $$ |= $1; } @@ -1845,6 +1858,61 @@ hfscopts_item : LINKSHARE bandwidth { } ; +fairq_opts : { + bzero(&fairq_opts, + sizeof(struct node_fairq_opts)); + } + fairqopts_list { + $$ = fairq_opts; + } + ; + +fairqopts_list : fairqopts_item + | fairqopts_list comma fairqopts_item + ; + +fairqopts_item : LINKSHARE bandwidth { + if (fairq_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + fairq_opts.linkshare.m2 = $2; + fairq_opts.linkshare.used = 1; + } + | LINKSHARE '(' bandwidth number bandwidth ')' { + if (fairq_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + fairq_opts.linkshare.m1 = $3; + fairq_opts.linkshare.d = $4; + fairq_opts.linkshare.m2 = $5; + fairq_opts.linkshare.used = 1; + } + | HOGS bandwidth { + fairq_opts.hogs_bw = $2; + } + | BUCKETS number { + fairq_opts.nbuckets = $2; + } + | STRING { + if (!strcmp($1, "default")) + fairq_opts.flags |= FARF_DEFAULTCLASS; + else if (!strcmp($1, "red")) + fairq_opts.flags |= FARF_RED; + else if (!strcmp($1, "ecn")) + fairq_opts.flags |= FARF_RED|FARF_ECN; + else if (!strcmp($1, "rio")) + fairq_opts.flags |= FARF_RIO; + else { + yyerror("unknown fairq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + qassign : /* empty */ { $$ = NULL; } | qassign_item { $$ = $1; } | '{' optnl qassign_list '}' { $$ = $3; } @@ -5516,6 +5584,7 @@ lookup(char *s) { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, + { "buckets", BUCKETS}, { "cbq", CBQ}, { "code", CODE}, { "crop", FRAGCROP}, @@ -5528,6 +5597,7 @@ lookup(char *s) { "drop-ovl", FRAGDROP}, { "dscp", DSCP}, { "dup-to", DUPTO}, + { "fairq", FAIRQ}, { "fastroute", FASTROUTE}, { "file", FILENAME}, { "fingerprints", FINGERPRINTS}, @@ -5540,6 +5610,7 @@ lookup(char *s) { "global", GLOBAL}, { "group", GROUP}, { "hfsc", HFSC}, + { "hogs", HOGS}, { "hostid", HOSTID}, { "icmp-type", ICMPTYPE}, { "icmp6-type", ICMP6TYPE}, diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c index be2776c..adba457 100644 --- a/sbin/pfctl/pfctl_altq.c +++ b/sbin/pfctl/pfctl_altq.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "pfctl_parser.h" #include "pfctl.h" @@ -69,6 +70,11 @@ static int check_commit_hfsc(int, int, struct pf_altq *); static int print_hfsc_opts(const struct pf_altq *, const struct node_queue_opt *); +static int eval_pfqueue_fairq(struct pfctl *, struct pf_altq *); +static int print_fairq_opts(const struct pf_altq *, + const struct node_queue_opt *); +static int check_commit_fairq(int, int, struct pf_altq *); + static void gsc_add_sc(struct gen_sc *, struct service_curve *); static int is_gsc_under_sc(struct gen_sc *, struct service_curve *); @@ -89,6 +95,8 @@ int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); void print_hfsc_sc(const char *, u_int, u_int, u_int, const struct node_hfsc_sc *); +void print_fairq_sc(const char *, u_int, u_int, u_int, + const struct node_fairq_sc *); void pfaltq_store(struct pf_altq *a) @@ -174,6 +182,10 @@ print_altq(const struct pf_altq *a, unsigned int level, if (!print_hfsc_opts(a, qopts)) printf("hfsc "); break; + case ALTQT_FAIRQ: + if (!print_fairq_opts(a, qopts)) + printf("fairq "); + break; } if (bw != NULL && bw->bw_percent > 0) { @@ -204,7 +216,8 @@ print_queue(const struct pf_altq *a, unsigned int level, printf("%s ", a->qname); if (print_interface) printf("on %s ", a->ifname); - if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { + if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC || + a->scheduler == ALTQT_FAIRQ) { if (bw != NULL && bw->bw_percent > 0) { if (bw->bw_percent < 100) printf("bandwidth %u%% ", bw->bw_percent); @@ -225,6 +238,9 @@ print_queue(const struct pf_altq *a, unsigned int level, case ALTQT_HFSC: print_hfsc_opts(a, qopts); break; + case ALTQT_FAIRQ: + print_fairq_opts(a, qopts); + break; } } @@ -291,6 +307,9 @@ check_commit_altq(int dev, int opts) case ALTQT_HFSC: error = check_commit_hfsc(dev, opts, altq); break; + case ALTQT_FAIRQ: + error = check_commit_fairq(dev, opts, altq); + break; default: break; } @@ -339,7 +358,8 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, if (pa->qlimit == 0) pa->qlimit = DEFAULT_QLIMIT; - if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { + if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC || + pa->scheduler == ALTQT_FAIRQ) { pa->bandwidth = eval_bwspec(bw, parent == NULL ? 0 : parent->bandwidth); @@ -385,6 +405,9 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, case ALTQT_HFSC: error = eval_pfqueue_hfsc(pf, pa); break; + case ALTQT_FAIRQ: + error = eval_pfqueue_fairq(pf, pa); + break; default: break; } @@ -797,6 +820,85 @@ err_ret: return (-1); } +/* + * FAIRQ support functions + */ +static int +eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa) +{ + struct pf_altq *altq, *parent; + struct fairq_opts *opts; + struct service_curve sc; + + opts = &pa->pq_u.fairq_opts; + + if (pa->parent[0] == 0) { + /* root queue */ + opts->lssc_m1 = pa->ifbandwidth; + opts->lssc_m2 = pa->ifbandwidth; + opts->lssc_d = 0; + return (0); + } + + LIST_INIT(&lssc); + + /* if link_share is not specified, use bandwidth */ + if (opts->lssc_m2 == 0) + opts->lssc_m2 = pa->bandwidth; + + /* + * admission control: + * for the real-time service curve, the sum of the service curves + * should not exceed 80% of the interface bandwidth. 20% is reserved + * not to over-commit the actual interface bandwidth. + * for the link-sharing service curve, the sum of the child service + * curve should not exceed the parent service curve. + * for the upper-limit service curve, the assigned bandwidth should + * be smaller than the interface bandwidth, and the upper-limit should + * be larger than the real-time service curve when both are defined. + */ + parent = qname_to_pfaltq(pa->parent, pa->ifname); + if (parent == NULL) + errx(1, "parent %s not found for %s", pa->parent, pa->qname); + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + + if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) + continue; + + /* if the class has a link-sharing service curve, add it. */ + if (opts->lssc_m2 != 0 && altq->pq_u.fairq_opts.lssc_m2 != 0) { + sc.m1 = altq->pq_u.fairq_opts.lssc_m1; + sc.d = altq->pq_u.fairq_opts.lssc_d; + sc.m2 = altq->pq_u.fairq_opts.lssc_m2; + gsc_add_sc(&lssc, &sc); + } + } + + /* check the link-sharing service curve. */ + if (opts->lssc_m2 != 0) { + sc.m1 = parent->pq_u.fairq_opts.lssc_m1; + sc.d = parent->pq_u.fairq_opts.lssc_d; + sc.m2 = parent->pq_u.fairq_opts.lssc_m2; + if (!is_gsc_under_sc(&lssc, &sc)) { + warnx("link-sharing sc exceeds parent's sc"); + goto err_ret; + } + } + + gsc_destroy(&lssc); + + return (0); + +err_ret: + gsc_destroy(&lssc); + return (-1); +} + static int check_commit_hfsc(int dev, int opts, struct pf_altq *pa) { @@ -837,6 +939,43 @@ check_commit_hfsc(int dev, int opts, struct pf_altq *pa) } static int +check_commit_fairq(int dev __unused, int opts __unused, struct pf_altq *pa) +{ + struct pf_altq *altq, *def = NULL; + int default_class; + int error = 0; + + /* check if fairq has one default queue for this interface */ + default_class = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (altq->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) { + default_class++; + def = altq; + } + } + if (default_class != 1) { + warnx("should have one default queue on %s", pa->ifname); + return (1); + } + /* make sure the default queue is a leaf */ + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { + warnx("default queue is not a leaf"); + error++; + } + } + return (error); +} + +static int print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { const struct hfsc_opts *opts; @@ -882,6 +1021,43 @@ print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) return (0); } +static int +print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) +{ + const struct fairq_opts *opts; + const struct node_fairq_sc *loc_lssc; + + opts = &a->pq_u.fairq_opts; + if (qopts == NULL) + loc_lssc = NULL; + else + loc_lssc = &qopts->data.fairq_opts.linkshare; + + if (opts->flags || + (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0))) { + printf("fairq("); + if (opts->flags & FARF_RED) + printf(" red"); + if (opts->flags & FARF_ECN) + printf(" ecn"); + if (opts->flags & FARF_RIO) + printf(" rio"); + if (opts->flags & FARF_CLEARDSCP) + printf(" cleardscp"); + if (opts->flags & FARF_DEFAULTCLASS) + printf(" default"); + if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0)) + print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d, + opts->lssc_m2, loc_lssc); + printf(" ) "); + + return (1); + } else + return (0); +} + /* * admission control using generalized service curve */ @@ -1201,6 +1377,23 @@ eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, opts->data.hfsc_opts.upperlimit.d; } break; + case ALTQT_FAIRQ: + pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags; + pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets; + pa->pq_u.fairq_opts.hogs_m1 = + eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw); + + if (opts->data.fairq_opts.linkshare.used) { + pa->pq_u.fairq_opts.lssc_m1 = + eval_bwspec(&opts->data.fairq_opts.linkshare.m1, + ref_bw); + pa->pq_u.fairq_opts.lssc_m2 = + eval_bwspec(&opts->data.fairq_opts.linkshare.m2, + ref_bw); + pa->pq_u.fairq_opts.lssc_d = + opts->data.fairq_opts.linkshare.d; + } + break; default: warnx("eval_queue_opts: unknown scheduler type %u", opts->qtype); @@ -1246,3 +1439,27 @@ print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, if (d != 0) printf(")"); } + +void +print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2, + const struct node_fairq_sc *sc) +{ + printf(" %s", scname); + + if (d != 0) { + printf("("); + if (sc != NULL && sc->m1.bw_percent > 0) + printf("%u%%", sc->m1.bw_percent); + else + printf("%s", rate2str((double)m1)); + printf(" %u", d); + } + + if (sc != NULL && sc->m2.bw_percent > 0) + printf(" %u%%", sc->m2.bw_percent); + else + printf(" %s", rate2str((double)m2)); + + if (d != 0) + printf(")"); +} diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 5c909e9..8a4e84e 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -150,12 +150,27 @@ struct node_hfsc_opts { int flags; }; +struct node_fairq_sc { + struct node_queue_bw m1; /* slope of 1st segment; bps */ + u_int d; /* x-projection of m1; msec */ + struct node_queue_bw m2; /* slope of 2nd segment; bps */ + u_int8_t used; +}; + +struct node_fairq_opts { + struct node_fairq_sc linkshare; + struct node_queue_bw hogs_bw; + u_int nbuckets; + int flags; +}; + struct node_queue_opt { int qtype; union { struct cbq_opts cbq_opts; struct priq_opts priq_opts; struct node_hfsc_opts hfsc_opts; + struct node_fairq_opts fairq_opts; } data; }; diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c index 95371e4..4087d71 100644 --- a/sbin/pfctl/pfctl_qstats.c +++ b/sbin/pfctl/pfctl_qstats.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "pfctl.h" #include "pfctl_parser.h" @@ -46,6 +47,7 @@ union class_stats { class_stats_t cbq_stats; struct priq_classstats priq_stats; struct hfsc_classstats hfsc_stats; + struct fairq_classstats fairq_stats; }; #define AVGN_MAX 8 @@ -77,6 +79,7 @@ void pfctl_print_altq_node(int, const struct pf_altq_node *, void print_cbqstats(struct queue_stats); void print_priqstats(struct queue_stats); void print_hfscstats(struct queue_stats); +void print_fairqstats(struct queue_stats); void pfctl_free_altq_node(struct pf_altq_node *); void pfctl_print_altq_nodestat(int, const struct pf_altq_node *); @@ -317,6 +320,9 @@ pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) case ALTQT_HFSC: print_hfscstats(a->qstats); break; + case ALTQT_FAIRQ: + print_fairqstats(a->qstats); + break; } } @@ -382,6 +388,26 @@ print_hfscstats(struct queue_stats cur) } void +print_fairqstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets, + (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes, + (unsigned long long)cur.data.fairq_stats.drop_cnt.packets, + (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", + cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void pfctl_free_altq_node(struct pf_altq_node *node) { while (node != NULL) { @@ -421,6 +447,10 @@ update_avg(struct pf_altq_node *a) b = qs->data.hfsc_stats.xmit_cnt.bytes; p = qs->data.hfsc_stats.xmit_cnt.packets; break; + case ALTQT_FAIRQ: + b = qs->data.fairq_stats.xmit_cnt.bytes; + p = qs->data.fairq_stats.xmit_cnt.packets; + break; default: b = 0; p = 0; -- cgit v1.1