mirror of
https://github.com/opnsense/src.git
synced 2026-06-10 17:22:46 -04:00
ipfw: apply dummynet aqm patch 0.2.1
Taken from: http://caia.swin.edu.au/freebsd/aqm/
This commit is contained in:
parent
643069f1b8
commit
f8a72da2e2
23 changed files with 5248 additions and 13 deletions
|
|
@ -1,4 +1,11 @@
|
|||
/*
|
||||
* Codel/FQ_Codel and PIE/FQ_PIE Code:
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Copyright (c) 2002-2003,2010 Luigi Rizzo
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
|
|
@ -15,6 +22,7 @@
|
|||
* dummynet support
|
||||
*/
|
||||
|
||||
#define NEW_AQM
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
/* XXX there are several sysctl leftover here */
|
||||
|
|
@ -22,6 +30,10 @@
|
|||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#ifdef NEW_AQM
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -56,8 +68,15 @@ static struct _s_x dummynet_params[] = {
|
|||
{ "sched_mask", TOK_SCHED_MASK },
|
||||
{ "flow_mask", TOK_FLOW_MASK },
|
||||
{ "droptail", TOK_DROPTAIL },
|
||||
{ "ecn", TOK_ECN },
|
||||
{ "red", TOK_RED },
|
||||
{ "gred", TOK_GRED },
|
||||
#ifdef NEW_AQM
|
||||
{ "codel", TOK_CODEL}, /* Codel AQM */
|
||||
{ "fq_codel", TOK_FQ_CODEL}, /* FQ-Codel */
|
||||
{ "pie", TOK_PIE}, /* PIE AQM */
|
||||
{ "fq_pie", TOK_FQ_PIE}, /* FQ-PIE */
|
||||
#endif
|
||||
{ "bw", TOK_BW },
|
||||
{ "bandwidth", TOK_BW },
|
||||
{ "delay", TOK_DELAY },
|
||||
|
|
@ -80,6 +99,32 @@ static struct _s_x dummynet_params[] = {
|
|||
{ NULL, 0 } /* terminator */
|
||||
};
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* AQM/extra sched parameters tokens*/
|
||||
static struct _s_x aqm_params[] = {
|
||||
{ "target", TOK_TARGET},
|
||||
{ "interval", TOK_INTERVAL},
|
||||
{ "limit", TOK_LIMIT},
|
||||
{ "flows", TOK_FLOWS},
|
||||
{ "quantum", TOK_QUANTUM},
|
||||
{ "ecn", TOK_ECN},
|
||||
{ "noecn", TOK_NO_ECN},
|
||||
{ "tupdate", TOK_TUPDATE},
|
||||
{ "max_burst", TOK_MAX_BURST},
|
||||
{ "max_ecnth", TOK_MAX_ECNTH},
|
||||
{ "alpha", TOK_ALPHA},
|
||||
{ "beta", TOK_BETA},
|
||||
{ "capdrop", TOK_CAPDROP},
|
||||
{ "nocapdrop", TOK_NO_CAPDROP},
|
||||
{ "onoff", TOK_ONOFF},
|
||||
{ "dre", TOK_DRE},
|
||||
{ "ts", TOK_TS},
|
||||
{ "derand", TOK_DERAND},
|
||||
{ "noderand", TOK_NO_DERAND},
|
||||
{ NULL, 0 } /* terminator */
|
||||
};
|
||||
#endif
|
||||
|
||||
#define O_NEXT(p, len) ((void *)((char *)p + len))
|
||||
|
||||
static void
|
||||
|
|
@ -101,6 +146,214 @@ o_next(struct dn_id **o, int len, int type)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
|
||||
/* Codel flags */
|
||||
enum {
|
||||
CODEL_ECN_ENABLED = 1
|
||||
};
|
||||
|
||||
/* PIE flags, from PIE kernel module */
|
||||
enum {
|
||||
PIE_ECN_ENABLED = 1,
|
||||
PIE_CAPDROP_ENABLED = 2,
|
||||
PIE_ON_OFF_MODE_ENABLED = 4,
|
||||
PIE_DEPRATEEST_ENABLED = 8,
|
||||
PIE_DERAND_ENABLED = 16
|
||||
};
|
||||
|
||||
#define PIE_FIX_POINT_BITS 13
|
||||
#define PIE_SCALE (1L<<PIE_FIX_POINT_BITS)
|
||||
|
||||
/* integer to time */
|
||||
void
|
||||
us_to_time(int t,char *strt)
|
||||
{
|
||||
if (t < 0)
|
||||
strt[0]='\0';
|
||||
else if ( t==0 )
|
||||
sprintf(strt,"%d", t);
|
||||
else if (t< 1000)
|
||||
sprintf(strt,"%dus", t);
|
||||
else if (t < 1000000)
|
||||
sprintf(strt,"%gms", (float) t / 1000);
|
||||
else
|
||||
sprintf(strt,"%gfs", (float) t / 1000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns -1 if s is not a valid time, otherwise, return time in us
|
||||
*/
|
||||
static long
|
||||
time_to_us(const char *s)
|
||||
{
|
||||
int i, dots = 0;
|
||||
int len = strlen(s);
|
||||
char strt[16]="", stru[16]="";
|
||||
|
||||
if (len>15)
|
||||
return -1;
|
||||
for (i = 0; i<len && (isdigit(s[i]) || s[i]=='.') ; i++)
|
||||
if (s[i]=='.') {
|
||||
if (dots)
|
||||
return -1;
|
||||
else
|
||||
dots++;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
return -1;
|
||||
strncpy(strt, s, i);
|
||||
if (i<len)
|
||||
strcpy(stru, s+i);
|
||||
else
|
||||
strcpy(stru, "ms");
|
||||
|
||||
if (!strcasecmp(stru, "us"))
|
||||
return atol(strt);
|
||||
if (!strcasecmp(stru, "ms"))
|
||||
return (strtod(strt, NULL) * 1000);
|
||||
if (!strcasecmp(stru, "s"))
|
||||
return (strtod(strt, NULL)*1000000);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Get AQM or scheduler extra parameters */
|
||||
void
|
||||
get_extra_parms(uint32_t nr, char *out, int subtype)
|
||||
{
|
||||
struct dn_extra_parms *ep;
|
||||
int ret;
|
||||
char strt1[15], strt2[15], strt3[15];
|
||||
u_int l;
|
||||
|
||||
/* prepare the request */
|
||||
l = sizeof(struct dn_extra_parms);
|
||||
ep = safe_calloc(1, l);
|
||||
memset(ep, 0, sizeof(*ep));
|
||||
*out = '\0';
|
||||
|
||||
oid_fill(&ep->oid, l, DN_CMD_GET, DN_API_VERSION);
|
||||
ep->oid.len = l;
|
||||
ep->oid.subtype = subtype;
|
||||
ep->nr = nr;
|
||||
|
||||
ret = do_cmd(-IP_DUMMYNET3, ep, (uintptr_t)&l);
|
||||
if (ret) {
|
||||
free(ep);
|
||||
errx(EX_DATAERR, "Error getting extra parameters\n");
|
||||
}
|
||||
|
||||
switch (subtype) {
|
||||
case DN_AQM_PARAMS:
|
||||
if( !strcasecmp(ep->name, "codel")) {
|
||||
us_to_time(ep->par[0], strt1);
|
||||
us_to_time(ep->par[1], strt2);
|
||||
l = sprintf(out, " AQM CoDel target %s interval %s",
|
||||
strt1, strt2);
|
||||
if (ep->par[2] & CODEL_ECN_ENABLED)
|
||||
l = sprintf(out + l, " ECN");
|
||||
else
|
||||
l += sprintf(out + l, " NoECN");
|
||||
} else if( !strcasecmp(ep->name, "pie")) {
|
||||
us_to_time(ep->par[0], strt1);
|
||||
us_to_time(ep->par[1], strt2);
|
||||
us_to_time(ep->par[2], strt3);
|
||||
l = sprintf(out, " AQM type PIE target %s tupdate %s alpha "
|
||||
"%g beta %g max_burst %s max_ecnth %.3g",
|
||||
strt1,
|
||||
strt2,
|
||||
ep->par[4] / (float) PIE_SCALE,
|
||||
ep->par[5] / (float) PIE_SCALE,
|
||||
strt3,
|
||||
ep->par[3] / (float) PIE_SCALE
|
||||
);
|
||||
|
||||
if (ep->par[6] & PIE_ECN_ENABLED)
|
||||
l += sprintf(out + l, " ECN");
|
||||
else
|
||||
l += sprintf(out + l, " NoECN");
|
||||
if (ep->par[6] & PIE_CAPDROP_ENABLED)
|
||||
l += sprintf(out + l, " CapDrop");
|
||||
else
|
||||
l += sprintf(out + l, " NoCapDrop");
|
||||
if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED)
|
||||
l += sprintf(out + l, " OnOff");
|
||||
if (ep->par[6] & PIE_DEPRATEEST_ENABLED)
|
||||
l += sprintf(out + l, " DRE");
|
||||
else
|
||||
l += sprintf(out + l, " TS");
|
||||
if (ep->par[6] & PIE_DERAND_ENABLED)
|
||||
l += sprintf(out + l, " Derand");
|
||||
else
|
||||
l += sprintf(out + l, " NoDerand");
|
||||
}
|
||||
break;
|
||||
|
||||
case DN_SCH_PARAMS:
|
||||
if (!strcasecmp(ep->name,"FQ_CODEL")) {
|
||||
us_to_time(ep->par[0], strt1);
|
||||
us_to_time(ep->par[1], strt2);
|
||||
l = sprintf(out," FQ_CODEL target %s interval %s"
|
||||
" quantum %jd limit %jd flows %jd",
|
||||
strt1, strt2,
|
||||
(intmax_t) ep->par[3],
|
||||
(intmax_t) ep->par[4],
|
||||
(intmax_t) ep->par[5]
|
||||
);
|
||||
if (ep->par[2] & CODEL_ECN_ENABLED)
|
||||
l += sprintf(out + l, " ECN");
|
||||
else
|
||||
l += sprintf(out + l, " NoECN");
|
||||
l += sprintf(out + l, "\n");
|
||||
} else if (!strcasecmp(ep->name,"FQ_PIE")) {
|
||||
us_to_time(ep->par[0], strt1);
|
||||
us_to_time(ep->par[1], strt2);
|
||||
us_to_time(ep->par[2], strt3);
|
||||
l = sprintf(out, " FQ_PIE target %s tupdate %s alpha "
|
||||
"%g beta %g max_burst %s max_ecnth %.3g"
|
||||
" quantum %jd limit %jd flows %jd",
|
||||
strt1,
|
||||
strt2,
|
||||
ep->par[4] / (float) PIE_SCALE,
|
||||
ep->par[5] / (float) PIE_SCALE,
|
||||
strt3,
|
||||
ep->par[3] / (float) PIE_SCALE,
|
||||
(intmax_t) ep->par[7],
|
||||
(intmax_t) ep->par[8],
|
||||
(intmax_t) ep->par[9]
|
||||
);
|
||||
|
||||
if (ep->par[6] & PIE_ECN_ENABLED)
|
||||
l += sprintf(out + l, " ECN");
|
||||
else
|
||||
l += sprintf(out + l, " NoECN");
|
||||
if (ep->par[6] & PIE_CAPDROP_ENABLED)
|
||||
l += sprintf(out + l, " CapDrop");
|
||||
else
|
||||
l += sprintf(out + l, " NoCapDrop");
|
||||
if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED)
|
||||
l += sprintf(out + l, " OnOff");
|
||||
if (ep->par[6] & PIE_DEPRATEEST_ENABLED)
|
||||
l += sprintf(out + l, " DRE");
|
||||
else
|
||||
l += sprintf(out + l, " TS");
|
||||
if (ep->par[6] & PIE_DERAND_ENABLED)
|
||||
l += sprintf(out + l, " Derand");
|
||||
else
|
||||
l += sprintf(out + l, " NoDerand");
|
||||
l += sprintf(out + l, "\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
free(ep);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
static int
|
||||
sort_q(void *arg, const void *pa, const void *pb)
|
||||
|
|
@ -224,7 +477,7 @@ print_flowset_parms(struct dn_fs *fs, char *prefix)
|
|||
int l;
|
||||
char qs[30];
|
||||
char plr[30];
|
||||
char red[90]; /* Display RED parameters */
|
||||
char red[200]; /* Display RED parameters */
|
||||
|
||||
l = fs->qsize;
|
||||
if (fs->flags & DN_QSIZE_BYTES) {
|
||||
|
|
@ -239,7 +492,7 @@ print_flowset_parms(struct dn_fs *fs, char *prefix)
|
|||
else
|
||||
plr[0] = '\0';
|
||||
|
||||
if (fs->flags & DN_IS_RED) /* RED parameters */
|
||||
if (fs->flags & DN_IS_RED) { /* RED parameters */
|
||||
sprintf(red,
|
||||
"\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
|
||||
(fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ',
|
||||
|
|
@ -247,7 +500,14 @@ print_flowset_parms(struct dn_fs *fs, char *prefix)
|
|||
fs->min_th,
|
||||
fs->max_th,
|
||||
1.0 * fs->max_p / (double)(1 << SCALE_RED));
|
||||
else
|
||||
if (fs->flags & DN_IS_ECN)
|
||||
strncat(red, " (ecn)", 6);
|
||||
#ifdef NEW_AQM
|
||||
/* get AQM parameters */
|
||||
} else if (fs->flags & DN_IS_AQM) {
|
||||
get_extra_parms(fs->fs_nr, red, DN_AQM_PARAMS);
|
||||
#endif
|
||||
} else
|
||||
sprintf(red, "droptail");
|
||||
|
||||
if (prefix[0]) {
|
||||
|
|
@ -337,6 +597,11 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
|
|||
printf(" sched %d type %s flags 0x%x %d buckets %d active\n",
|
||||
s->sched_nr,
|
||||
s->name, s->flags, s->buckets, s->oid.id);
|
||||
#ifdef NEW_AQM
|
||||
char parms[200];
|
||||
get_extra_parms(s->sched_nr, parms, DN_SCH_PARAMS);
|
||||
printf("%s",parms);
|
||||
#endif
|
||||
if (s->flags & DN_HAVE_MASK)
|
||||
print_mask(&s->sched_mask);
|
||||
}
|
||||
|
|
@ -737,6 +1002,242 @@ load_extra_delays(const char *filename, struct dn_profile *p,
|
|||
strncpy(p->name, profile_name, sizeof(p->name));
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
|
||||
/* Parse AQM/extra scheduler parameters */
|
||||
static int
|
||||
process_extra_parms(int *ac, char **av, struct dn_extra_parms *ep,
|
||||
uint16_t type)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* use kernel defaults */
|
||||
for (i=0; i<DN_MAX_EXTRA_PARM; i++)
|
||||
ep->par[i] = -1;
|
||||
|
||||
switch(type) {
|
||||
case TOK_CODEL:
|
||||
case TOK_FQ_CODEL:
|
||||
/* Codel
|
||||
* 0- target, 1- interval, 2- flags,
|
||||
* FQ_CODEL
|
||||
* 3- quantum, 4- limit, 5- flows
|
||||
*/
|
||||
if (type==TOK_CODEL)
|
||||
ep->par[2] = 0;
|
||||
else
|
||||
ep->par[2] = CODEL_ECN_ENABLED;
|
||||
|
||||
while (*ac > 0) {
|
||||
int tok = match_token(aqm_params, *av);
|
||||
(*ac)--; av++;
|
||||
switch(tok) {
|
||||
case TOK_TARGET:
|
||||
if (*ac <= 0 || time_to_us(av[0]) < 0)
|
||||
errx(EX_DATAERR, "target needs time\n");
|
||||
|
||||
ep->par[0] = time_to_us(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_INTERVAL:
|
||||
if (*ac <= 0 || time_to_us(av[0]) < 0)
|
||||
errx(EX_DATAERR, "interval needs time\n");
|
||||
|
||||
ep->par[1] = time_to_us(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_ECN:
|
||||
ep->par[2] = CODEL_ECN_ENABLED;
|
||||
break;
|
||||
case TOK_NO_ECN:
|
||||
ep->par[2] &= ~CODEL_ECN_ENABLED;
|
||||
break;
|
||||
/* Config fq_codel parameters */
|
||||
case TOK_QUANTUM:
|
||||
if (type != TOK_FQ_CODEL)
|
||||
errx(EX_DATAERR, "quantum is not for codel\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "quantum needs number\n");
|
||||
|
||||
ep->par[3]= atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_LIMIT:
|
||||
if (type != TOK_FQ_CODEL)
|
||||
errx(EX_DATAERR, "limit is not for codel, use queue instead\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "limit needs number\n");
|
||||
|
||||
ep->par[4] = atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_FLOWS:
|
||||
if (type != TOK_FQ_CODEL)
|
||||
errx(EX_DATAERR, "flows is not for codel\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "flows needs number\n");
|
||||
|
||||
ep->par[5] = atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("%s is Invalid parameter\n", av[-1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TOK_PIE:
|
||||
case TOK_FQ_PIE:
|
||||
/* PIE
|
||||
* 0- target , 1- tupdate, 2- max_burst,
|
||||
* 3- max_ecnth, 4- alpha,
|
||||
* 5- beta, 6- flags
|
||||
* FQ_CODEL
|
||||
* 7- quantum, 8- limit, 9- flows
|
||||
*/
|
||||
|
||||
if ( type == TOK_PIE)
|
||||
ep->par[6] = PIE_CAPDROP_ENABLED | PIE_DEPRATEEST_ENABLED
|
||||
| PIE_DERAND_ENABLED;
|
||||
else
|
||||
/* for FQ-PIE, use TS mode */
|
||||
ep->par[6] = PIE_CAPDROP_ENABLED | PIE_DERAND_ENABLED
|
||||
| PIE_ECN_ENABLED;
|
||||
|
||||
while (*ac > 0) {
|
||||
int tok = match_token(aqm_params, *av);
|
||||
(*ac)--; av++;
|
||||
switch(tok) {
|
||||
case TOK_TARGET:
|
||||
if (*ac <= 0 || time_to_us(av[0]) < 0)
|
||||
errx(EX_DATAERR, "target needs time\n");
|
||||
|
||||
ep->par[0] = time_to_us(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_TUPDATE:
|
||||
if (*ac <= 0 || time_to_us(av[0]) < 0)
|
||||
errx(EX_DATAERR, "tupdate needs time\n");
|
||||
|
||||
ep->par[1] = time_to_us(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_MAX_BURST:
|
||||
if (*ac <= 0 || time_to_us(av[0]) < 0)
|
||||
errx(EX_DATAERR, "max_burst needs time\n");
|
||||
|
||||
ep->par[2] = time_to_us(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_MAX_ECNTH:
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "max_ecnth needs number\n");
|
||||
|
||||
ep->par[3] = atof(av[0]) * PIE_SCALE;
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_ALPHA:
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "alpha needs number\n");
|
||||
|
||||
ep->par[4] = atof(av[0]) * PIE_SCALE;
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_BETA:
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "beta needs number\n");
|
||||
|
||||
ep->par[5] = atof(av[0]) * PIE_SCALE;
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_ECN:
|
||||
ep->par[6] |= PIE_ECN_ENABLED;
|
||||
break;
|
||||
case TOK_NO_ECN:
|
||||
ep->par[6] &= ~PIE_ECN_ENABLED;
|
||||
break;
|
||||
|
||||
case TOK_CAPDROP:
|
||||
ep->par[6] |= PIE_CAPDROP_ENABLED;
|
||||
break;
|
||||
case TOK_NO_CAPDROP:
|
||||
ep->par[6] &= ~PIE_CAPDROP_ENABLED;
|
||||
break;
|
||||
|
||||
case TOK_ONOFF:
|
||||
ep->par[6] |= PIE_ON_OFF_MODE_ENABLED;
|
||||
break;
|
||||
|
||||
case TOK_DRE:
|
||||
ep->par[6] |= PIE_DEPRATEEST_ENABLED;
|
||||
break;
|
||||
|
||||
case TOK_TS:
|
||||
ep->par[6] &= ~PIE_DEPRATEEST_ENABLED;
|
||||
break;
|
||||
|
||||
case TOK_DERAND:
|
||||
ep->par[6] |= PIE_DERAND_ENABLED;
|
||||
break;
|
||||
case TOK_NO_DERAND:
|
||||
ep->par[6] &= ~PIE_DERAND_ENABLED;
|
||||
break;
|
||||
|
||||
/* Config fq_pie parameters */
|
||||
case TOK_QUANTUM:
|
||||
if (type != TOK_FQ_PIE)
|
||||
errx(EX_DATAERR, "quantum is not for pie\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "quantum needs number\n");
|
||||
|
||||
ep->par[7]= atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_LIMIT:
|
||||
if (type != TOK_FQ_PIE)
|
||||
errx(EX_DATAERR, "limit is not for pie, use queue instead\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "limit needs number\n");
|
||||
|
||||
ep->par[8] = atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
case TOK_FLOWS:
|
||||
if (type != TOK_FQ_PIE)
|
||||
errx(EX_DATAERR, "flows is not for pie\n");
|
||||
if (*ac <= 0 || !is_valid_number(av[0]))
|
||||
errx(EX_DATAERR, "flows needs number\n");
|
||||
|
||||
ep->par[9] = atoi(av[0]);
|
||||
(*ac)--; av++;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
printf("%s is invalid parameter\n", av[-1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* configuration of pipes, schedulers, flowsets.
|
||||
* When we configure a new scheduler, an empty pipe is created, so:
|
||||
|
|
@ -768,6 +1269,12 @@ ipfw_config_pipe(int ac, char **av)
|
|||
struct dn_fs *fs = NULL;
|
||||
struct dn_profile *pf = NULL;
|
||||
struct ipfw_flow_id *mask = NULL;
|
||||
#ifdef NEW_AQM
|
||||
struct dn_extra_parms *aqm_extra;
|
||||
struct dn_extra_parms *sch_extra;
|
||||
int lmax_extra;
|
||||
#endif
|
||||
|
||||
int lmax;
|
||||
uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo;
|
||||
|
||||
|
|
@ -779,6 +1286,15 @@ ipfw_config_pipe(int ac, char **av)
|
|||
lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
|
||||
sizeof(struct dn_fs) + sizeof(struct dn_profile);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* Extra Params */
|
||||
lmax_extra = sizeof(struct dn_extra_parms);
|
||||
/* two lmax_extra because one for AQM params and another
|
||||
* sch params
|
||||
*/
|
||||
lmax += lmax_extra*2;
|
||||
#endif
|
||||
|
||||
av++; ac--;
|
||||
/* Pipe number */
|
||||
if (ac && isdigit(**av)) {
|
||||
|
|
@ -804,8 +1320,16 @@ ipfw_config_pipe(int ac, char **av)
|
|||
* The FIFO scheduler and link are derived from the
|
||||
* WF2Q+ one in the kernel.
|
||||
*/
|
||||
#ifdef NEW_AQM
|
||||
sch_extra = o_next(&buf, lmax_extra, DN_TEXT);
|
||||
sch_extra ->oid.subtype = 0; /* don't configure scheduler */
|
||||
#endif
|
||||
sch = o_next(&buf, sizeof(*sch), DN_SCH);
|
||||
p = o_next(&buf, sizeof(*p), DN_LINK);
|
||||
#ifdef NEW_AQM
|
||||
aqm_extra = o_next(&buf, lmax_extra, DN_TEXT);
|
||||
aqm_extra ->oid.subtype = 0; /* don't configure AQM */
|
||||
#endif
|
||||
fs = o_next(&buf, sizeof(*fs), DN_FS);
|
||||
|
||||
sch->sched_nr = i;
|
||||
|
|
@ -823,6 +1347,10 @@ ipfw_config_pipe(int ac, char **av)
|
|||
break;
|
||||
|
||||
case 2: /* "queue N config ... " */
|
||||
#ifdef NEW_AQM
|
||||
aqm_extra = o_next(&buf, lmax_extra, DN_TEXT);
|
||||
aqm_extra ->oid.subtype = 0;
|
||||
#endif
|
||||
fs = o_next(&buf, sizeof(*fs), DN_FS);
|
||||
fs->fs_nr = i;
|
||||
mask = &fs->flow_mask;
|
||||
|
|
@ -831,7 +1359,15 @@ ipfw_config_pipe(int ac, char **av)
|
|||
break;
|
||||
|
||||
case 3: /* "sched N config ..." */
|
||||
#ifdef NEW_AQM
|
||||
sch_extra = o_next(&buf, lmax_extra, DN_TEXT);
|
||||
sch_extra ->oid.subtype = 0;
|
||||
#endif
|
||||
sch = o_next(&buf, sizeof(*sch), DN_SCH);
|
||||
#ifdef NEW_AQM
|
||||
aqm_extra = o_next(&buf, lmax_extra, DN_TEXT);
|
||||
aqm_extra ->oid.subtype = 0;
|
||||
#endif
|
||||
fs = o_next(&buf, sizeof(*fs), DN_FS);
|
||||
sch->sched_nr = i;
|
||||
mask = &sch->sched_mask;
|
||||
|
|
@ -1018,7 +1554,31 @@ ipfw_config_pipe(int ac, char **av)
|
|||
} /* end while, config masks */
|
||||
end_mask:
|
||||
break;
|
||||
#ifdef NEW_AQM
|
||||
case TOK_CODEL:
|
||||
case TOK_PIE:
|
||||
NEED(fs, "codel/pie is only for flowsets");
|
||||
|
||||
fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
|
||||
fs->flags |= DN_IS_AQM;
|
||||
|
||||
strcpy(aqm_extra->name,av[-1]);
|
||||
aqm_extra->oid.subtype = DN_AQM_PARAMS;
|
||||
|
||||
process_extra_parms(&ac, av, aqm_extra, tok);
|
||||
break;
|
||||
|
||||
case TOK_FQ_CODEL:
|
||||
case TOK_FQ_PIE:
|
||||
if (!strcmp(av[-1],"type"))
|
||||
errx(EX_DATAERR, "use type before fq_codel/fq_pie");
|
||||
|
||||
NEED(sch, "fq_codel/fq_pie is only for schd");
|
||||
strcpy(sch_extra->name,av[-1]);
|
||||
sch_extra->oid.subtype = DN_SCH_PARAMS;
|
||||
process_extra_parms(&ac, av, sch_extra, tok);
|
||||
break;
|
||||
#endif
|
||||
case TOK_RED:
|
||||
case TOK_GRED:
|
||||
NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
|
||||
|
|
@ -1046,13 +1606,17 @@ end_mask:
|
|||
}
|
||||
if ((end = strsep(&av[0], "/"))) {
|
||||
double max_p = strtod(end, NULL);
|
||||
if (max_p > 1 || max_p <= 0)
|
||||
errx(EX_DATAERR, "0 < max_p <= 1");
|
||||
if (max_p > 1 || max_p < 0)
|
||||
errx(EX_DATAERR, "0 <= max_p <= 1");
|
||||
fs->max_p = (int)(max_p * (1 << SCALE_RED));
|
||||
}
|
||||
ac--; av++;
|
||||
break;
|
||||
|
||||
case TOK_ECN:
|
||||
fs->flags |= DN_IS_ECN;
|
||||
break;
|
||||
|
||||
case TOK_DROPTAIL:
|
||||
NEED(fs, "droptail is only for flowsets");
|
||||
fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
|
||||
|
|
@ -1081,7 +1645,20 @@ end_mask:
|
|||
errx(1, "type %s too long\n", av[0]);
|
||||
strcpy(sch->name, av[0]);
|
||||
sch->oid.subtype = 0; /* use string */
|
||||
ac--; av++;
|
||||
#ifdef NEW_AQM
|
||||
/* if fq_codel is selected, consider all tokens after it
|
||||
* as parameters
|
||||
*/
|
||||
if (!strcasecmp(av[0],"fq_codel") || !strcasecmp(av[0],"fq_pie")){
|
||||
strcpy(sch_extra->name,av[0]);
|
||||
sch_extra->oid.subtype = DN_SCH_PARAMS;
|
||||
process_extra_parms(&ac, av, sch_extra, tok);
|
||||
} else {
|
||||
ac--;av++;
|
||||
}
|
||||
#else
|
||||
ac--;av++;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1175,13 +1752,28 @@ end_mask:
|
|||
errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
if ((fs->flags & DN_IS_ECN) && !((fs->flags & DN_IS_RED)||
|
||||
(fs->flags & DN_IS_AQM)))
|
||||
errx(EX_USAGE, "ECN can be used with red/gred/"
|
||||
"codel/fq_codel only!");
|
||||
#else
|
||||
if ((fs->flags & DN_IS_ECN) && !(fs->flags & DN_IS_RED))
|
||||
errx(EX_USAGE, "enable red/gred for ECN");
|
||||
|
||||
#endif
|
||||
|
||||
if (fs->flags & DN_IS_RED) {
|
||||
size_t len;
|
||||
int lookup_depth, avg_pkt_size;
|
||||
|
||||
if (fs->min_th >= fs->max_th)
|
||||
if (!(fs->flags & DN_IS_ECN) && (fs->min_th >= fs->max_th))
|
||||
errx(EX_DATAERR, "min_th %d must be < than max_th %d",
|
||||
fs->min_th, fs->max_th);
|
||||
else if ((fs->flags & DN_IS_ECN) && (fs->min_th > fs->max_th))
|
||||
errx(EX_DATAERR, "min_th %d must be =< than max_th %d",
|
||||
fs->min_th, fs->max_th);
|
||||
|
||||
if (fs->max_th == 0)
|
||||
errx(EX_DATAERR, "max_th must be > 0");
|
||||
|
||||
|
|
|
|||
|
|
@ -165,8 +165,34 @@ enum tokens {
|
|||
TOK_BURST,
|
||||
TOK_RED,
|
||||
TOK_GRED,
|
||||
TOK_ECN,
|
||||
TOK_DROPTAIL,
|
||||
TOK_PROTO,
|
||||
#ifdef NEW_AQM
|
||||
/* AQM tokens*/
|
||||
TOK_NO_ECN,
|
||||
TOK_CODEL,
|
||||
TOK_FQ_CODEL,
|
||||
TOK_TARGET,
|
||||
TOK_INTERVAL,
|
||||
TOK_FLOWS,
|
||||
TOK_QUANTUM,
|
||||
|
||||
TOK_PIE,
|
||||
TOK_FQ_PIE,
|
||||
TOK_TUPDATE,
|
||||
TOK_MAX_BURST,
|
||||
TOK_MAX_ECNTH,
|
||||
TOK_ALPHA,
|
||||
TOK_BETA,
|
||||
TOK_CAPDROP,
|
||||
TOK_NO_CAPDROP,
|
||||
TOK_ONOFF,
|
||||
TOK_DRE,
|
||||
TOK_TS,
|
||||
TOK_DERAND,
|
||||
TOK_NO_DERAND,
|
||||
#endif
|
||||
/* dummynet tokens */
|
||||
TOK_WEIGHT,
|
||||
TOK_LMAX,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@
|
|||
KMOD= dummynet
|
||||
SRCS= ip_dummynet.c
|
||||
SRCS+= ip_dn_glue.c ip_dn_io.c
|
||||
SRCS+= dn_aqm_codel.c dn_aqm_pie.c
|
||||
SRCS+= dn_heap.c dn_sched_fifo.c dn_sched_qfq.c dn_sched_rr.c dn_sched_wf2q.c
|
||||
SRCS+= dn_sched_prio.c
|
||||
SRCS+= dn_sched_prio.c dn_sched_fq_codel.c dn_sched_fq_pie.c
|
||||
SRCS+= opt_inet6.h
|
||||
|
||||
.if !defined(KERNBUILDDIR)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#ifndef _IP_DUMMYNET_H
|
||||
#define _IP_DUMMYNET_H
|
||||
|
||||
#define NEW_AQM
|
||||
/*
|
||||
* Definition of the kernel-userland API for dummynet.
|
||||
*
|
||||
|
|
@ -85,7 +85,13 @@ enum {
|
|||
/* special commands for emulation of sysctl variables */
|
||||
DN_SYSCTL_GET,
|
||||
DN_SYSCTL_SET,
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* subtypes used for setting/getting extra parameters.
|
||||
* these subtypes used with IP_DUMMYNET3 command (get)
|
||||
* and DN_TEXT (set). */
|
||||
DN_AQM_PARAMS, /* AQM extra params */
|
||||
DN_SCH_PARAMS, /* scheduler extra params */
|
||||
#endif
|
||||
DN_LAST,
|
||||
};
|
||||
|
||||
|
|
@ -104,6 +110,10 @@ enum { /* user flags */
|
|||
DN_HAS_PROFILE = 0x0010, /* a link has a profile */
|
||||
DN_IS_RED = 0x0020,
|
||||
DN_IS_GENTLE_RED= 0x0040,
|
||||
DN_IS_ECN = 0x0080,
|
||||
#ifdef NEW_AQM
|
||||
DN_IS_AQM = 0x0100, /* AQMs: e.g Codel & PIE */
|
||||
#endif
|
||||
DN_PIPE_CMD = 0x1000, /* pipe config... */
|
||||
};
|
||||
|
||||
|
|
@ -209,7 +219,19 @@ struct dn_profile {
|
|||
int samples[ED_MAX_SAMPLES_NO]; /* may be shorter */
|
||||
};
|
||||
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* Extra parameters for AQM and scheduler.
|
||||
* This struct is used to pass and retrieve parameters (configurations)
|
||||
* to/from AQM and Scheduler.
|
||||
*/
|
||||
struct dn_extra_parms {
|
||||
struct dn_id oid;
|
||||
char name[16];
|
||||
uint32_t nr;
|
||||
#define DN_MAX_EXTRA_PARM 10
|
||||
int64_t par[DN_MAX_EXTRA_PARM];
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overall structure of dummynet
|
||||
|
|
|
|||
165
sys/netpfil/ipfw/dn_aqm.h
Normal file
165
sys/netpfil/ipfw/dn_aqm.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*-
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* API for writting an Active Queue Management algorithm for Dummynet
|
||||
*/
|
||||
|
||||
#ifndef _IP_DN_AQM_H
|
||||
#define _IP_DN_AQM_H
|
||||
|
||||
|
||||
/* NOW is the current time in millisecond*/
|
||||
#define NOW ((dn_cfg.curr_time * tick) / 1000)
|
||||
|
||||
#define AQM_UNOW (dn_cfg.curr_time * tick)
|
||||
#define AQM_TIME_1US ((aqm_time_t)(1))
|
||||
#define AQM_TIME_1MS ((aqm_time_t)(1000))
|
||||
#define AQM_TIME_1S ((aqm_time_t)(AQM_TIME_1MS * 1000))
|
||||
|
||||
/* aqm time allows to store up to 4294 seconds */
|
||||
typedef uint32_t aqm_time_t;
|
||||
typedef int32_t aqm_stime_t;
|
||||
|
||||
#define DN_AQM_MTAG_TS 55345
|
||||
|
||||
/* Macro for variable bounding */
|
||||
#define BOUND_VAR(x,l,h) (x < l? l : x > h? h : x)
|
||||
|
||||
/* sysctl variable to count number of droped packets */
|
||||
extern unsigned long io_pkt_drop;
|
||||
|
||||
/*
|
||||
* Structure for holding data and function pointers that together represent a
|
||||
* AQM algorithm.
|
||||
*/
|
||||
struct dn_aqm {
|
||||
#define DN_AQM_NAME_MAX 50
|
||||
char name[DN_AQM_NAME_MAX]; /* name of AQM algorithm */
|
||||
uint32_t type; /* AQM type number */
|
||||
|
||||
/* Methods implemented by AQM algorithm:
|
||||
*
|
||||
* enqueue enqueue packet 'm' on queue 'q'.
|
||||
* Return 0 on success, 1 on drop.
|
||||
*
|
||||
* dequeue dequeue a packet from queue 'q'.
|
||||
* Return a packet, NULL if no packet available.
|
||||
*
|
||||
* config configure AQM algorithm
|
||||
* If required, this function should allocate space to store
|
||||
* the configurations and set 'fs->aqmcfg' to point to this space.
|
||||
* 'dn_extra_parms' includes array of parameters send
|
||||
* from ipfw userland command.
|
||||
* Return 0 on success, non-zero otherwise.
|
||||
*
|
||||
* deconfig deconfigure AQM algorithm.
|
||||
* The allocated configuration memory space should be freed here.
|
||||
* Return 0 on success, non-zero otherwise.
|
||||
*
|
||||
* init initialise AQM status variables of queue 'q'
|
||||
* This function is used to allocate space and init AQM status for a
|
||||
* queue and q->aqm_status to point to this space.
|
||||
* Return 0 on success, non-zero otherwise.
|
||||
*
|
||||
* cleanup cleanup AQM status variables of queue 'q'
|
||||
* The allocated memory space for AQM status should be freed here.
|
||||
* Return 0 on success, non-zero otherwise.
|
||||
*
|
||||
* getconfig retrieve AQM configurations
|
||||
* This function is used to return AQM parameters to userland
|
||||
* command. The function should fill 'dn_extra_parms' struct with
|
||||
* the AQM configurations using 'par' array.
|
||||
*
|
||||
*/
|
||||
|
||||
int (*enqueue)(struct dn_queue *, struct mbuf *);
|
||||
struct mbuf * (*dequeue)(struct dn_queue *);
|
||||
int (*config)(struct dn_fsk *, struct dn_extra_parms *ep, int);
|
||||
int (*deconfig)(struct dn_fsk *);
|
||||
int (*init)(struct dn_queue *);
|
||||
int (*cleanup)(struct dn_queue *);
|
||||
int (*getconfig)(struct dn_fsk *, struct dn_extra_parms *);
|
||||
|
||||
int ref_count; /*Number of queues instances in the system */
|
||||
int cfg_ref_count; /*Number of AQM instances in the system */
|
||||
SLIST_ENTRY (dn_aqm) next; /* Next AQM in the list */
|
||||
};
|
||||
|
||||
/* Helper function to update queue and scheduler statistics.
|
||||
* negative len + drop -> drop
|
||||
* negative len -> dequeue
|
||||
* positive len -> enqueue
|
||||
* positive len + drop -> drop during enqueue
|
||||
*/
|
||||
__inline static void
|
||||
update_stats(struct dn_queue *q, int len, int drop)
|
||||
{
|
||||
int inc = 0;
|
||||
struct dn_flow *sni;
|
||||
struct dn_flow *qni;
|
||||
|
||||
sni = &q->_si->ni;
|
||||
qni = &q->ni;
|
||||
|
||||
if (len < 0)
|
||||
inc = -1;
|
||||
else if(len > 0)
|
||||
inc = 1;
|
||||
|
||||
if (drop) {
|
||||
qni->drops++;
|
||||
sni->drops++;
|
||||
io_pkt_drop++;
|
||||
} else {
|
||||
/*update queue stats */
|
||||
qni->length += inc;
|
||||
qni->len_bytes += len;
|
||||
|
||||
/*update scheduler instance stats */
|
||||
sni->length += inc;
|
||||
sni->len_bytes += len;
|
||||
}
|
||||
/* tot_pkts is updated in dn_enqueue function */
|
||||
}
|
||||
|
||||
|
||||
/* kernel module related function */
|
||||
int
|
||||
dn_aqm_modevent(module_t mod, int cmd, void *arg);
|
||||
|
||||
#define DECLARE_DNAQM_MODULE(name, dnaqm) \
|
||||
static moduledata_t name##_mod = { \
|
||||
#name, dn_aqm_modevent, dnaqm \
|
||||
}; \
|
||||
DECLARE_MODULE(name, name##_mod, \
|
||||
SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); \
|
||||
MODULE_DEPEND(name, dummynet, 3, 3, 3)
|
||||
|
||||
#endif
|
||||
443
sys/netpfil/ipfw/dn_aqm_codel.c
Normal file
443
sys/netpfil/ipfw/dn_aqm_codel.c
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* Codel - The Controlled-Delay Active Queue Management algorithm.
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include "opt_inet6.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
|
||||
#include <net/netisr.h>
|
||||
#include <net/vnet.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h> /* ip_len, ip_off */
|
||||
#include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <netinet/ip_dummynet.h>
|
||||
#include <netinet/if_ether.h> /* various ether_* routines */
|
||||
#include <netinet/ip6.h> /* for ip6_input, ip6_output prototypes */
|
||||
#include <netinet6/ip6_var.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#include <netpfil/ipfw/dn_aqm_codel.h>
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
|
||||
#define DN_AQM_CODEL 1
|
||||
|
||||
static struct dn_aqm codel_desc;
|
||||
|
||||
/* default codel parameters */
|
||||
struct dn_aqm_codel_parms codel_sysctl = {5000 * AQM_TIME_1US,
|
||||
100000 * AQM_TIME_1US, 0};
|
||||
|
||||
static int
|
||||
codel_sysctl_interval_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
value = codel_sysctl.interval;
|
||||
value /= AQM_TIME_1US;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > 100 * AQM_TIME_1S)
|
||||
return (EINVAL);
|
||||
codel_sysctl.interval = value * AQM_TIME_1US ;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
codel_sysctl_target_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
value = codel_sysctl.target;
|
||||
value /= AQM_TIME_1US;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
D("%ld", value);
|
||||
if (value < 1 || value > 5 * AQM_TIME_1S)
|
||||
return (EINVAL);
|
||||
codel_sysctl.target = value * AQM_TIME_1US ;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* defining Codel sysctl variables */
|
||||
SYSBEGIN(f4)
|
||||
|
||||
SYSCTL_DECL(_net_inet);
|
||||
SYSCTL_DECL(_net_inet_ip);
|
||||
SYSCTL_DECL(_net_inet_ip_dummynet);
|
||||
static SYSCTL_NODE(_net_inet_ip_dummynet, OID_AUTO,
|
||||
codel, CTLFLAG_RW, 0, "CODEL");
|
||||
|
||||
#ifdef SYSCTL_NODE
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_codel, OID_AUTO, target,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,codel_sysctl_target_handler, "L",
|
||||
"CoDel target in microsecond");
|
||||
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_codel, OID_AUTO, interval,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0, codel_sysctl_interval_handler, "L",
|
||||
"CoDel interval in microsecond");
|
||||
#endif
|
||||
|
||||
/* This function computes codel_interval/sqrt(count)
|
||||
* Newton's method of approximation is used to compute 1/sqrt(count).
|
||||
* http://betterexplained.com/articles/
|
||||
* understanding-quakes-fast-inverse-square-root/
|
||||
*/
|
||||
aqm_time_t
|
||||
control_law(struct codel_status *cst, struct dn_aqm_codel_parms *cprms,
|
||||
aqm_time_t t)
|
||||
{
|
||||
uint32_t count;
|
||||
uint64_t temp;
|
||||
count = cst->count;
|
||||
|
||||
/* we don't calculate isqrt(1) to get more accurate result*/
|
||||
if (count == 1) {
|
||||
/* prepare isqrt (old guess) for the next iteration i.e. 1/sqrt(2)*/
|
||||
cst->isqrt = (1UL<< FIX_POINT_BITS) * 7/10;
|
||||
/* return time + isqrt(1)*interval */
|
||||
return t + cprms->interval;
|
||||
}
|
||||
|
||||
/* newguess = g(1.5 - 0.5*c*g^2)
|
||||
* Multiplying both sides by 2 to make all the constants intergers
|
||||
* newguess * 2 = g(3 - c*g^2) g=old guess, c=count
|
||||
* So, newguess = newguess /2
|
||||
* Fixed point operations are used here.
|
||||
*/
|
||||
|
||||
/* Calculate g^2 */
|
||||
temp = (uint32_t) cst->isqrt * cst->isqrt;
|
||||
/* Calculate (3 - c*g^2) i.e. (3 - c * temp) */
|
||||
temp = (3ULL<< (FIX_POINT_BITS*2)) - (count * temp);
|
||||
|
||||
/*
|
||||
* Divide by 2 because we multiplied the original equation by two
|
||||
* Also, we shift the result by 8 bits to prevent overflow.
|
||||
* */
|
||||
temp >>= (1 + 8);
|
||||
|
||||
/* Now, temp = (1.5 - 0.5*c*g^2)
|
||||
* Calculate g (1.5 - 0.5*c*g^2) i.e. g * temp
|
||||
*/
|
||||
temp = (cst->isqrt * temp) >> (FIX_POINT_BITS + FIX_POINT_BITS - 8);
|
||||
cst->isqrt = temp;
|
||||
|
||||
/* calculate codel_interval/sqrt(count) */
|
||||
return t + ((cprms->interval * temp) >> FIX_POINT_BITS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a packet from the head of queue 'q'
|
||||
* Return a packet or NULL if the queue is empty.
|
||||
* Also extract packet's timestamp from mtag.
|
||||
*/
|
||||
struct mbuf *
|
||||
codel_extract_head(struct dn_queue *q, aqm_time_t *pkt_ts)
|
||||
{
|
||||
struct m_tag *mtag;
|
||||
struct mbuf *m = q->mq.head;
|
||||
|
||||
if (m == NULL)
|
||||
return m;
|
||||
q->mq.head = m->m_nextpkt;
|
||||
|
||||
/* Update stats */
|
||||
update_stats(q, -m->m_pkthdr.len, 0);
|
||||
|
||||
if (q->ni.length == 0) /* queue is now idle */
|
||||
q->q_time = dn_cfg.curr_time;
|
||||
|
||||
/* extract packet TS*/
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL) {
|
||||
D("Codel timestamp mtag not found!");
|
||||
*pkt_ts = 0;
|
||||
} else {
|
||||
*pkt_ts = *(aqm_time_t *)(mtag + 1);
|
||||
m_tag_delete(m,mtag);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue a packet 'm' in queue 'q'
|
||||
*/
|
||||
static int
|
||||
aqm_codel_enqueue(struct dn_queue *q, struct mbuf *m)
|
||||
{
|
||||
struct dn_fs *f;
|
||||
uint64_t len;
|
||||
struct codel_status *cst; /*codel status variables */
|
||||
struct m_tag *mtag;
|
||||
|
||||
f = &(q->fs->fs);
|
||||
len = m->m_pkthdr.len;
|
||||
cst = q->aqm_status;
|
||||
if(!cst) {
|
||||
D("Codel queue is not initialized\n");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Finding maximum packet size */
|
||||
// XXX we can get MTU from driver instead
|
||||
if (len > cst->maxpkt_size)
|
||||
cst->maxpkt_size = len;
|
||||
|
||||
/* check for queue size and drop the tail if exceed queue limit*/
|
||||
if (f->flags & DN_QSIZE_BYTES) {
|
||||
if ( q->ni.len_bytes > f->qsize)
|
||||
goto drop;
|
||||
}
|
||||
else {
|
||||
if ( q->ni.length >= f->qsize)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Add timestamp as mtag */
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL)
|
||||
mtag = m_tag_alloc(MTAG_ABI_COMPAT, DN_AQM_MTAG_TS,
|
||||
sizeof(aqm_time_t), M_NOWAIT);
|
||||
if (mtag == NULL) {
|
||||
m_freem(m);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
*(aqm_time_t *)(mtag + 1) = AQM_UNOW;
|
||||
m_tag_prepend(m, mtag);
|
||||
|
||||
mq_append(&q->mq, m);
|
||||
update_stats(q, len, 0);
|
||||
return (0);
|
||||
|
||||
drop:
|
||||
update_stats(q, 0, 1);
|
||||
FREE_PKT(m);
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/* Dequeue a pcaket from queue q */
|
||||
static struct mbuf *
|
||||
aqm_codel_dequeue(struct dn_queue *q)
|
||||
{
|
||||
return codel_dequeue(q);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize Codel for queue 'q'
|
||||
* First allocate memory for codel status.
|
||||
*/
|
||||
static int
|
||||
aqm_codel_init(struct dn_queue *q)
|
||||
{
|
||||
struct codel_status *cst;
|
||||
|
||||
if (!q->fs->aqmcfg) {
|
||||
D("Codel is not configure!d");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
q->aqm_status = malloc(sizeof(struct codel_status),
|
||||
M_DUMMYNET, M_NOWAIT | M_ZERO);
|
||||
if (q->aqm_status == NULL) {
|
||||
D("Cannot allocate AQM_codel private data");
|
||||
return ENOMEM ;
|
||||
}
|
||||
|
||||
/* init codel status variables */
|
||||
cst = q->aqm_status;
|
||||
cst->dropping=0;
|
||||
cst->first_above_time=0;
|
||||
cst->drop_next_time=0;
|
||||
cst->count=0;
|
||||
cst->maxpkt_size = 500;
|
||||
|
||||
/* increase reference counters */
|
||||
codel_desc.ref_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up Codel status for queue 'q'
|
||||
* Destroy memory allocated for codel status.
|
||||
*/
|
||||
static int
|
||||
aqm_codel_cleanup(struct dn_queue *q)
|
||||
{
|
||||
|
||||
if (q && q->aqm_status) {
|
||||
free(q->aqm_status, M_DUMMYNET);
|
||||
q->aqm_status = NULL;
|
||||
/* decrease reference counters */
|
||||
codel_desc.ref_count--;
|
||||
}
|
||||
else
|
||||
D("Codel already cleaned up");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config codel parameters
|
||||
* also allocate memory for codel configurations
|
||||
*/
|
||||
static int
|
||||
aqm_codel_config(struct dn_fsk* fs, struct dn_extra_parms *ep, int len)
|
||||
{
|
||||
struct dn_aqm_codel_parms *ccfg;
|
||||
|
||||
int l = sizeof(struct dn_extra_parms);
|
||||
if (len < l) {
|
||||
D("invalid sched parms length got %d need %d", len, l);
|
||||
return EINVAL;
|
||||
}
|
||||
/* we free the old cfg because maybe the original allocation
|
||||
* not the same size as the new one (different AQM type).
|
||||
*/
|
||||
if (fs->aqmcfg) {
|
||||
free(fs->aqmcfg, M_DUMMYNET);
|
||||
fs->aqmcfg = NULL;
|
||||
}
|
||||
|
||||
fs->aqmcfg = malloc(sizeof(struct dn_aqm_codel_parms),
|
||||
M_DUMMYNET, M_NOWAIT | M_ZERO);
|
||||
if (fs->aqmcfg== NULL) {
|
||||
D("cannot allocate AQM_codel configuration parameters");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* configure codel parameters */
|
||||
ccfg = fs->aqmcfg;
|
||||
|
||||
if (ep->par[0] < 0)
|
||||
ccfg->target = codel_sysctl.target;
|
||||
else
|
||||
ccfg->target = ep->par[0] * AQM_TIME_1US;
|
||||
|
||||
if (ep->par[1] < 0)
|
||||
ccfg->interval = codel_sysctl.interval;
|
||||
else
|
||||
ccfg->interval = ep->par[1] * AQM_TIME_1US;
|
||||
|
||||
if (ep->par[2] < 0)
|
||||
ccfg->flags = 0;
|
||||
else
|
||||
ccfg->flags = ep->par[2];
|
||||
|
||||
/* bound codel configurations */
|
||||
ccfg->target = BOUND_VAR(ccfg->target,1, 5 * AQM_TIME_1S);
|
||||
ccfg->interval = BOUND_VAR(ccfg->interval,1, 5 * AQM_TIME_1S);
|
||||
/* increase config reference counter */
|
||||
codel_desc.cfg_ref_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deconfigure Codel and free memory allocation
|
||||
*/
|
||||
static int
|
||||
aqm_codel_deconfig(struct dn_fsk* fs)
|
||||
{
|
||||
|
||||
if (fs && fs->aqmcfg) {
|
||||
free(fs->aqmcfg, M_DUMMYNET);
|
||||
fs->aqmcfg = NULL;
|
||||
fs->aqmfp = NULL;
|
||||
/* decrease config reference counter */
|
||||
codel_desc.cfg_ref_count--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve Codel configuration parameters.
|
||||
*/
|
||||
static int
|
||||
aqm_codel_getconfig(struct dn_fsk *fs, struct dn_extra_parms * ep)
|
||||
{
|
||||
struct dn_aqm_codel_parms *ccfg;
|
||||
|
||||
if (fs->aqmcfg) {
|
||||
strcpy(ep->name, codel_desc.name);
|
||||
ccfg = fs->aqmcfg;
|
||||
ep->par[0] = ccfg->target / AQM_TIME_1US;
|
||||
ep->par[1] = ccfg->interval / AQM_TIME_1US;
|
||||
ep->par[2] = ccfg->flags;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dn_aqm codel_desc = {
|
||||
_SI( .type = ) DN_AQM_CODEL,
|
||||
_SI( .name = ) "CODEL",
|
||||
_SI( .enqueue = ) aqm_codel_enqueue,
|
||||
_SI( .dequeue = ) aqm_codel_dequeue,
|
||||
_SI( .config = ) aqm_codel_config,
|
||||
_SI( .getconfig = ) aqm_codel_getconfig,
|
||||
_SI( .deconfig = ) aqm_codel_deconfig,
|
||||
_SI( .init = ) aqm_codel_init,
|
||||
_SI( .cleanup = ) aqm_codel_cleanup,
|
||||
};
|
||||
|
||||
DECLARE_DNAQM_MODULE(dn_aqm_codel, &codel_desc);
|
||||
|
||||
|
||||
#endif
|
||||
220
sys/netpfil/ipfw/dn_aqm_codel.h
Normal file
220
sys/netpfil/ipfw/dn_aqm_codel.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Codel - The Controlled-Delay Active Queue Management algorithm.
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Copyright (C) 2011-2014 Kathleen Nichols <nichols@pollere.com>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* o The names of the authors may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* Alternatively, provided that this notice is retained in full, this
|
||||
* software may be distributed under the terms of the GNU General Public
|
||||
* License ("GPL") version 2, in which case the provisions of the GPL
|
||||
* apply INSTEAD OF those given above.
|
||||
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _IP_DN_AQM_CODEL_H
|
||||
#define _IP_DN_AQM_CODEL_H
|
||||
|
||||
|
||||
// XXX How to choose MTAG?
|
||||
#define FIX_POINT_BITS 16
|
||||
|
||||
enum {
|
||||
CODEL_ECN_ENABLED = 1
|
||||
};
|
||||
|
||||
/* Codel parameters */
|
||||
struct dn_aqm_codel_parms {
|
||||
aqm_time_t target;
|
||||
aqm_time_t interval;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* codel status variables */
|
||||
struct codel_status {
|
||||
uint32_t count; /* number of dropped pkts since entering drop state */
|
||||
uint16_t dropping; /* dropping state */
|
||||
aqm_time_t drop_next_time; /* time for next drop */
|
||||
aqm_time_t first_above_time; /* time for first ts over target we observed */
|
||||
uint16_t isqrt; /* last isqrt for control low */
|
||||
uint16_t maxpkt_size; /* max packet size seen so far */
|
||||
};
|
||||
|
||||
struct mbuf *codel_extract_head(struct dn_queue *, aqm_time_t *);
|
||||
aqm_time_t control_law(struct codel_status *,
|
||||
struct dn_aqm_codel_parms *, aqm_time_t );
|
||||
|
||||
__inline static struct mbuf *
|
||||
codel_dodequeue(struct dn_queue *q, aqm_time_t now, uint16_t *ok_to_drop)
|
||||
{
|
||||
struct mbuf * m;
|
||||
struct dn_aqm_codel_parms *cprms;
|
||||
struct codel_status *cst;
|
||||
aqm_time_t pkt_ts, sojourn_time;
|
||||
|
||||
*ok_to_drop = 0;
|
||||
m = codel_extract_head(q, &pkt_ts);
|
||||
|
||||
cst = q->aqm_status;
|
||||
|
||||
if (m == NULL) {
|
||||
/* queue is empty - we can't be above target */
|
||||
cst->first_above_time= 0;
|
||||
return m;
|
||||
}
|
||||
|
||||
cprms = q->fs->aqmcfg;
|
||||
|
||||
/* To span a large range of bandwidths, CoDel runs two
|
||||
* different AQMs in parallel. One is sojourn-time-based
|
||||
* and takes effect when the time to send an MTU-sized
|
||||
* packet is less than target. The 1st term of the "if"
|
||||
* below does this. The other is backlog-based and takes
|
||||
* effect when the time to send an MTU-sized packet is >=
|
||||
* target. The goal here is to keep the output link
|
||||
* utilization high by never allowing the queue to get
|
||||
* smaller than the amount that arrives in a typical
|
||||
* interarrival time (MTU-sized packets arriving spaced
|
||||
* by the amount of time it takes to send such a packet on
|
||||
* the bottleneck). The 2nd term of the "if" does this.
|
||||
*/
|
||||
sojourn_time = now - pkt_ts;
|
||||
if (sojourn_time < cprms->target || q->ni.len_bytes <= cst->maxpkt_size) {
|
||||
/* went below - stay below for at least interval */
|
||||
cst->first_above_time = 0;
|
||||
} else {
|
||||
if (cst->first_above_time == 0) {
|
||||
/* just went above from below. if still above at
|
||||
* first_above_time, will say it's ok to drop. */
|
||||
cst->first_above_time = now + cprms->interval;
|
||||
} else if (now >= cst->first_above_time) {
|
||||
*ok_to_drop = 1;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a packet from queue 'q'
|
||||
*/
|
||||
__inline static struct mbuf *
|
||||
codel_dequeue(struct dn_queue *q)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct dn_aqm_codel_parms *cprms;
|
||||
struct codel_status *cst;
|
||||
aqm_time_t now;
|
||||
uint16_t ok_to_drop;
|
||||
|
||||
cst = q->aqm_status;;
|
||||
cprms = q->fs->aqmcfg;
|
||||
now = AQM_UNOW;
|
||||
|
||||
m = codel_dodequeue(q, now, &ok_to_drop);
|
||||
if (cst->dropping) {
|
||||
if (!ok_to_drop) {
|
||||
/* sojourn time below target - leave dropping state */
|
||||
cst->dropping = false;
|
||||
}
|
||||
/*
|
||||
* Time for the next drop. Drop current packet and dequeue
|
||||
* next. If the dequeue doesn't take us out of dropping
|
||||
* state, schedule the next drop. A large backlog might
|
||||
* result in drop rates so high that the next drop should
|
||||
* happen now, hence the 'while' loop.
|
||||
*/
|
||||
while (now >= cst->drop_next_time && cst->dropping) {
|
||||
|
||||
/* mark the packet */
|
||||
if (cprms->flags & CODEL_ECN_ENABLED && ecn_mark(m)) {
|
||||
cst->count++;
|
||||
/* schedule the next mark. */
|
||||
cst->drop_next_time = control_law(cst, cprms,
|
||||
cst->drop_next_time);
|
||||
return m;
|
||||
}
|
||||
|
||||
/* drop the packet */
|
||||
update_stats(q, 0, 1);
|
||||
FREE_PKT(m);
|
||||
m = codel_dodequeue(q, now, &ok_to_drop);
|
||||
|
||||
if (!ok_to_drop) {
|
||||
/* leave dropping state */
|
||||
cst->dropping = false;
|
||||
} else {
|
||||
cst->count++;
|
||||
/* schedule the next drop. */
|
||||
cst->drop_next_time = control_law(cst, cprms,
|
||||
cst->drop_next_time);
|
||||
}
|
||||
}
|
||||
/* If we get here we're not in dropping state. The 'ok_to_drop'
|
||||
* return from dodequeue means that the sojourn time has been
|
||||
* above 'target' for 'interval' so enter dropping state.
|
||||
*/
|
||||
} else if (ok_to_drop) {
|
||||
|
||||
/* if ECN option is disabled or the packet cannot be marked,
|
||||
* drop the packet and extract another.
|
||||
*/
|
||||
if (!(cprms->flags & CODEL_ECN_ENABLED) || !ecn_mark(m)) {
|
||||
update_stats(q, 0, 1);
|
||||
FREE_PKT(m);
|
||||
m = codel_dodequeue(q, now, &ok_to_drop);
|
||||
}
|
||||
|
||||
cst->dropping = true;
|
||||
|
||||
/* If min went above target close to when it last went
|
||||
* below, assume that the drop rate that controlled the
|
||||
* queue on the last cycle is a good starting point to
|
||||
* control it now. ('drop_next' will be at most 'interval'
|
||||
* later than the time of the last drop so 'now - drop_next'
|
||||
* is a good approximation of the time from the last drop
|
||||
* until now.)
|
||||
*/
|
||||
cst->count = (cst->count > 2 && ((aqm_stime_t)now -
|
||||
(aqm_stime_t)cst->drop_next_time) < 8* cprms->interval)?
|
||||
cst->count - 2 : 1;
|
||||
/* we don't have to set initial guess for Newton's method isqrt as
|
||||
* we initilaize isqrt in control_law function when count == 1 */
|
||||
cst->drop_next_time = control_law(cst, cprms, now);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
#endif
|
||||
791
sys/netpfil/ipfw/dn_aqm_pie.c
Normal file
791
sys/netpfil/ipfw/dn_aqm_pie.c
Normal file
|
|
@ -0,0 +1,791 @@
|
|||
/*
|
||||
* PIE - Proportional Integral controller Enhanced AQM algorithm.
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include "opt_inet6.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
|
||||
#include <net/netisr.h>
|
||||
#include <net/vnet.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h> /* ip_len, ip_off */
|
||||
#include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <netinet/ip_dummynet.h>
|
||||
#include <netinet/if_ether.h> /* various ether_* routines */
|
||||
#include <netinet/ip6.h> /* for ip6_input, ip6_output prototypes */
|
||||
#include <netinet6/ip6_var.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#include <netpfil/ipfw/dn_aqm_pie.h>
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
|
||||
/* for debugging */
|
||||
#include <sys/syslog.h>
|
||||
|
||||
static struct dn_aqm pie_desc;
|
||||
|
||||
/* PIE defaults
|
||||
* target=15ms, tupdate=15ms, max_burst=150ms,
|
||||
* max_ecnth=0.1, alpha=0.125, beta=1.25,
|
||||
*/
|
||||
struct dn_aqm_pie_parms pie_sysctl =
|
||||
{ 15 * AQM_TIME_1MS, 15 * AQM_TIME_1MS, 150 * AQM_TIME_1MS,
|
||||
PIE_SCALE/10 , PIE_SCALE * 0.125, PIE_SCALE * 1.25 ,
|
||||
PIE_CAPDROP_ENABLED | PIE_DEPRATEEST_ENABLED | PIE_DERAND_ENABLED };
|
||||
|
||||
static int
|
||||
pie_sysctl_alpha_beta_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
if (!strcmp(oidp->oid_name,"alpha"))
|
||||
value = pie_sysctl.alpha;
|
||||
else
|
||||
value = pie_sysctl.beta;
|
||||
|
||||
value = value * 1000 / PIE_SCALE;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > 7 * PIE_SCALE)
|
||||
return (EINVAL);
|
||||
value = (value * PIE_SCALE) / 1000;
|
||||
if (!strcmp(oidp->oid_name,"alpha"))
|
||||
pie_sysctl.alpha = value;
|
||||
else
|
||||
pie_sysctl.beta = value;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
pie_sysctl_target_tupdate_maxb_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
if (!strcmp(oidp->oid_name,"target"))
|
||||
value = pie_sysctl.qdelay_ref;
|
||||
else if (!strcmp(oidp->oid_name,"tupdate"))
|
||||
value = pie_sysctl.tupdate;
|
||||
else
|
||||
value = pie_sysctl.max_burst;
|
||||
|
||||
value = value / AQM_TIME_1US;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > 10 * AQM_TIME_1S)
|
||||
return (EINVAL);
|
||||
value = value * AQM_TIME_1US;
|
||||
|
||||
if (!strcmp(oidp->oid_name,"target"))
|
||||
pie_sysctl.qdelay_ref = value;
|
||||
else if (!strcmp(oidp->oid_name,"tupdate"))
|
||||
pie_sysctl.tupdate = value;
|
||||
else
|
||||
pie_sysctl.max_burst = value;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
pie_sysctl_max_ecnth_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
value = pie_sysctl.max_ecnth;
|
||||
value = value * 1000 / PIE_SCALE;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > PIE_SCALE)
|
||||
return (EINVAL);
|
||||
value = (value * PIE_SCALE) / 1000;
|
||||
pie_sysctl.max_ecnth = value;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* define PIE sysctl variables */
|
||||
SYSBEGIN(f4)
|
||||
SYSCTL_DECL(_net_inet);
|
||||
SYSCTL_DECL(_net_inet_ip);
|
||||
SYSCTL_DECL(_net_inet_ip_dummynet);
|
||||
static SYSCTL_NODE(_net_inet_ip_dummynet, OID_AUTO,
|
||||
pie, CTLFLAG_RW, 0, "PIE");
|
||||
|
||||
#ifdef SYSCTL_NODE
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, target,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_target_tupdate_maxb_handler, "L",
|
||||
"queue target in microsecond");
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, tupdate,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_target_tupdate_maxb_handler, "L",
|
||||
"the frequency of drop probability calculation in microsecond");
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, max_burst,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_target_tupdate_maxb_handler, "L",
|
||||
"Burst allowance interval in microsecond");
|
||||
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, max_ecnth,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_max_ecnth_handler, "L",
|
||||
"ECN safeguard threshold scaled by 1000");
|
||||
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, alpha,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_alpha_beta_handler, "L",
|
||||
"PIE alpha scaled by 1000");
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_pie, OID_AUTO, beta,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0,
|
||||
pie_sysctl_alpha_beta_handler, "L",
|
||||
"beta scaled by 1000");
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Callout function for drop probability calculation
|
||||
* This function is called over tupdate ms and takes pointer of PIE
|
||||
* status variables as an argument
|
||||
*/
|
||||
static void
|
||||
calculate_drop_prob(void *x)
|
||||
{
|
||||
int64_t p, prob, oldprob;
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
struct pie_status *pst = (struct pie_status *) x;
|
||||
|
||||
/* dealing with race condition */
|
||||
if (callout_pending(&pst->aqm_pie_callout)) {
|
||||
/* callout was reset */
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callout_active(&pst->aqm_pie_callout)) {
|
||||
/* callout was stopped */
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
mtx_destroy(&pst->lock_mtx);
|
||||
free(x, M_DUMMYNET);
|
||||
//pst->pq->aqm_status = NULL;
|
||||
pie_desc.ref_count--;
|
||||
return;
|
||||
}
|
||||
callout_deactivate(&pst->aqm_pie_callout);
|
||||
|
||||
pprms = pst->parms;
|
||||
prob = pst->drop_prob;
|
||||
|
||||
/* calculate current qdelay */
|
||||
if (pprms->flags & PIE_DEPRATEEST_ENABLED) {
|
||||
pst->current_qdelay = ((uint64_t)pst->pq->ni.len_bytes *
|
||||
pst->avg_dq_time) >> PIE_DQ_THRESHOLD_BITS;
|
||||
}
|
||||
|
||||
/* calculate drop probability */
|
||||
p = (int64_t)pprms->alpha *
|
||||
((int64_t)pst->current_qdelay - (int64_t)pprms->qdelay_ref);
|
||||
p +=(int64_t) pprms->beta *
|
||||
((int64_t)pst->current_qdelay - (int64_t)pst->qdelay_old);
|
||||
|
||||
/* We PIE_MAX_PROB shift by 12-bits to increase the division precision */
|
||||
p *= (PIE_MAX_PROB << 12) / AQM_TIME_1S;
|
||||
|
||||
/* auto-tune drop probability */
|
||||
if (prob< PIE_MAX_PROB * 0.000001)
|
||||
p >>= 11 + PIE_FIX_POINT_BITS+12;
|
||||
else if (prob < PIE_MAX_PROB * 0.00001)
|
||||
p >>= 9 + PIE_FIX_POINT_BITS+12;
|
||||
else if (prob < PIE_MAX_PROB * 0.0001)
|
||||
p >>= 7 + PIE_FIX_POINT_BITS+12;
|
||||
else if (prob < PIE_MAX_PROB * 0.001)
|
||||
p >>= 5 + PIE_FIX_POINT_BITS+12;
|
||||
else if (prob < PIE_MAX_PROB * 0.01)
|
||||
p >>= 3 + PIE_FIX_POINT_BITS+12;
|
||||
else if (prob < PIE_MAX_PROB * 0.1)
|
||||
p >>= 1 + PIE_FIX_POINT_BITS+12;
|
||||
else
|
||||
p >>= PIE_FIX_POINT_BITS+12;
|
||||
|
||||
oldprob = prob;
|
||||
|
||||
/* Cap Drop adjustment */
|
||||
if ((pprms->flags & PIE_CAPDROP_ENABLED) && prob >= PIE_MAX_PROB / 10
|
||||
&& p > PIE_MAX_PROB / 50 )
|
||||
p = PIE_MAX_PROB / 50;
|
||||
|
||||
prob = prob + p;
|
||||
|
||||
/* decay the drop probability exponentially */
|
||||
if (pst->current_qdelay == 0 && pst->qdelay_old == 0)
|
||||
/* 0.98 ~= 1- 1/64 */
|
||||
prob = prob - (prob >> 6);
|
||||
|
||||
|
||||
/* check for multiplication overflow/underflow */
|
||||
if (p>0) {
|
||||
if (prob<oldprob) {
|
||||
D("overflow");
|
||||
prob= PIE_MAX_PROB;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (prob>oldprob) {
|
||||
prob= 0;
|
||||
D("underflow");
|
||||
}
|
||||
|
||||
/* make drop probability between 0 and PIE_MAX_PROB*/
|
||||
if (prob < 0)
|
||||
prob = 0;
|
||||
else if (prob > PIE_MAX_PROB)
|
||||
prob = PIE_MAX_PROB;
|
||||
|
||||
pst->drop_prob = prob;
|
||||
|
||||
/* store current queue delay value in old queue delay*/
|
||||
pst->qdelay_old = pst->current_qdelay;
|
||||
|
||||
/* update burst allowance */
|
||||
if ((pst->sflags & PIE_ACTIVE) && pst->burst_allowance>0) {
|
||||
|
||||
if (pst->burst_allowance > pprms->tupdate )
|
||||
pst->burst_allowance -= pprms->tupdate;
|
||||
else
|
||||
pst->burst_allowance = 0;
|
||||
}
|
||||
|
||||
/* reschedule calculate_drop_prob function */
|
||||
if (pst->sflags & PIE_ACTIVE)
|
||||
callout_reset_sbt(&pst->aqm_pie_callout,
|
||||
(uint64_t)pprms->tupdate * SBT_1US, 0, calculate_drop_prob, pst, 0);
|
||||
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a packet from the head of queue 'q'
|
||||
* Return a packet or NULL if the queue is empty.
|
||||
* If getts is set, also extract packet's timestamp from mtag.
|
||||
*/
|
||||
static struct mbuf *
|
||||
pie_extract_head(struct dn_queue *q, aqm_time_t *pkt_ts, int getts)
|
||||
{
|
||||
struct m_tag *mtag;
|
||||
struct mbuf *m = q->mq.head;
|
||||
|
||||
if (m == NULL)
|
||||
return m;
|
||||
q->mq.head = m->m_nextpkt;
|
||||
|
||||
/* Update stats */
|
||||
update_stats(q, -m->m_pkthdr.len, 0);
|
||||
|
||||
if (q->ni.length == 0) /* queue is now idle */
|
||||
q->q_time = dn_cfg.curr_time;
|
||||
|
||||
if (getts) {
|
||||
/* extract packet TS*/
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL) {
|
||||
D("PIE timestamp mtag not found!");
|
||||
*pkt_ts = 0;
|
||||
} else {
|
||||
*pkt_ts = *(aqm_time_t *)(mtag + 1);
|
||||
m_tag_delete(m,mtag);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate PIE variable and optionally activate it
|
||||
*/
|
||||
__inline static void
|
||||
init_activate_pie(struct pie_status *pst, int resettimer)
|
||||
{
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
|
||||
mtx_lock(&pst->lock_mtx);
|
||||
pprms = pst->parms;
|
||||
pst->drop_prob = 0;
|
||||
pst->qdelay_old = 0;
|
||||
pst->burst_allowance = pprms->max_burst;
|
||||
pst->accu_prob = 0;
|
||||
pst->dq_count = 0;
|
||||
pst->avg_dq_time = 0;
|
||||
pst->sflags = PIE_INMEASUREMENT;
|
||||
pst->measurement_start = AQM_UNOW;
|
||||
|
||||
if (resettimer) {
|
||||
pst->sflags |= PIE_ACTIVE;
|
||||
callout_reset_sbt(&pst->aqm_pie_callout,
|
||||
(uint64_t)pprms->tupdate * SBT_1US,
|
||||
0, calculate_drop_prob, pst, 0);
|
||||
}
|
||||
//DX(2, "PIE Activated");
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivate PIE and stop probe update callout
|
||||
*/
|
||||
__inline static void
|
||||
deactivate_pie(struct pie_status *pst)
|
||||
{
|
||||
mtx_lock(&pst->lock_mtx);
|
||||
pst->sflags &= ~(PIE_ACTIVE | PIE_INMEASUREMENT);
|
||||
callout_stop(&pst->aqm_pie_callout);
|
||||
//D("PIE Deactivated");
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue and return a pcaket from queue 'q' or NULL if 'q' is empty.
|
||||
* Also, caculate depature time or queue delay using timestamp
|
||||
*/
|
||||
static struct mbuf *
|
||||
aqm_pie_dequeue(struct dn_queue *q)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct dn_flow *ni; /* stats for scheduler instance */
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
struct pie_status *pst;
|
||||
aqm_time_t now;
|
||||
aqm_time_t pkt_ts, dq_time;
|
||||
int32_t w;
|
||||
|
||||
pst = q->aqm_status;
|
||||
pprms = pst->parms;
|
||||
ni = &q->_si->ni;
|
||||
|
||||
/*we extarct packet ts only when Departure Rate Estimation dis not used*/
|
||||
m = pie_extract_head(q, &pkt_ts, !(pprms->flags & PIE_DEPRATEEST_ENABLED));
|
||||
|
||||
if (!m || !(pst->sflags & PIE_ACTIVE))
|
||||
return m;
|
||||
|
||||
now = AQM_UNOW;
|
||||
if (pprms->flags & PIE_DEPRATEEST_ENABLED) {
|
||||
/* calculate average depature time */
|
||||
if(pst->sflags & PIE_INMEASUREMENT) {
|
||||
pst->dq_count += m->m_pkthdr.len;
|
||||
|
||||
if (pst->dq_count >= PIE_DQ_THRESHOLD) {
|
||||
dq_time = now - pst->measurement_start;
|
||||
|
||||
/*
|
||||
* if we don't have old avg dq_time i.e PIE is (re)initialized,
|
||||
* don't use weight to calculate new avg_dq_time
|
||||
*/
|
||||
if(pst->avg_dq_time == 0)
|
||||
pst->avg_dq_time = dq_time;
|
||||
else {
|
||||
/*
|
||||
* weight = PIE_DQ_THRESHOLD/2^6, but we scaled
|
||||
* weight by 2^8. Thus, scaled
|
||||
* weight = PIE_DQ_THRESHOLD /2^8
|
||||
* */
|
||||
w = PIE_DQ_THRESHOLD >> 8;
|
||||
pst->avg_dq_time = (dq_time* w
|
||||
+ (pst->avg_dq_time * ((1L << 8) - w))) >> 8;
|
||||
pst->sflags &= ~PIE_INMEASUREMENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start new measurment cycle when the queue has
|
||||
* PIE_DQ_THRESHOLD worth of bytes.
|
||||
*/
|
||||
if(!(pst->sflags & PIE_INMEASUREMENT) &&
|
||||
q->ni.len_bytes >= PIE_DQ_THRESHOLD) {
|
||||
pst->sflags |= PIE_INMEASUREMENT;
|
||||
pst->measurement_start = now;
|
||||
pst->dq_count = 0;
|
||||
}
|
||||
}
|
||||
/* Optionally, use packet timestamp to estimate queue delay */
|
||||
else
|
||||
pst->current_qdelay = now - pkt_ts;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue a packet in q, subject to space and PIE queue management policy
|
||||
* (whose parameters are in q->fs).
|
||||
* Update stats for the queue and the scheduler.
|
||||
* Return 0 on success, 1 on drop. The packet is consumed anyways.
|
||||
*/
|
||||
static int
|
||||
aqm_pie_enqueue(struct dn_queue *q, struct mbuf* m)
|
||||
{
|
||||
struct dn_fs *f;
|
||||
uint64_t len;
|
||||
uint32_t qlen;
|
||||
struct pie_status *pst;
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
int t;
|
||||
|
||||
len = m->m_pkthdr.len;
|
||||
pst = q->aqm_status;
|
||||
if(!pst) {
|
||||
DX(2, "PIE queue is not initialized\n");
|
||||
update_stats(q, 0, 1);
|
||||
FREE_PKT(m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
f = &(q->fs->fs);
|
||||
pprms = pst->parms;
|
||||
t = ENQUE;
|
||||
|
||||
/* get current queue length in bytes or packets*/
|
||||
qlen = (f->flags & DN_QSIZE_BYTES) ?
|
||||
q->ni.len_bytes : q->ni.length;
|
||||
|
||||
/* check for queue size and drop the tail if exceed queue limit*/
|
||||
if (qlen >= f->qsize)
|
||||
t = DROP;
|
||||
/* drop/mark the packet when PIE is active and burst time elapsed */
|
||||
else if ((pst->sflags & PIE_ACTIVE) && pst->burst_allowance==0
|
||||
&& drop_early(pst, q->ni.len_bytes) == DROP) {
|
||||
/*
|
||||
* if drop_prob over ECN threshold, drop the packet
|
||||
* otherwise mark and enqueue it.
|
||||
*/
|
||||
if ((pprms->flags & PIE_ECN_ENABLED) && pst->drop_prob <
|
||||
(pprms->max_ecnth << (PIE_PROB_BITS - PIE_FIX_POINT_BITS))
|
||||
&& ecn_mark(m))
|
||||
t = ENQUE;
|
||||
else
|
||||
t = DROP;
|
||||
}
|
||||
|
||||
/* Turn PIE on when 1/3 of the queue is full */
|
||||
if (!(pst->sflags & PIE_ACTIVE) && qlen >= pst->one_third_q_size) {
|
||||
init_activate_pie(pst, 1);
|
||||
}
|
||||
|
||||
/* Reset burst tolerance and optinally turn PIE off*/
|
||||
if ((pst->sflags & PIE_ACTIVE) && pst->drop_prob == 0 &&
|
||||
pst->current_qdelay < (pprms->qdelay_ref >> 1) &&
|
||||
pst->qdelay_old < (pprms->qdelay_ref >> 1)) {
|
||||
|
||||
pst->burst_allowance = pprms->max_burst;
|
||||
if ((pprms->flags & PIE_ON_OFF_MODE_ENABLED) && qlen<=0)
|
||||
deactivate_pie(pst);
|
||||
}
|
||||
|
||||
/* Timestamp the packet if Departure Rate Estimation is disabled */
|
||||
if (t != DROP && !(pprms->flags & PIE_DEPRATEEST_ENABLED)) {
|
||||
/* Add TS to mbuf as a TAG */
|
||||
struct m_tag *mtag;
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL)
|
||||
mtag = m_tag_alloc(MTAG_ABI_COMPAT, DN_AQM_MTAG_TS,
|
||||
sizeof(aqm_time_t), M_NOWAIT);
|
||||
if (mtag == NULL) {
|
||||
m_freem(m);
|
||||
t = DROP;
|
||||
}
|
||||
*(aqm_time_t *)(mtag + 1) = AQM_UNOW;
|
||||
m_tag_prepend(m, mtag);
|
||||
}
|
||||
|
||||
if (t != DROP) {
|
||||
mq_append(&q->mq, m);
|
||||
update_stats(q, len, 0);
|
||||
return (0);
|
||||
} else {
|
||||
update_stats(q, 0, 1);
|
||||
|
||||
/* reset accu_prob after packet drop */
|
||||
pst->accu_prob = 0;
|
||||
FREE_PKT(m);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize PIE for queue 'q'
|
||||
* First allocate memory for PIE status.
|
||||
*/
|
||||
static int
|
||||
aqm_pie_init(struct dn_queue *q)
|
||||
{
|
||||
struct pie_status *pst;
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
int err = 0;
|
||||
|
||||
pprms = q->fs->aqmcfg;
|
||||
|
||||
do { /* exit with break when error occurs*/
|
||||
if (!pprms){
|
||||
D("AQM_PIE is not configured");
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
q->aqm_status = malloc(sizeof(struct pie_status),
|
||||
M_DUMMYNET, M_NOWAIT | M_ZERO);
|
||||
if (q->aqm_status == NULL) {
|
||||
D("cannot allocate PIE private data");
|
||||
err = ENOMEM ;
|
||||
break;
|
||||
}
|
||||
|
||||
pst = q->aqm_status;
|
||||
/* increase reference count for PIE module */
|
||||
pie_desc.ref_count++;
|
||||
|
||||
pst->pq = q;
|
||||
pst->parms = pprms;
|
||||
|
||||
/* For speed optimization, we caculate 1/3 queue size once here */
|
||||
// we can use x/3 = (x >>2) + (x >>4) + (x >>7)
|
||||
pst->one_third_q_size = q->fs->fs.qsize/3;
|
||||
|
||||
mtx_init(&pst->lock_mtx, "mtx_pie", NULL, MTX_DEF);
|
||||
callout_init_mtx(&pst->aqm_pie_callout, &pst->lock_mtx,
|
||||
CALLOUT_RETURNUNLOCKED);
|
||||
|
||||
pst->current_qdelay = 0;
|
||||
init_activate_pie(pst, !(pprms->flags & PIE_ON_OFF_MODE_ENABLED));
|
||||
|
||||
//DX(2, "aqm_PIE_init");
|
||||
|
||||
} while(0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up PIE status for queue 'q'
|
||||
* Destroy memory allocated for PIE status.
|
||||
*/
|
||||
static int
|
||||
aqm_pie_cleanup(struct dn_queue *q)
|
||||
{
|
||||
|
||||
if(!q) {
|
||||
D("q is null");
|
||||
return 0;
|
||||
}
|
||||
struct pie_status *pst = q->aqm_status;
|
||||
if(!pst) {
|
||||
//D("queue is already cleaned up");
|
||||
return 0;
|
||||
}
|
||||
if(!q->fs || !q->fs->aqmcfg) {
|
||||
D("fs is null or no cfg");
|
||||
return 1;
|
||||
}
|
||||
if (q->fs->aqmfp && q->fs->aqmfp->type !=DN_AQM_PIE) {
|
||||
D("Not PIE fs (%d)", q->fs->fs.fs_nr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mtx_lock(&pst->lock_mtx);
|
||||
|
||||
/* stop callout timer */
|
||||
if (callout_stop(&pst->aqm_pie_callout) || !(pst->sflags & PIE_ACTIVE)) {
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
mtx_destroy(&pst->lock_mtx);
|
||||
free(q->aqm_status, M_DUMMYNET);
|
||||
q->aqm_status = NULL;
|
||||
pie_desc.ref_count--;
|
||||
return 0;
|
||||
} else {
|
||||
q->aqm_status = NULL;
|
||||
mtx_unlock(&pst->lock_mtx);
|
||||
DX(2, "PIE callout has not been stoped from cleanup!");
|
||||
return EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config PIE parameters
|
||||
* also allocate memory for PIE configurations
|
||||
*/
|
||||
static int
|
||||
aqm_pie_config(struct dn_fsk* fs, struct dn_extra_parms *ep, int len)
|
||||
{
|
||||
struct dn_aqm_pie_parms *pcfg;
|
||||
|
||||
int l = sizeof(struct dn_extra_parms);
|
||||
if (len < l) {
|
||||
D("invalid sched parms length got %d need %d", len, l);
|
||||
return EINVAL;
|
||||
}
|
||||
/* we free the old cfg because maybe the orignal allocation
|
||||
* was used for diffirent AQM type.
|
||||
*/
|
||||
if (fs->aqmcfg) {
|
||||
free(fs->aqmcfg, M_DUMMYNET);
|
||||
fs->aqmcfg = NULL;
|
||||
}
|
||||
|
||||
fs->aqmcfg = malloc(sizeof(struct dn_aqm_pie_parms),
|
||||
M_DUMMYNET, M_NOWAIT | M_ZERO);
|
||||
if (fs->aqmcfg== NULL) {
|
||||
D("cannot allocate PIE configuration parameters");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* par array contains pie configuration as follow
|
||||
* 0- qdelay_ref,1- tupdate, 2- max_burst
|
||||
* 3- max_ecnth, 4- alpha, 5- beta, 6- flags
|
||||
*/
|
||||
|
||||
/* configure PIE parameters */
|
||||
pcfg = fs->aqmcfg;
|
||||
|
||||
if (ep->par[0] < 0)
|
||||
pcfg->qdelay_ref = pie_sysctl.qdelay_ref * AQM_TIME_1US;
|
||||
else
|
||||
pcfg->qdelay_ref = ep->par[0];
|
||||
if (ep->par[1] < 0)
|
||||
pcfg->tupdate = pie_sysctl.tupdate * AQM_TIME_1US;
|
||||
else
|
||||
pcfg->tupdate = ep->par[1];
|
||||
if (ep->par[2] < 0)
|
||||
pcfg->max_burst = pie_sysctl.max_burst * AQM_TIME_1US;
|
||||
else
|
||||
pcfg->max_burst = ep->par[2];
|
||||
if (ep->par[3] < 0)
|
||||
pcfg->max_ecnth = pie_sysctl.max_ecnth;
|
||||
else
|
||||
pcfg->max_ecnth = ep->par[3];
|
||||
if (ep->par[4] < 0)
|
||||
pcfg->alpha = pie_sysctl.alpha;
|
||||
else
|
||||
pcfg->alpha = ep->par[4];
|
||||
if (ep->par[5] < 0)
|
||||
pcfg->beta = pie_sysctl.beta;
|
||||
else
|
||||
pcfg->beta = ep->par[5];
|
||||
if (ep->par[6] < 0)
|
||||
pcfg->flags = pie_sysctl.flags;
|
||||
else
|
||||
pcfg->flags = ep->par[6];
|
||||
|
||||
/* bound PIE configurations */
|
||||
pcfg->qdelay_ref = BOUND_VAR(pcfg->qdelay_ref, 1, 10 * AQM_TIME_1S);
|
||||
pcfg->tupdate = BOUND_VAR(pcfg->tupdate, 1, 10 * AQM_TIME_1S);
|
||||
pcfg->max_burst = BOUND_VAR(pcfg->max_burst, 0, 10 * AQM_TIME_1S);
|
||||
pcfg->max_ecnth = BOUND_VAR(pcfg->max_ecnth, 0, PIE_SCALE);
|
||||
pcfg->alpha = BOUND_VAR(pcfg->alpha, 0, 7 * PIE_SCALE);
|
||||
pcfg->beta = BOUND_VAR(pcfg->beta, 0 , 7 * PIE_SCALE);
|
||||
|
||||
pie_desc.cfg_ref_count++;
|
||||
//D("pie cfg_ref_count=%d", pie_desc.cfg_ref_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deconfigure PIE and free memory allocation
|
||||
*/
|
||||
static int
|
||||
aqm_pie_deconfig(struct dn_fsk* fs)
|
||||
{
|
||||
if (fs && fs->aqmcfg) {
|
||||
free(fs->aqmcfg, M_DUMMYNET);
|
||||
fs->aqmcfg = NULL;
|
||||
pie_desc.cfg_ref_count--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve PIE configuration parameters.
|
||||
*/
|
||||
static int
|
||||
aqm_pie_getconfig (struct dn_fsk *fs, struct dn_extra_parms * ep)
|
||||
{
|
||||
struct dn_aqm_pie_parms *pcfg;
|
||||
if (fs->aqmcfg) {
|
||||
strcpy(ep->name, pie_desc.name);
|
||||
pcfg = fs->aqmcfg;
|
||||
ep->par[0] = pcfg->qdelay_ref / AQM_TIME_1US;
|
||||
ep->par[1] = pcfg->tupdate / AQM_TIME_1US;
|
||||
ep->par[2] = pcfg->max_burst / AQM_TIME_1US;
|
||||
ep->par[3] = pcfg->max_ecnth;
|
||||
ep->par[4] = pcfg->alpha;
|
||||
ep->par[5] = pcfg->beta;
|
||||
ep->par[6] = pcfg->flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dn_aqm pie_desc = {
|
||||
_SI( .type = ) DN_AQM_PIE,
|
||||
_SI( .name = ) "PIE",
|
||||
_SI( .ref_count = ) 0,
|
||||
_SI( .cfg_ref_count = ) 0,
|
||||
_SI( .enqueue = ) aqm_pie_enqueue,
|
||||
_SI( .dequeue = ) aqm_pie_dequeue,
|
||||
_SI( .config = ) aqm_pie_config,
|
||||
_SI( .deconfig = ) aqm_pie_deconfig,
|
||||
_SI( .getconfig = ) aqm_pie_getconfig,
|
||||
_SI( .init = ) aqm_pie_init,
|
||||
_SI( .cleanup = ) aqm_pie_cleanup,
|
||||
};
|
||||
|
||||
DECLARE_DNAQM_MODULE(dn_aqm_pie, &pie_desc);
|
||||
#endif
|
||||
149
sys/netpfil/ipfw/dn_aqm_pie.h
Normal file
149
sys/netpfil/ipfw/dn_aqm_pie.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* PIE - Proportional Integral controller Enhanced AQM algorithm.
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _IP_DN_AQM_PIE_H
|
||||
#define _IP_DN_AQM_PIE_H
|
||||
|
||||
#define DN_AQM_PIE 2
|
||||
#define PIE_DQ_THRESHOLD_BITS 14
|
||||
/* 2^14 =16KB */
|
||||
#define PIE_DQ_THRESHOLD (1UL << PIE_DQ_THRESHOLD_BITS)
|
||||
#define MEAN_PKTSIZE 800
|
||||
|
||||
/* 31-bits because random() generates range from 0->(2**31)-1 */
|
||||
#define PIE_PROB_BITS 31
|
||||
#define PIE_MAX_PROB ((1ULL<<PIE_PROB_BITS) -1)
|
||||
|
||||
/* for 16-bits, we have 3-bits for integer part and 13-bits for fraction */
|
||||
#define PIE_FIX_POINT_BITS 13
|
||||
#define PIE_SCALE (1UL<<PIE_FIX_POINT_BITS)
|
||||
|
||||
|
||||
/* PIE options */
|
||||
enum {
|
||||
PIE_ECN_ENABLED =1,
|
||||
PIE_CAPDROP_ENABLED = 2,
|
||||
PIE_ON_OFF_MODE_ENABLED = 4,
|
||||
PIE_DEPRATEEST_ENABLED = 8,
|
||||
PIE_DERAND_ENABLED = 16
|
||||
};
|
||||
|
||||
/* PIE parameters */
|
||||
struct dn_aqm_pie_parms {
|
||||
aqm_time_t qdelay_ref; /* AQM Latency Target (default: 15ms) */
|
||||
aqm_time_t tupdate; /* a period to calculate drop probability (default:15ms) */
|
||||
aqm_time_t max_burst; /* AQM Max Burst Allowance (default: 150ms) */
|
||||
uint16_t max_ecnth; /*AQM Max ECN Marking Threshold (default: 10%) */
|
||||
uint16_t alpha; /* (default: 1/8) */
|
||||
uint16_t beta; /* (default: 1+1/4) */
|
||||
uint32_t flags; /* PIE options */
|
||||
};
|
||||
|
||||
/* PIE status variables */
|
||||
struct pie_status{
|
||||
struct callout aqm_pie_callout;
|
||||
aqm_time_t burst_allowance;
|
||||
uint32_t drop_prob;
|
||||
aqm_time_t current_qdelay;
|
||||
aqm_time_t qdelay_old;
|
||||
uint64_t accu_prob;
|
||||
aqm_time_t measurement_start;
|
||||
aqm_time_t avg_dq_time;
|
||||
uint32_t dq_count;
|
||||
uint32_t sflags;
|
||||
struct dn_aqm_pie_parms *parms; /* pointer to PIE configurations */
|
||||
/* pointer to parent queue of FQ-PIE sub-queues, or queue of owner fs. */
|
||||
struct dn_queue *pq;
|
||||
struct mtx lock_mtx;
|
||||
uint32_t one_third_q_size; /* 1/3 of queue size, for speed optization */
|
||||
};
|
||||
|
||||
enum {
|
||||
ENQUE = 1,
|
||||
DROP,
|
||||
MARKECN
|
||||
};
|
||||
|
||||
/* PIE current state */
|
||||
enum {
|
||||
PIE_ACTIVE = 1,
|
||||
PIE_INMEASUREMENT = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if eneque should drop packet to control delay or not based on
|
||||
* PIe algorithm.
|
||||
* return DROP if it is time to drop or ENQUE otherwise.
|
||||
* This function is used by PIE and FQ-PIE.
|
||||
*/
|
||||
__inline static int
|
||||
drop_early(struct pie_status *pst, uint32_t qlen)
|
||||
{
|
||||
struct dn_aqm_pie_parms *pprms;
|
||||
|
||||
pprms = pst->parms;
|
||||
|
||||
/* queue is not congested */
|
||||
|
||||
if ((pst->qdelay_old < (pprms->qdelay_ref >> 1)
|
||||
&& pst->drop_prob < PIE_MAX_PROB / 5 )
|
||||
|| qlen <= 2 * MEAN_PKTSIZE)
|
||||
return ENQUE;
|
||||
|
||||
|
||||
if (pst->drop_prob == 0)
|
||||
pst->accu_prob = 0;
|
||||
|
||||
/* increment accu_prob */
|
||||
if (pprms->flags & PIE_DERAND_ENABLED)
|
||||
pst->accu_prob += pst->drop_prob;
|
||||
|
||||
/* De-randomize option
|
||||
* if accu_prob < 0.85 -> enqueue
|
||||
* if accu_prob>8.5 ->drop
|
||||
* between 0.85 and 8.5 || !De-randomize --> drop on prob
|
||||
*/
|
||||
if (pprms->flags & PIE_DERAND_ENABLED) {
|
||||
if(pst->accu_prob < (uint64_t) (PIE_MAX_PROB * 0.85))
|
||||
return ENQUE;
|
||||
if( pst->accu_prob >= (uint64_t) (PIE_MAX_PROB * 8.5))
|
||||
return DROP;
|
||||
}
|
||||
|
||||
if (random() < pst->drop_prob) {
|
||||
pst->accu_prob = 0;
|
||||
return DROP;
|
||||
}
|
||||
|
||||
return ENQUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -132,6 +132,10 @@ struct dn_alg {
|
|||
int (*free_fsk)(struct dn_fsk *f);
|
||||
int (*new_queue)(struct dn_queue *q);
|
||||
int (*free_queue)(struct dn_queue *q);
|
||||
#ifdef NEW_AQM
|
||||
/* Getting scheduler extra parameters */
|
||||
int (*getconfig)(struct dn_schk *, struct dn_extra_parms *);
|
||||
#endif
|
||||
|
||||
/* run-time fields */
|
||||
int ref_count; /* XXX number of instances in the system */
|
||||
|
|
@ -165,6 +169,11 @@ dn_dequeue(struct dn_queue *q)
|
|||
struct mbuf *m = q->mq.head;
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
#ifdef NEW_AQM
|
||||
/* Call AQM dequeue function */
|
||||
if (q->fs->aqmfp && q->fs->aqmfp->dequeue )
|
||||
return q->fs->aqmfp->dequeue(q);
|
||||
#endif
|
||||
q->mq.head = m->m_nextpkt;
|
||||
|
||||
/* Update stats for the queue */
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
#include <netinet/ip_dummynet.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
|
|
@ -115,6 +118,9 @@ static struct dn_alg fifo_desc = {
|
|||
_SI( .free_fsk = ) NULL,
|
||||
_SI( .new_queue = ) NULL,
|
||||
_SI( .free_queue = ) NULL,
|
||||
#ifdef NEW_AQM
|
||||
_SI( .getconfig = ) NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
DECLARE_DNSCHED_MODULE(dn_fifo, &fifo_desc);
|
||||
|
|
|
|||
615
sys/netpfil/ipfw/dn_sched_fq_codel.c
Normal file
615
sys/netpfil/ipfw/dn_sched_fq_codel.c
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* FQ_Codel - The FlowQueue-Codel scheduler/AQM
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/socket.h>
|
||||
//#include <sys/socketvar.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/module.h>
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_var.h> /* ipfw_rule_ref */
|
||||
#include <netinet/ip_fw.h> /* flow_id */
|
||||
#include <netinet/ip_dummynet.h>
|
||||
|
||||
#include <sys/proc.h>
|
||||
#include <sys/rwlock.h>
|
||||
|
||||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/hash.h>
|
||||
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#include <netpfil/ipfw/dn_aqm_codel.h>
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#include <netpfil/ipfw/dn_sched_fq_codel.h>
|
||||
#include <netpfil/ipfw/dn_sched_fq_codel_helper.h>
|
||||
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
#endif
|
||||
|
||||
/* NOTE: In fq_codel module, we reimplements CoDel AQM functions
|
||||
* because fq_codel use different flows (sub-queues) structure and
|
||||
* dn_queue includes many variables not needed by a flow (sub-queue
|
||||
* )i.e. avoid extra overhead (88 bytes vs 208 bytes).
|
||||
* Also, CoDel functions manages stats of sub-queues as well as the main queue.
|
||||
*/
|
||||
|
||||
#define DN_SCHED_FQ_CODEL 6
|
||||
|
||||
static struct dn_alg fq_codel_desc;
|
||||
|
||||
/* fq_codel default parameters including codel */
|
||||
struct dn_sch_fq_codel_parms
|
||||
fq_codel_sysctl = {{5000 * AQM_TIME_1US, 100000 * AQM_TIME_1US,
|
||||
CODEL_ECN_ENABLED}, 1024, 10240, 1514};
|
||||
|
||||
static int
|
||||
fqcodel_sysctl_interval_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
value = fq_codel_sysctl.ccfg.interval;
|
||||
value /= AQM_TIME_1US;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > 100 * AQM_TIME_1S)
|
||||
return (EINVAL);
|
||||
fq_codel_sysctl.ccfg.interval = value * AQM_TIME_1US ;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fqcodel_sysctl_target_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error;
|
||||
long value;
|
||||
|
||||
value = fq_codel_sysctl.ccfg.target;
|
||||
value /= AQM_TIME_1US;
|
||||
error = sysctl_handle_long(oidp, &value, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (value < 1 || value > 5 * AQM_TIME_1S)
|
||||
return (EINVAL);
|
||||
fq_codel_sysctl.ccfg.target = value * AQM_TIME_1US ;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
SYSBEGIN(f4)
|
||||
|
||||
SYSCTL_DECL(_net_inet);
|
||||
SYSCTL_DECL(_net_inet_ip);
|
||||
SYSCTL_DECL(_net_inet_ip_dummynet);
|
||||
static SYSCTL_NODE(_net_inet_ip_dummynet, OID_AUTO, fqcodel,
|
||||
CTLFLAG_RW, 0, "FQ_CODEL");
|
||||
|
||||
#ifdef SYSCTL_NODE
|
||||
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_fqcodel, OID_AUTO, target,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0, fqcodel_sysctl_target_handler, "L",
|
||||
"FQ_CoDel target in microsecond");
|
||||
SYSCTL_PROC(_net_inet_ip_dummynet_fqcodel, OID_AUTO, interval,
|
||||
CTLTYPE_LONG | CTLFLAG_RW, NULL, 0, fqcodel_sysctl_interval_handler, "L",
|
||||
"FQ_CoDel interval in microsecond");
|
||||
|
||||
SYSCTL_UINT(_net_inet_ip_dummynet_fqcodel, OID_AUTO, quantum,
|
||||
CTLFLAG_RW, &fq_codel_sysctl.quantum, 1514, "FQ_CoDel quantum");
|
||||
SYSCTL_UINT(_net_inet_ip_dummynet_fqcodel, OID_AUTO, flows,
|
||||
CTLFLAG_RW, &fq_codel_sysctl.flows_cnt, 1024,
|
||||
"Number of queues for FQ_CoDel");
|
||||
SYSCTL_UINT(_net_inet_ip_dummynet_fqcodel, OID_AUTO, limit,
|
||||
CTLFLAG_RW, &fq_codel_sysctl.limit, 10240, "FQ_CoDel queues size limit");
|
||||
#endif
|
||||
|
||||
/* Drop a packet form the head of codel queue */
|
||||
static void
|
||||
codel_drop_head(struct fq_codel_flow *q, struct fq_codel_si *si)
|
||||
{
|
||||
struct mbuf *m = q->mq.head;
|
||||
|
||||
if (m == NULL)
|
||||
return;
|
||||
q->mq.head = m->m_nextpkt;
|
||||
|
||||
fq_update_stats(q, si, -m->m_pkthdr.len, 1);
|
||||
|
||||
if (si->main_q.ni.length == 0) /* queue is now idle */
|
||||
si->main_q.q_time = dn_cfg.curr_time;
|
||||
|
||||
FREE_PKT(m);
|
||||
}
|
||||
|
||||
/* Enqueue a packet 'm' to a queue 'q' and add timestamp to that packet.
|
||||
* Return 1 when unable to add timestamp, otherwise return 0
|
||||
*/
|
||||
static int
|
||||
codel_enqueue(struct fq_codel_flow *q, struct mbuf *m, struct fq_codel_si *si)
|
||||
{
|
||||
uint64_t len;
|
||||
|
||||
len = m->m_pkthdr.len;
|
||||
/* finding maximum packet size */
|
||||
if (len > q->cst.maxpkt_size)
|
||||
q->cst.maxpkt_size = len;
|
||||
|
||||
/* Add timestamp to mbuf as MTAG */
|
||||
struct m_tag *mtag;
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL)
|
||||
mtag = m_tag_alloc(MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, sizeof(aqm_time_t),
|
||||
M_NOWAIT);
|
||||
if (mtag == NULL) {
|
||||
m_freem(m);
|
||||
goto drop;
|
||||
}
|
||||
*(aqm_time_t *)(mtag + 1) = AQM_UNOW;
|
||||
m_tag_prepend(m, mtag);
|
||||
|
||||
mq_append(&q->mq, m);
|
||||
fq_update_stats(q, si, len, 0);
|
||||
return 0;
|
||||
|
||||
drop:
|
||||
fq_update_stats(q, si, len, 1);
|
||||
m_freem(m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Classify a packet to queue number using Jenkins hash function.
|
||||
* Return: queue number
|
||||
* the input of the hash are protocol no, perturbation, src IP, dst IP,
|
||||
* src port, dst port,
|
||||
*/
|
||||
static inline int
|
||||
fq_codel_classify_flow(struct mbuf *m, uint16_t fcount, struct fq_codel_si *si)
|
||||
{
|
||||
struct ip *ip;
|
||||
struct tcphdr *th;
|
||||
struct udphdr *uh;
|
||||
uint8_t tuple[41];
|
||||
uint16_t hash=0;
|
||||
|
||||
//#ifdef INET6
|
||||
struct ip6_hdr *ip6;
|
||||
int isip6;
|
||||
isip6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
|
||||
|
||||
if(isip6) {
|
||||
ip6 = mtod(m, struct ip6_hdr *);
|
||||
*((uint8_t *) &tuple[0]) = ip6->ip6_nxt;
|
||||
*((uint32_t *) &tuple[1]) = si->perturbation;
|
||||
memcpy(&tuple[5], ip6->ip6_src.s6_addr, 16);
|
||||
memcpy(&tuple[21], ip6->ip6_dst.s6_addr, 16);
|
||||
|
||||
switch (ip6->ip6_nxt) {
|
||||
case IPPROTO_TCP:
|
||||
th = (struct tcphdr *)(ip6 + 1);
|
||||
*((uint16_t *) &tuple[37]) = th->th_dport;
|
||||
*((uint16_t *) &tuple[39]) = th->th_sport;
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
uh = (struct udphdr *)(ip6 + 1);
|
||||
*((uint16_t *) &tuple[37]) = uh->uh_dport;
|
||||
*((uint16_t *) &tuple[39]) = uh->uh_sport;
|
||||
break;
|
||||
default:
|
||||
memset(&tuple[37], 0, 4);
|
||||
|
||||
}
|
||||
|
||||
hash = jenkins_hash(tuple, 41, HASHINIT) % fcount;
|
||||
return hash;
|
||||
}
|
||||
//#endif
|
||||
|
||||
/* IPv4 */
|
||||
ip = mtod(m, struct ip *);
|
||||
*((uint8_t *) &tuple[0]) = ip->ip_p;
|
||||
*((uint32_t *) &tuple[1]) = si->perturbation;
|
||||
*((uint32_t *) &tuple[5]) = ip->ip_src.s_addr;
|
||||
*((uint32_t *) &tuple[9]) = ip->ip_dst.s_addr;
|
||||
|
||||
switch (ip->ip_p) {
|
||||
case IPPROTO_TCP:
|
||||
th = (struct tcphdr *)(ip + 1);
|
||||
*((uint16_t *) &tuple[13]) = th->th_dport;
|
||||
*((uint16_t *) &tuple[15]) = th->th_sport;
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
uh = (struct udphdr *)(ip + 1);
|
||||
*((uint16_t *) &tuple[13]) = uh->uh_dport;
|
||||
*((uint16_t *) &tuple[15]) = uh->uh_sport;
|
||||
break;
|
||||
default:
|
||||
memset(&tuple[13], 0, 4);
|
||||
|
||||
}
|
||||
hash = jenkins_hash(tuple, 17, HASHINIT) % fcount;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue a packet into an appropriate queue according to
|
||||
* FQ_CODEL algorithm.
|
||||
*/
|
||||
static int
|
||||
fq_codel_enqueue(struct dn_sch_inst *_si, struct dn_queue *_q,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct fq_codel_si *si;
|
||||
struct fq_codel_schk *schk;
|
||||
struct dn_sch_fq_codel_parms *param;
|
||||
struct dn_queue *mainq;
|
||||
int idx, drop, i, maxidx;
|
||||
|
||||
mainq = (struct dn_queue *)(_si + 1);
|
||||
si = (struct fq_codel_si *)_si;
|
||||
schk = (struct fq_codel_schk *)(si->_si.sched+1);
|
||||
param = &schk->cfg;
|
||||
|
||||
/* classify a packet to queue number*/
|
||||
idx = fq_codel_classify_flow(m, param->flows_cnt, si);
|
||||
/* enqueue packet into appropriate queue using CoDel AQM.
|
||||
* Note: 'codel_enqueue' function returns 1 only when it unable to
|
||||
* add timestamp to packet (no limit check)*/
|
||||
drop = codel_enqueue(&si->flows[idx], m, si);
|
||||
|
||||
/* codel unable to timestamp a packet */
|
||||
if (drop)
|
||||
return 1;
|
||||
|
||||
/* If the flow (sub-queue) is not active ,then add it to the tail of
|
||||
* new flows list, initialize and activate it.
|
||||
*/
|
||||
if (!si->flows[idx].active ) {
|
||||
STAILQ_INSERT_TAIL(&si->newflows, &si->flows[idx], flowchain);
|
||||
si->flows[idx].deficit = param->quantum;
|
||||
si->flows[idx].cst.dropping = false;
|
||||
si->flows[idx].cst.first_above_time = 0;
|
||||
si->flows[idx].active = 1;
|
||||
//D("activate %d",idx);
|
||||
}
|
||||
|
||||
/* check the limit for all queues and remove a packet from the
|
||||
* largest one
|
||||
*/
|
||||
if (mainq->ni.length > schk->cfg.limit) { D("over limit");
|
||||
/* find first active flow */
|
||||
for (maxidx = 0; maxidx < schk->cfg.flows_cnt; maxidx++)
|
||||
if (si->flows[maxidx].active)
|
||||
break;
|
||||
if (maxidx < schk->cfg.flows_cnt) {
|
||||
/* find the largest sub- queue */
|
||||
for (i = maxidx + 1; i < schk->cfg.flows_cnt; i++)
|
||||
if (si->flows[i].active && si->flows[i].stats.length >
|
||||
si->flows[maxidx].stats.length)
|
||||
maxidx = i;
|
||||
codel_drop_head(&si->flows[maxidx], si);
|
||||
D("maxidx = %d",maxidx);
|
||||
drop = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return drop;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a packet from an appropriate queue according to
|
||||
* FQ_CODEL algorithm.
|
||||
*/
|
||||
static struct mbuf *
|
||||
fq_codel_dequeue(struct dn_sch_inst *_si)
|
||||
{
|
||||
struct fq_codel_si *si;
|
||||
struct fq_codel_schk *schk;
|
||||
struct dn_sch_fq_codel_parms *param;
|
||||
struct fq_codel_flow *f;
|
||||
struct mbuf *mbuf;
|
||||
struct fq_codel_list *fq_codel_flowlist;
|
||||
|
||||
si = (struct fq_codel_si *)_si;
|
||||
schk = (struct fq_codel_schk *)(si->_si.sched+1);
|
||||
param = &schk->cfg;
|
||||
|
||||
do {
|
||||
/* select a list to start with */
|
||||
if (STAILQ_EMPTY(&si->newflows))
|
||||
fq_codel_flowlist = &si->oldflows;
|
||||
else
|
||||
fq_codel_flowlist = &si->newflows;
|
||||
|
||||
/* Both new and old queue lists are empty, return NULL */
|
||||
if (STAILQ_EMPTY(fq_codel_flowlist))
|
||||
return NULL;
|
||||
|
||||
f = STAILQ_FIRST(fq_codel_flowlist);
|
||||
while (f != NULL) {
|
||||
/* if there is no flow(sub-queue) deficit, increase deficit
|
||||
* by quantum, move the flow to the tail of old flows list
|
||||
* and try another flow.
|
||||
* Otherwise, the flow will be used for dequeue.
|
||||
*/
|
||||
if (f->deficit < 0) {
|
||||
f->deficit += param->quantum;
|
||||
STAILQ_REMOVE_HEAD(fq_codel_flowlist, flowchain);
|
||||
STAILQ_INSERT_TAIL(&si->oldflows, f, flowchain);
|
||||
} else
|
||||
break;
|
||||
|
||||
f = STAILQ_FIRST(fq_codel_flowlist);
|
||||
}
|
||||
|
||||
/* the new flows list is empty, try old flows list */
|
||||
if (STAILQ_EMPTY(fq_codel_flowlist))
|
||||
continue;
|
||||
|
||||
/* Dequeue a packet from the selected flow */
|
||||
mbuf = fqc_codel_dequeue(f, si);
|
||||
|
||||
/* Codel did not return a packet */
|
||||
if (!mbuf) {
|
||||
/* If the selected flow belongs to new flows list, then move
|
||||
* it to the tail of old flows list. Otherwise, deactivate it and
|
||||
* remove it from the old list and
|
||||
*/
|
||||
if (fq_codel_flowlist == &si->newflows) {
|
||||
STAILQ_REMOVE_HEAD(fq_codel_flowlist, flowchain);
|
||||
STAILQ_INSERT_TAIL(&si->oldflows, f, flowchain);
|
||||
} else {
|
||||
f->active = 0;
|
||||
STAILQ_REMOVE_HEAD(fq_codel_flowlist, flowchain);
|
||||
}
|
||||
/* start again */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we have a packet to return,
|
||||
* update flow deficit and return the packet*/
|
||||
f->deficit -= mbuf->m_pkthdr.len;
|
||||
return mbuf;
|
||||
|
||||
} while (1);
|
||||
|
||||
/* unreachable point */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fq_codel scheduler instance.
|
||||
* also, allocate memory for flows array.
|
||||
*/
|
||||
static int
|
||||
fq_codel_new_sched(struct dn_sch_inst *_si)
|
||||
{
|
||||
struct fq_codel_si *si;
|
||||
struct dn_queue *q;
|
||||
struct fq_codel_schk *schk;
|
||||
int i;
|
||||
|
||||
si = (struct fq_codel_si *)_si;
|
||||
schk = (struct fq_codel_schk *)(_si->sched+1);
|
||||
|
||||
if(si->flows) {
|
||||
D("si already configured!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init the main queue */
|
||||
q = &si->main_q;
|
||||
set_oid(&q->ni.oid, DN_QUEUE, sizeof(*q));
|
||||
q->_si = _si;
|
||||
q->fs = _si->sched->fs;
|
||||
|
||||
/* allocate memory for flows array */
|
||||
si->flows = malloc(schk->cfg.flows_cnt * sizeof(struct fq_codel_flow),
|
||||
M_DUMMYNET, M_NOWAIT | M_ZERO);
|
||||
if (si->flows == NULL) {
|
||||
D("cannot allocate memory for fq_codel configuration parameters");
|
||||
return ENOMEM ;
|
||||
}
|
||||
|
||||
/* init perturbation for this si */
|
||||
si->perturbation = random();
|
||||
|
||||
/* init the old and new flows lists */
|
||||
STAILQ_INIT(&si->newflows);
|
||||
STAILQ_INIT(&si->oldflows);
|
||||
|
||||
/* init the flows (sub-queues) */
|
||||
for (i = 0; i < schk->cfg.flows_cnt; i++) {
|
||||
/* init codel */
|
||||
si->flows[i].cst.maxpkt_size = 500;
|
||||
}
|
||||
|
||||
fq_codel_desc.ref_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free fq_codel scheduler instance.
|
||||
*/
|
||||
static int
|
||||
fq_codel_free_sched(struct dn_sch_inst *_si)
|
||||
{
|
||||
struct fq_codel_si *si = (struct fq_codel_si *)_si ;
|
||||
|
||||
/* free the flows array */
|
||||
free(si->flows , M_DUMMYNET);
|
||||
si->flows = NULL;
|
||||
fq_codel_desc.ref_count--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure fq_codel scheduler.
|
||||
* the configurations for the scheduler is passed from userland.
|
||||
*/
|
||||
static int
|
||||
fq_codel_config(struct dn_schk *_schk)
|
||||
{
|
||||
struct fq_codel_schk *schk;
|
||||
struct dn_extra_parms *ep;
|
||||
struct dn_sch_fq_codel_parms *fqc_cfg;
|
||||
|
||||
schk = (struct fq_codel_schk *)(_schk+1);
|
||||
ep = (struct dn_extra_parms *) _schk->cfg;
|
||||
|
||||
/* par array contains fq_codel configuration as follow
|
||||
* Codel: 0- target,1- interval, 2- flags
|
||||
* FQ_CODEL: 3- quantum, 4- limit, 5- flows
|
||||
*/
|
||||
if (ep && ep->oid.len ==sizeof(*ep) &&
|
||||
ep->oid.subtype == DN_SCH_PARAMS) {
|
||||
|
||||
fqc_cfg = &schk->cfg;
|
||||
if (ep->par[0] < 0)
|
||||
fqc_cfg->ccfg.target = fq_codel_sysctl.ccfg.target;
|
||||
else
|
||||
fqc_cfg->ccfg.target = ep->par[0] * AQM_TIME_1US;
|
||||
|
||||
if (ep->par[1] < 0)
|
||||
fqc_cfg->ccfg.interval = fq_codel_sysctl.ccfg.interval;
|
||||
else
|
||||
fqc_cfg->ccfg.interval = ep->par[1] * AQM_TIME_1US;
|
||||
|
||||
if (ep->par[2] < 0)
|
||||
fqc_cfg->ccfg.flags = 0;
|
||||
else
|
||||
fqc_cfg->ccfg.flags = ep->par[2];
|
||||
|
||||
/* FQ configurations */
|
||||
if (ep->par[3] < 0)
|
||||
fqc_cfg->quantum = fq_codel_sysctl.quantum;
|
||||
else
|
||||
fqc_cfg->quantum = ep->par[3];
|
||||
|
||||
if (ep->par[4] < 0)
|
||||
fqc_cfg->limit = fq_codel_sysctl.limit;
|
||||
else
|
||||
fqc_cfg->limit = ep->par[4];
|
||||
|
||||
if (ep->par[5] < 0)
|
||||
fqc_cfg->flows_cnt = fq_codel_sysctl.flows_cnt;
|
||||
else
|
||||
fqc_cfg->flows_cnt = ep->par[5];
|
||||
|
||||
/* Bound the configurations */
|
||||
fqc_cfg->ccfg.target = BOUND_VAR(fqc_cfg->ccfg.target, 1 ,
|
||||
5 * AQM_TIME_1S); ;
|
||||
fqc_cfg->ccfg.interval = BOUND_VAR(fqc_cfg->ccfg.interval, 1,
|
||||
100 * AQM_TIME_1S);
|
||||
|
||||
fqc_cfg->quantum = BOUND_VAR(fqc_cfg->quantum,1, 9000);
|
||||
fqc_cfg->limit= BOUND_VAR(fqc_cfg->limit,1,20480);
|
||||
fqc_cfg->flows_cnt= BOUND_VAR(fqc_cfg->flows_cnt,1,65536);
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return fq_codel scheduler configurations
|
||||
* the configurations for the scheduler is passed to userland.
|
||||
*/
|
||||
static int
|
||||
fq_codel_getconfig (struct dn_schk *_schk, struct dn_extra_parms *ep) {
|
||||
|
||||
struct fq_codel_schk *schk = (struct fq_codel_schk *)(_schk+1);
|
||||
struct dn_sch_fq_codel_parms *fqc_cfg;
|
||||
|
||||
fqc_cfg = &schk->cfg;
|
||||
|
||||
strcpy(ep->name, fq_codel_desc.name);
|
||||
ep->par[0] = fqc_cfg->ccfg.target / AQM_TIME_1US;
|
||||
ep->par[1] = fqc_cfg->ccfg.interval / AQM_TIME_1US;
|
||||
ep->par[2] = fqc_cfg->ccfg.flags;
|
||||
|
||||
ep->par[3] = fqc_cfg->quantum;
|
||||
ep->par[4] = fqc_cfg->limit;
|
||||
ep->par[5] = fqc_cfg->flows_cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fq_codel scheduler descriptor
|
||||
* contains the type of the scheduler, the name, the size of extra
|
||||
* data structures, and function pointers.
|
||||
*/
|
||||
static struct dn_alg fq_codel_desc = {
|
||||
_SI( .type = ) DN_SCHED_FQ_CODEL,
|
||||
_SI( .name = ) "FQ_CODEL",
|
||||
_SI( .flags = ) 0,
|
||||
|
||||
_SI( .schk_datalen = ) sizeof(struct fq_codel_schk),
|
||||
_SI( .si_datalen = ) sizeof(struct fq_codel_si) - sizeof(struct dn_sch_inst),
|
||||
_SI( .q_datalen = ) 0,
|
||||
|
||||
_SI( .enqueue = ) fq_codel_enqueue,
|
||||
_SI( .dequeue = ) fq_codel_dequeue,
|
||||
_SI( .config = ) fq_codel_config, /* new sched i.e. sched X config ...*/
|
||||
_SI( .destroy = ) NULL, /*sched x delete */
|
||||
_SI( .new_sched = ) fq_codel_new_sched, /* new schd instance */
|
||||
_SI( .free_sched = ) fq_codel_free_sched, /* delete schd instance */
|
||||
_SI( .new_fsk = ) NULL,
|
||||
_SI( .free_fsk = ) NULL,
|
||||
_SI( .new_queue = ) NULL,
|
||||
_SI( .free_queue = ) NULL,
|
||||
_SI( .getconfig = ) fq_codel_getconfig,
|
||||
_SI( .ref_count = ) 0
|
||||
};
|
||||
|
||||
DECLARE_DNSCHED_MODULE(dn_fq_codel, &fq_codel_desc);
|
||||
165
sys/netpfil/ipfw/dn_sched_fq_codel.h
Normal file
165
sys/netpfil/ipfw/dn_sched_fq_codel.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*-
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FQ_Codel Structures and helper functions
|
||||
*/
|
||||
|
||||
#ifndef _IP_DN_SCHED_FQ_CODEL_H
|
||||
#define _IP_DN_SCHED_FQ_CODEL_H
|
||||
|
||||
/* list of queues */
|
||||
STAILQ_HEAD(fq_codel_list, fq_codel_flow) ;
|
||||
|
||||
/* fq_codel parameters including codel */
|
||||
struct dn_sch_fq_codel_parms {
|
||||
struct dn_aqm_codel_parms ccfg; /* CoDel Parameters */
|
||||
/* FQ_CODEL Parameters */
|
||||
uint32_t flows_cnt; /* number of flows */
|
||||
uint32_t limit; /* hard limit of fq_codel queue size*/
|
||||
uint32_t quantum;
|
||||
}; /* defaults */
|
||||
|
||||
/* flow (sub-queue) stats */
|
||||
struct flow_stats {
|
||||
uint64_t tot_pkts; /* statistics counters */
|
||||
uint64_t tot_bytes;
|
||||
uint32_t length; /* Queue length, in packets */
|
||||
uint32_t len_bytes; /* Queue length, in bytes */
|
||||
uint32_t drops;
|
||||
};
|
||||
|
||||
/* A flow of packets (sub-queue).*/
|
||||
struct fq_codel_flow {
|
||||
struct mq mq; /* list of packets */
|
||||
struct flow_stats stats; /* statistics */
|
||||
int deficit;
|
||||
int active; /* 1: flow is active (in a list) */
|
||||
struct codel_status cst;
|
||||
STAILQ_ENTRY(fq_codel_flow) flowchain;
|
||||
};
|
||||
|
||||
/* extra fq_codel scheduler configurations */
|
||||
struct fq_codel_schk {
|
||||
struct dn_sch_fq_codel_parms cfg;
|
||||
};
|
||||
|
||||
/* fq_codel scheduler instance */
|
||||
struct fq_codel_si {
|
||||
struct dn_sch_inst _si; /* standard scheduler instance */
|
||||
struct dn_queue main_q; /* main queue is after si directly */
|
||||
|
||||
struct fq_codel_flow *flows; /* array of flows (queues) */
|
||||
uint32_t perturbation; /* random value */
|
||||
struct fq_codel_list newflows; /* list of new queues */
|
||||
struct fq_codel_list oldflows; /* list of old queues */
|
||||
};
|
||||
|
||||
/* Helper function to update queue&main-queue and scheduler statistics.
|
||||
* negative len + drop -> drop
|
||||
* negative len -> dequeue
|
||||
* positive len -> enqueue
|
||||
* positive len + drop -> drop during enqueue
|
||||
*/
|
||||
__inline static void
|
||||
fq_update_stats(struct fq_codel_flow *q, struct fq_codel_si *si, int len,
|
||||
int drop)
|
||||
{
|
||||
int inc = 0;
|
||||
|
||||
if (len < 0)
|
||||
inc = -1;
|
||||
else if (len > 0)
|
||||
inc = 1;
|
||||
|
||||
if (drop) {
|
||||
si->main_q.ni.drops ++;
|
||||
q->stats.drops ++;
|
||||
si->_si.ni.drops ++;
|
||||
io_pkt_drop ++;
|
||||
}
|
||||
|
||||
if (!drop || (drop && len < 0)) {
|
||||
/* Update stats for the main queue */
|
||||
si->main_q.ni.length += inc;
|
||||
si->main_q.ni.len_bytes += len;
|
||||
|
||||
/*update sub-queue stats */
|
||||
q->stats.length += inc;
|
||||
q->stats.len_bytes += len;
|
||||
|
||||
/*update scheduler instance stats */
|
||||
si->_si.ni.length += inc;
|
||||
si->_si.ni.len_bytes += len;
|
||||
}
|
||||
|
||||
if (inc > 0) {
|
||||
si->main_q.ni.tot_bytes += len;
|
||||
si->main_q.ni.tot_pkts ++;
|
||||
|
||||
q->stats.tot_bytes +=len;
|
||||
q->stats.tot_pkts++;
|
||||
|
||||
si->_si.ni.tot_bytes +=len;
|
||||
si->_si.ni.tot_pkts ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* extract the head of fq_codel sub-queue */
|
||||
__inline static struct mbuf *
|
||||
fq_codel_extract_head(struct fq_codel_flow *q, aqm_time_t *pkt_ts, struct fq_codel_si *si)
|
||||
{
|
||||
struct mbuf *m = q->mq.head;
|
||||
|
||||
if (m == NULL)
|
||||
return m;
|
||||
q->mq.head = m->m_nextpkt;
|
||||
|
||||
fq_update_stats(q, si, -m->m_pkthdr.len, 0);
|
||||
|
||||
if (si->main_q.ni.length == 0) /* queue is now idle */
|
||||
si->main_q.q_time = dn_cfg.curr_time;
|
||||
|
||||
/* extract packet timestamp*/
|
||||
struct m_tag *mtag;
|
||||
mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
|
||||
if (mtag == NULL){
|
||||
D("timestamp tag is not found!");
|
||||
*pkt_ts = 0;
|
||||
} else {
|
||||
*pkt_ts = *(aqm_time_t *)(mtag + 1);
|
||||
m_tag_delete(m,mtag);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
185
sys/netpfil/ipfw/dn_sched_fq_codel_helper.h
Normal file
185
sys/netpfil/ipfw/dn_sched_fq_codel_helper.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Codel - The Controlled-Delay Active Queue Management algorithm.
|
||||
*
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Copyright (C) 2011-2014 Kathleen Nichols <nichols@pollere.com>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* o The names of the authors may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* Alternatively, provided that this notice is retained in full, this
|
||||
* software may be distributed under the terms of the GNU General Public
|
||||
* License ("GPL") version 2, in which case the provisions of the GPL
|
||||
* apply INSTEAD OF those given above.
|
||||
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _IP_DN_SCHED_FQ_CODEL_HELPER_H
|
||||
#define _IP_DN_SCHED_FQ_CODEL_HELPER_H
|
||||
|
||||
__inline static struct mbuf *
|
||||
fqc_dodequeue(struct fq_codel_flow *q, aqm_time_t now, uint16_t *ok_to_drop,
|
||||
struct fq_codel_si *si)
|
||||
{
|
||||
struct mbuf * m;
|
||||
struct fq_codel_schk *schk = (struct fq_codel_schk *)(si->_si.sched+1);
|
||||
aqm_time_t pkt_ts, sojourn_time;
|
||||
|
||||
*ok_to_drop = 0;
|
||||
m = fq_codel_extract_head(q, &pkt_ts, si);
|
||||
|
||||
if (m == NULL) {
|
||||
/*queue is empty - we can't be above target*/
|
||||
q->cst.first_above_time= 0;
|
||||
return m;
|
||||
}
|
||||
|
||||
/* To span a large range of bandwidths, CoDel runs two
|
||||
* different AQMs in parallel. One is sojourn-time-based
|
||||
* and takes effect when the time to send an MTU-sized
|
||||
* packet is less than target. The 1st term of the "if"
|
||||
* below does this. The other is backlog-based and takes
|
||||
* effect when the time to send an MTU-sized packet is >=
|
||||
* target. The goal here is to keep the output link
|
||||
* utilization high by never allowing the queue to get
|
||||
* smaller than the amount that arrives in a typical
|
||||
* interarrival time (MTU-sized packets arriving spaced
|
||||
* by the amount of time it takes to send such a packet on
|
||||
* the bottleneck). The 2nd term of the "if" does this.
|
||||
*/
|
||||
sojourn_time = now - pkt_ts;
|
||||
if (sojourn_time < schk->cfg.ccfg.target || q->stats.len_bytes <= q->cst.maxpkt_size) {
|
||||
/* went below - stay below for at least interval */
|
||||
q->cst.first_above_time = 0;
|
||||
} else {
|
||||
if (q->cst.first_above_time == 0) {
|
||||
/* just went above from below. if still above at
|
||||
* first_above_time, will say it's ok to drop. */
|
||||
q->cst.first_above_time = now + schk->cfg.ccfg.interval;
|
||||
} else if (now >= q->cst.first_above_time) {
|
||||
*ok_to_drop = 1;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Codel dequeue function */
|
||||
__inline static struct mbuf *
|
||||
fqc_codel_dequeue(struct fq_codel_flow *q, struct fq_codel_si *si)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct dn_aqm_codel_parms *cprms;
|
||||
struct codel_status *cst;
|
||||
aqm_time_t now;
|
||||
uint16_t ok_to_drop;
|
||||
struct fq_codel_schk *schk = (struct fq_codel_schk *)(si->_si.sched+1);
|
||||
|
||||
cst = &q->cst;
|
||||
cprms = &schk->cfg.ccfg;
|
||||
|
||||
now = AQM_UNOW;
|
||||
m = fqc_dodequeue(q, now, &ok_to_drop, si);
|
||||
|
||||
if (cst->dropping) {
|
||||
if (!ok_to_drop) {
|
||||
/* sojourn time below target - leave dropping state */
|
||||
cst->dropping = false;
|
||||
}
|
||||
|
||||
/* Time for the next drop. Drop current packet and dequeue
|
||||
* next. If the dequeue doesn't take us out of dropping
|
||||
* state, schedule the next drop. A large backlog might
|
||||
* result in drop rates so high that the next drop should
|
||||
* happen now, hence the 'while' loop.
|
||||
*/
|
||||
while (now >= cst->drop_next_time && cst->dropping) {
|
||||
|
||||
/* mark the packet */
|
||||
if (cprms->flags & CODEL_ECN_ENABLED && ecn_mark(m)) {
|
||||
cst->count++;
|
||||
/* schedule the next mark. */
|
||||
cst->drop_next_time = control_law(cst, cprms, cst->drop_next_time);
|
||||
return m;
|
||||
}
|
||||
|
||||
/* drop the packet */
|
||||
fq_update_stats(q, si, 0, 1);
|
||||
m_freem(m);
|
||||
m = fqc_dodequeue(q, now, &ok_to_drop, si);
|
||||
|
||||
if (!ok_to_drop) {
|
||||
/* leave dropping state */
|
||||
cst->dropping = false;
|
||||
} else {
|
||||
cst->count++;
|
||||
/* schedule the next drop. */
|
||||
cst->drop_next_time = control_law(cst, cprms, cst->drop_next_time);
|
||||
}
|
||||
}
|
||||
/* If we get here we're not in dropping state. The 'ok_to_drop'
|
||||
* return from dodequeue means that the sojourn time has been
|
||||
* above 'target' for 'interval' so enter dropping state.
|
||||
*/
|
||||
} else if (ok_to_drop) {
|
||||
|
||||
/* if ECN option is disabled or the packet cannot be marked,
|
||||
* drop the packet and extract another.
|
||||
*/
|
||||
if (!(cprms->flags & CODEL_ECN_ENABLED) || !ecn_mark(m)) {
|
||||
fq_update_stats(q, si, 0, 1);
|
||||
m_freem(m);
|
||||
m = fqc_dodequeue(q, now, &ok_to_drop,si);
|
||||
}
|
||||
|
||||
cst->dropping = true;
|
||||
|
||||
/* If min went above target close to when it last went
|
||||
* below, assume that the drop rate that controlled the
|
||||
* queue on the last cycle is a good starting point to
|
||||
* control it now. ('drop_next' will be at most 'interval'
|
||||
* later than the time of the last drop so 'now - drop_next'
|
||||
* is a good approximation of the time from the last drop
|
||||
* until now.)
|
||||
*/
|
||||
cst->count = (cst->count > 2 && ((aqm_stime_t)now -
|
||||
(aqm_stime_t)cst->drop_next_time) < 8* cprms->interval)? cst->count - 2 : 1;
|
||||
|
||||
/* we don't have to set initial guess for Newton's method isqrt as
|
||||
* we initilaize isqrt in control_law function when count == 1 */
|
||||
cst->drop_next_time = control_law(cst, cprms, now);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
#endif
|
||||
1260
sys/netpfil/ipfw/dn_sched_fq_pie.c
Normal file
1260
sys/netpfil/ipfw/dn_sched_fq_pie.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -41,6 +41,9 @@
|
|||
#include <netinet/ip_dummynet.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
|
|
@ -223,6 +226,9 @@ static struct dn_alg prio_desc = {
|
|||
|
||||
_SI( .new_queue = ) prio_new_queue,
|
||||
_SI( .free_queue = ) prio_free_queue,
|
||||
#ifdef NEW_AQM
|
||||
_SI( .getconfig = ) NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
#include <netinet/ip_dummynet.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
|
|
@ -824,6 +827,9 @@ static struct dn_alg qfq_desc = {
|
|||
_SI( .free_fsk = ) NULL,
|
||||
_SI( .new_queue = ) qfq_new_queue,
|
||||
_SI( .free_queue = ) qfq_free_queue,
|
||||
#ifdef NEW_AQM
|
||||
_SI( .getconfig = ) NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
DECLARE_DNSCHED_MODULE(dn_qfq, &qfq_desc);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
#include <netinet/ip_dummynet.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
|
|
@ -301,6 +304,9 @@ static struct dn_alg rr_desc = {
|
|||
_SI( .free_fsk = ) NULL,
|
||||
_SI( .new_queue = ) rr_new_queue,
|
||||
_SI( .free_queue = ) rr_free_queue,
|
||||
#ifdef NEW_AQM
|
||||
_SI( .getconfig = ) NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
#include <netinet/ip_dummynet.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
#else
|
||||
#include <dn_test.h>
|
||||
|
|
@ -367,6 +370,10 @@ static struct dn_alg wf2qp_desc = {
|
|||
|
||||
_SI( .new_queue = ) wf2qp_new_queue,
|
||||
_SI( .free_queue = ) wf2qp_free_queue,
|
||||
#ifdef NEW_AQM
|
||||
_SI( .getconfig = ) NULL,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@
|
|||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
|
||||
/* FREEBSD7.2 ip_dummynet.h r191715*/
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ __FBSDID("$FreeBSD$");
|
|||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
|
||||
/*
|
||||
|
|
@ -83,8 +86,12 @@ static long tick_diff;
|
|||
|
||||
static unsigned long io_pkt;
|
||||
static unsigned long io_pkt_fast;
|
||||
static unsigned long io_pkt_drop;
|
||||
|
||||
#ifdef NEW_AQM
|
||||
unsigned long io_pkt_drop;
|
||||
#else
|
||||
static unsigned long io_pkt_drop;
|
||||
#endif
|
||||
/*
|
||||
* We use a heap to store entities for which we have pending timer events.
|
||||
* The heap is checked at every tick and all entities with expired events
|
||||
|
|
@ -147,7 +154,11 @@ SYSBEGIN(f4)
|
|||
|
||||
SYSCTL_DECL(_net_inet);
|
||||
SYSCTL_DECL(_net_inet_ip);
|
||||
#ifdef NEW_AQM
|
||||
SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
|
||||
#else
|
||||
static SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
|
||||
#endif
|
||||
|
||||
/* wrapper to pass dn_cfg fields to SYSCTL_* */
|
||||
//#define DC(x) (&(VNET_NAME(_base_dn_cfg).x))
|
||||
|
|
@ -249,6 +260,14 @@ static struct dn_pkt_tag *
|
|||
dn_tag_get(struct mbuf *m)
|
||||
{
|
||||
struct m_tag *mtag = m_tag_first(m);
|
||||
#ifdef NEW_AQM
|
||||
/* XXX: to skip ts m_tag. For Debugging only*/
|
||||
if (mtag != NULL && mtag->m_tag_id == DN_AQM_MTAG_TS) {
|
||||
m_tag_delete(m,mtag);
|
||||
mtag = m_tag_first(m);
|
||||
D("skip TS tag");
|
||||
}
|
||||
#endif
|
||||
KASSERT(mtag != NULL &&
|
||||
mtag->m_tag_cookie == MTAG_ABI_COMPAT &&
|
||||
mtag->m_tag_id == PACKET_TAG_DUMMYNET,
|
||||
|
|
@ -256,6 +275,7 @@ dn_tag_get(struct mbuf *m)
|
|||
return (struct dn_pkt_tag *)(mtag+1);
|
||||
}
|
||||
|
||||
#ifndef NEW_AQM
|
||||
static inline void
|
||||
mq_append(struct mq *q, struct mbuf *m)
|
||||
{
|
||||
|
|
@ -266,6 +286,7 @@ mq_append(struct mq *q, struct mbuf *m)
|
|||
q->tail = m;
|
||||
m->m_nextpkt = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dispose a list of packet. Use a functions so if we need to do
|
||||
|
|
@ -337,6 +358,8 @@ red_drops (struct dn_queue *q, int len)
|
|||
return (0); /* accept packet */
|
||||
}
|
||||
if (q->avg >= fs->max_th) { /* average queue >= max threshold */
|
||||
if (fs->fs.flags & DN_IS_ECN)
|
||||
return (1);
|
||||
if (fs->fs.flags & DN_IS_GENTLE_RED) {
|
||||
/*
|
||||
* According to Gentle-RED, if avg is greater than
|
||||
|
|
@ -352,6 +375,8 @@ red_drops (struct dn_queue *q, int len)
|
|||
return (1);
|
||||
}
|
||||
} else if (q->avg > fs->min_th) {
|
||||
if (fs->fs.flags & DN_IS_ECN)
|
||||
return (1);
|
||||
/*
|
||||
* We compute p_b using the linear dropping function
|
||||
* p_b = c_1 * avg - c_2
|
||||
|
|
@ -383,6 +408,73 @@ red_drops (struct dn_queue *q, int len)
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* ECN/ECT Processing (partially adopted from altq)
|
||||
*/
|
||||
#ifndef NEW_AQM
|
||||
static
|
||||
#endif
|
||||
int
|
||||
ecn_mark(struct mbuf* m)
|
||||
{
|
||||
struct ip *ip;
|
||||
ip = mtod(m, struct ip *);
|
||||
|
||||
switch (ip->ip_v) {
|
||||
case IPVERSION:
|
||||
{
|
||||
u_int8_t otos;
|
||||
int sum;
|
||||
|
||||
if ((ip->ip_tos & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
|
||||
return (0); /* not-ECT */
|
||||
if ((ip->ip_tos & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
|
||||
return (1); /* already marked */
|
||||
|
||||
/*
|
||||
* ecn-capable but not marked,
|
||||
* mark CE and update checksum
|
||||
*/
|
||||
otos = ip->ip_tos;
|
||||
ip->ip_tos |= IPTOS_ECN_CE;
|
||||
/*
|
||||
* update checksum (from RFC1624)
|
||||
* HC' = ~(~HC + ~m + m')
|
||||
*/
|
||||
sum = ~ntohs(ip->ip_sum) & 0xffff;
|
||||
sum += (~otos & 0xffff) + ip->ip_tos;
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16); /* add carry */
|
||||
ip->ip_sum = htons(~sum & 0xffff);
|
||||
return (1);
|
||||
}
|
||||
#ifdef INET6
|
||||
case (IPV6_VERSION >> 4):
|
||||
{
|
||||
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
|
||||
u_int32_t flowlabel;
|
||||
|
||||
flowlabel = ntohl(ip6->ip6_flow);
|
||||
if ((flowlabel >> 28) != 6)
|
||||
return (0); /* version mismatch! */
|
||||
if ((flowlabel & (IPTOS_ECN_MASK << 20)) ==
|
||||
(IPTOS_ECN_NOTECT << 20))
|
||||
return (0); /* not-ECT */
|
||||
if ((flowlabel & (IPTOS_ECN_MASK << 20)) ==
|
||||
(IPTOS_ECN_CE << 20))
|
||||
return (1); /* already marked */
|
||||
/*
|
||||
* ecn-capable but not marked, mark CE
|
||||
*/
|
||||
flowlabel |= (IPTOS_ECN_CE << 20);
|
||||
ip6->ip6_flow = htonl(flowlabel);
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue a packet in q, subject to space and queue management policy
|
||||
* (whose parameters are in q->fs).
|
||||
|
|
@ -414,8 +506,15 @@ dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop)
|
|||
goto drop;
|
||||
if (f->plr && random() < f->plr)
|
||||
goto drop;
|
||||
if (f->flags & DN_IS_RED && red_drops(q, m->m_pkthdr.len))
|
||||
#ifdef NEW_AQM
|
||||
/* Call AQM enqueue function */
|
||||
if (q->fs->aqmfp)
|
||||
return q->fs->aqmfp->enqueue(q ,m);
|
||||
#endif
|
||||
if (f->flags & DN_IS_RED && red_drops(q, m->m_pkthdr.len)) {
|
||||
if (!(f->flags & DN_IS_ECN) || !ecn_mark(m))
|
||||
goto drop;
|
||||
}
|
||||
if (f->flags & DN_QSIZE_BYTES) {
|
||||
if (q->ni.len_bytes > f->qsize)
|
||||
goto drop;
|
||||
|
|
@ -794,6 +893,10 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa)
|
|||
if (fs->sched->fp->enqueue(si, q, m)) {
|
||||
/* packet was dropped by enqueue() */
|
||||
m = *m0 = NULL;
|
||||
|
||||
/* dn_enqueue already increases io_pkt_drop */
|
||||
io_pkt_drop--;
|
||||
|
||||
goto dropit;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ SLIST_HEAD(dn_fsk_head, dn_fsk);
|
|||
SLIST_HEAD(dn_queue_head, dn_queue);
|
||||
SLIST_HEAD(dn_alg_head, dn_alg);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
SLIST_HEAD(dn_aqm_head, dn_aqm); /* for new AQMs */
|
||||
#endif
|
||||
|
||||
struct mq { /* a basic queue of packets*/
|
||||
struct mbuf *head, *tail;
|
||||
};
|
||||
|
|
@ -135,6 +139,9 @@ struct dn_parms {
|
|||
/* list of flowsets without a scheduler -- use sch_chain */
|
||||
struct dn_fsk_head fsu; /* list of unlinked flowsets */
|
||||
struct dn_alg_head schedlist; /* list of algorithms */
|
||||
#ifdef NEW_AQM
|
||||
struct dn_aqm_head aqmlist; /* list of AQMs */
|
||||
#endif
|
||||
|
||||
/* Store the fs/sch to scan when draining. The value is the
|
||||
* bucket number of the hash table. Expire can be disabled
|
||||
|
|
@ -231,6 +238,10 @@ struct dn_fsk { /* kernel side of a flowset */
|
|||
int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
|
||||
int avg_pkt_size ; /* medium packet size */
|
||||
int max_pkt_size ; /* max packet size */
|
||||
#ifdef NEW_AQM
|
||||
struct dn_aqm *aqmfp; /* Pointer to AQM functions */
|
||||
void *aqmcfg; /* configuration parameters for AQM */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -253,6 +264,9 @@ struct dn_queue {
|
|||
int count; /* arrivals since last RED drop */
|
||||
int random; /* random value (scaled) */
|
||||
uint64_t q_time; /* start of queue idle time */
|
||||
#ifdef NEW_AQM
|
||||
void *aqm_status; /* per-queue status variables*/
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -400,4 +414,20 @@ int do_config(void *p, int l);
|
|||
void dn_drain_scheduler(void);
|
||||
void dn_drain_queue(void);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
int ecn_mark(struct mbuf* m);
|
||||
|
||||
/* moved from ip_dn_io.c to here to be available for AQMs modules*/
|
||||
static inline void
|
||||
mq_append(struct mq *q, struct mbuf *m)
|
||||
{
|
||||
if (q->head == NULL)
|
||||
q->head = m;
|
||||
else
|
||||
q->tail->m_nextpkt = m;
|
||||
q->tail = m;
|
||||
m->m_nextpkt = NULL;
|
||||
}
|
||||
#endif /* NEW_AQM */
|
||||
|
||||
#endif /* _IP_DN_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
/*-
|
||||
* Codel/FQ_Codel and PIE/FQ-PIE Code:
|
||||
* Copyright (C) 2016 Centre for Advanced Internet Architectures,
|
||||
* Swinburne University of Technology, Melbourne, Australia.
|
||||
* Portions of this code were made possible in part by a gift from
|
||||
* The Comcast Innovation Fund.
|
||||
* Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
|
||||
*
|
||||
* Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa
|
||||
* Portions Copyright (c) 2000 Akamba Corp.
|
||||
* All rights reserved
|
||||
|
|
@ -57,6 +64,9 @@ __FBSDID("$FreeBSD$");
|
|||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/ipfw/dn_heap.h>
|
||||
#include <netpfil/ipfw/ip_dn_private.h>
|
||||
#ifdef NEW_AQM
|
||||
#include <netpfil/ipfw/dn_aqm.h>
|
||||
#endif
|
||||
#include <netpfil/ipfw/dn_sched.h>
|
||||
|
||||
/* which objects to copy */
|
||||
|
|
@ -97,6 +107,21 @@ dn_reschedule(void)
|
|||
}
|
||||
/*----- end of callout hooks -----*/
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* Return AQM descriptor for given type or name. */
|
||||
static struct dn_aqm *
|
||||
find_aqm_type(int type, char *name)
|
||||
{
|
||||
struct dn_aqm *d;
|
||||
|
||||
SLIST_FOREACH(d, &dn_cfg.aqmlist, next) {
|
||||
if (d->type == type || (name && !strcasecmp(d->name, name)))
|
||||
return d;
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return a scheduler descriptor given the type or name. */
|
||||
static struct dn_alg *
|
||||
find_sched_type(int type, char *name)
|
||||
|
|
@ -319,7 +344,15 @@ q_new(uintptr_t key, int flags, void *arg)
|
|||
|
||||
if (fs->sched->fp->new_queue)
|
||||
fs->sched->fp->new_queue(q);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* call AQM init function after creating a queue*/
|
||||
if (fs->aqmfp && fs->aqmfp->init)
|
||||
if(fs->aqmfp->init(q))
|
||||
D("unable to init AQM for fs %d", fs->fs.fs_nr);
|
||||
#endif
|
||||
dn_cfg.queue_count++;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
|
@ -333,6 +366,13 @@ dn_delete_queue(struct dn_queue *q, int flags)
|
|||
{
|
||||
struct dn_fsk *fs = q->fs;
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* clean up AQM status for queue 'q'
|
||||
* cleanup here is called just with MULTIQUEUE
|
||||
*/
|
||||
if (fs && fs->aqmfp && fs->aqmfp->cleanup)
|
||||
fs->aqmfp->cleanup(q);
|
||||
#endif
|
||||
// D("fs %p si %p\n", fs, q->_si);
|
||||
/* notify the parent scheduler that the queue is going away */
|
||||
if (fs && fs->sched->fp->free_queue)
|
||||
|
|
@ -474,6 +514,16 @@ si_new(uintptr_t key, int flags, void *arg)
|
|||
if (s->sch.flags & DN_HAVE_MASK)
|
||||
si->ni.fid = *(struct ipfw_flow_id *)key;
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* init AQM status for !DN_MULTIQUEUE sched*/
|
||||
if (!(s->fp->flags & DN_MULTIQUEUE))
|
||||
if (s->fs->aqmfp && s->fs->aqmfp->init)
|
||||
if(s->fs->aqmfp->init((struct dn_queue *)(si + 1))) {
|
||||
D("unable to init AQM for fs %d", s->fs->fs.fs_nr);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
dn_cfg.si_count++;
|
||||
return si;
|
||||
|
||||
|
|
@ -503,6 +553,20 @@ si_destroy(void *_si, void *arg)
|
|||
dn_free_pkts(dl->mq.head); /* drain delay line */
|
||||
if (si->kflags & DN_ACTIVE) /* remove si from event heap */
|
||||
heap_extract(&dn_cfg.evheap, si);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* clean up AQM status for !DN_MULTIQUEUE sched
|
||||
* Note that all queues belong to fs were cleaned up in fsk_detach.
|
||||
* When drain_scheduler is called s->fs and q->fs are pointing
|
||||
* to a correct fs, so we can use fs in this case.
|
||||
*/
|
||||
if (!(s->fp->flags & DN_MULTIQUEUE)) {
|
||||
struct dn_queue *q = (struct dn_queue *)(si + 1);
|
||||
if (q->aqm_status && q->fs->aqmfp)
|
||||
if (q->fs->aqmfp->cleanup)
|
||||
q->fs->aqmfp->cleanup(q);
|
||||
}
|
||||
#endif
|
||||
if (s->fp->free_sched)
|
||||
s->fp->free_sched(si);
|
||||
bzero(si, sizeof(*si)); /* safety */
|
||||
|
|
@ -591,6 +655,67 @@ fsk_new(uintptr_t key, int flags, void *arg)
|
|||
return fs;
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* callback function for cleaning up AQM queue status belongs to a flowset
|
||||
* connected to scheduler instance '_si' (for !DN_MULTIQUEUE only).
|
||||
*/
|
||||
static int
|
||||
si_cleanup_q(void *_si, void *arg)
|
||||
{
|
||||
struct dn_sch_inst *si = _si;
|
||||
|
||||
if (!(si->sched->fp->flags & DN_MULTIQUEUE)) {
|
||||
if (si->sched->fs->aqmfp && si->sched->fs->aqmfp->cleanup)
|
||||
si->sched->fs->aqmfp->cleanup((struct dn_queue *) (si+1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback to clean up queue AQM status.*/
|
||||
static int
|
||||
q_cleanup_q(void *_q, void *arg)
|
||||
{
|
||||
struct dn_queue *q = _q;
|
||||
q->fs->aqmfp->cleanup(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up all AQM queues status belongs to flowset 'fs' and then
|
||||
* deconfig AQM for flowset 'fs'
|
||||
*/
|
||||
static void
|
||||
aqm_cleanup_deconfig_fs(struct dn_fsk *fs)
|
||||
{
|
||||
struct dn_sch_inst *si;
|
||||
|
||||
/* clean up AQM status for all queues for !DN_MULTIQUEUE sched*/
|
||||
if (fs->fs.fs_nr > DN_MAX_ID) {
|
||||
if (fs->sched && !(fs->sched->fp->flags & DN_MULTIQUEUE)) {
|
||||
if (fs->sched->sch.flags & DN_HAVE_MASK)
|
||||
dn_ht_scan(fs->sched->siht, si_cleanup_q, NULL);
|
||||
else {
|
||||
/* single si i.e. no sched mask */
|
||||
si = (struct dn_sch_inst *) fs->sched->siht;
|
||||
if (si && fs->aqmfp && fs->aqmfp->cleanup)
|
||||
fs->aqmfp->cleanup((struct dn_queue *) (si+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up AQM status for all queues for DN_MULTIQUEUE sched*/
|
||||
if (fs->sched && fs->sched->fp->flags & DN_MULTIQUEUE && fs->qht) {
|
||||
if (fs->fs.flags & DN_QHT_HASH)
|
||||
dn_ht_scan(fs->qht, q_cleanup_q, NULL);
|
||||
else
|
||||
fs->aqmfp->cleanup((struct dn_queue *)(fs->qht));
|
||||
}
|
||||
|
||||
/* deconfig AQM */
|
||||
if(fs->aqmcfg && fs->aqmfp && fs->aqmfp->deconfig)
|
||||
fs->aqmfp->deconfig(fs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* detach flowset from its current scheduler. Flags as follows:
|
||||
* DN_DETACH removes from the fsk_list
|
||||
|
|
@ -619,6 +744,10 @@ fsk_detach(struct dn_fsk *fs, int flags)
|
|||
free(fs->w_q_lookup, M_DUMMYNET);
|
||||
fs->w_q_lookup = NULL;
|
||||
qht_delete(fs, flags);
|
||||
#ifdef NEW_AQM
|
||||
aqm_cleanup_deconfig_fs(fs);
|
||||
#endif
|
||||
|
||||
if (fs->sched && fs->sched->fp->free_fsk)
|
||||
fs->sched->fp->free_fsk(fs);
|
||||
fs->sched = NULL;
|
||||
|
|
@ -1187,6 +1316,183 @@ update_fs(struct dn_schk *s)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* Retrieve AQM configurations to ipfw userland
|
||||
*/
|
||||
static int
|
||||
get_aqm_parms(struct sockopt *sopt)
|
||||
{
|
||||
struct dn_extra_parms *ep;
|
||||
struct dn_fsk *fs;
|
||||
size_t sopt_valsize;
|
||||
int l, err = 0;
|
||||
|
||||
sopt_valsize = sopt->sopt_valsize;
|
||||
l = sizeof(*ep);
|
||||
if (sopt->sopt_valsize < l) {
|
||||
D("bad len sopt->sopt_valsize %d len %d",
|
||||
(int) sopt->sopt_valsize , l);
|
||||
err = EINVAL;
|
||||
return err;
|
||||
}
|
||||
ep = malloc(l, M_DUMMYNET, M_WAITOK);
|
||||
if(!ep) {
|
||||
err = ENOMEM ;
|
||||
return err;
|
||||
}
|
||||
do {
|
||||
err = sooptcopyin(sopt, ep, l, l);
|
||||
if(err)
|
||||
break;
|
||||
sopt->sopt_valsize = sopt_valsize;
|
||||
if (ep->oid.len < l) {
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
fs = dn_ht_find(dn_cfg.fshash, ep->nr, 0, NULL);
|
||||
if (!fs) {
|
||||
D("fs %d not found", ep->nr);
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs->aqmfp && fs->aqmfp->getconfig) {
|
||||
if(fs->aqmfp->getconfig(fs, ep)) {
|
||||
D("Error while trying to get AQM params");
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
ep->oid.len = l;
|
||||
err = sooptcopyout(sopt, ep, l);
|
||||
}
|
||||
}while(0);
|
||||
|
||||
free(ep, M_DUMMYNET);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Retrieve AQM configurations to ipfw userland
|
||||
*/
|
||||
static int
|
||||
get_sched_parms(struct sockopt *sopt)
|
||||
{
|
||||
struct dn_extra_parms *ep;
|
||||
struct dn_schk *schk;
|
||||
size_t sopt_valsize;
|
||||
int l, err = 0;
|
||||
|
||||
sopt_valsize = sopt->sopt_valsize;
|
||||
l = sizeof(*ep);
|
||||
if (sopt->sopt_valsize < l) {
|
||||
D("bad len sopt->sopt_valsize %d len %d",
|
||||
(int) sopt->sopt_valsize , l);
|
||||
err = EINVAL;
|
||||
return err;
|
||||
}
|
||||
ep = malloc(l, M_DUMMYNET, M_WAITOK);
|
||||
if(!ep) {
|
||||
err = ENOMEM ;
|
||||
return err;
|
||||
}
|
||||
do {
|
||||
err = sooptcopyin(sopt, ep, l, l);
|
||||
if(err)
|
||||
break;
|
||||
sopt->sopt_valsize = sopt_valsize;
|
||||
if (ep->oid.len < l) {
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
schk = locate_scheduler(ep->nr);
|
||||
if (!schk) {
|
||||
D("sched %d not found", ep->nr);
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (schk->fp && schk->fp->getconfig) {
|
||||
if(schk->fp->getconfig(schk, ep)) {
|
||||
D("Error while trying to get sched params");
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
ep->oid.len = l;
|
||||
err = sooptcopyout(sopt, ep, l);
|
||||
}
|
||||
}while(0);
|
||||
free(ep, M_DUMMYNET);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Configure AQM for flowset 'fs'.
|
||||
* extra parameters are passed from userland.
|
||||
*/
|
||||
static int
|
||||
config_aqm(struct dn_fsk *fs, struct dn_extra_parms *ep, int busy)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
do {
|
||||
/* no configurations */
|
||||
if (!ep) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* no AQM for this flowset*/
|
||||
if (!strcmp(ep->name,"")) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
if (ep->oid.len < sizeof(*ep)) {
|
||||
D("short aqm len %d", ep->oid.len);
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (busy) {
|
||||
D("Unable to configure flowset, flowset busy!");
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* deconfigure old aqm if exist */
|
||||
if (fs->aqmcfg && fs->aqmfp && fs->aqmfp->deconfig) {
|
||||
aqm_cleanup_deconfig_fs(fs);
|
||||
}
|
||||
|
||||
if (!(fs->aqmfp = find_aqm_type(0, ep->name))) {
|
||||
D("AQM functions not found for type %s!", ep->name);
|
||||
fs->fs.flags &= ~DN_IS_AQM;
|
||||
err = EINVAL;
|
||||
break;
|
||||
} else
|
||||
fs->fs.flags |= DN_IS_AQM;
|
||||
|
||||
if (ep->oid.subtype != DN_AQM_PARAMS) {
|
||||
D("Wrong subtype");
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs->aqmfp->config) {
|
||||
err = fs->aqmfp->config(fs, ep, ep->oid.len);
|
||||
if (err) {
|
||||
D("Unable to configure AQM for FS %d", fs->fs.fs_nr );
|
||||
fs->fs.flags &= ~DN_IS_AQM;
|
||||
fs->aqmfp = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(0);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Configuration -- to preserve backward compatibility we use
|
||||
* the following scheme (N is 65536)
|
||||
|
|
@ -1319,6 +1625,14 @@ config_fs(struct dn_fs *nfs, struct dn_id *arg, int locked)
|
|||
}
|
||||
if (bcmp(&fs->fs, nfs, sizeof(*nfs)) == 0) {
|
||||
ND("flowset %d unchanged", i);
|
||||
#ifdef NEW_AQM
|
||||
/* reconfigure AQM as the parameters can be changed.
|
||||
* we consider the flowsetis busy if it has scheduler instance(s)
|
||||
*/
|
||||
s = locate_scheduler(nfs->sched_nr);
|
||||
config_aqm(fs, (struct dn_extra_parms *) arg,
|
||||
s != NULL && s->siht != NULL);
|
||||
#endif
|
||||
break; /* no change, nothing to do */
|
||||
}
|
||||
if (oldc != dn_cfg.fsk_count) /* new item */
|
||||
|
|
@ -1337,6 +1651,10 @@ config_fs(struct dn_fs *nfs, struct dn_id *arg, int locked)
|
|||
fsk_detach(fs, flags);
|
||||
}
|
||||
fs->fs = *nfs; /* copy configuration */
|
||||
#ifdef NEW_AQM
|
||||
fs->aqmfp = NULL;
|
||||
config_aqm(fs, (struct dn_extra_parms *) arg, s != NULL && s->siht != NULL);
|
||||
#endif
|
||||
if (s != NULL)
|
||||
fsk_attach(fs, s);
|
||||
} while (0);
|
||||
|
|
@ -1862,6 +2180,19 @@ dummynet_get(struct sockopt *sopt, void **compat)
|
|||
// cmd->id = sopt_valsize;
|
||||
D("compatibility mode");
|
||||
}
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* get AQM params */
|
||||
if(cmd->subtype == DN_AQM_PARAMS) {
|
||||
error = get_aqm_parms(sopt);
|
||||
goto done;
|
||||
/* get Scheduler params */
|
||||
} else if (cmd->subtype == DN_SCH_PARAMS) {
|
||||
error = get_sched_parms(sopt);
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
a.extra = (struct copy_range *)cmd;
|
||||
if (cmd->len == sizeof(*cmd)) { /* no range, create a default */
|
||||
uint32_t *rp = (uint32_t *)(cmd + 1);
|
||||
|
|
@ -2314,4 +2645,98 @@ MODULE_VERSION(dummynet, 3);
|
|||
*/
|
||||
//VNET_SYSUNINIT(vnet_dn_uninit, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_destroy, NULL);
|
||||
|
||||
#ifdef NEW_AQM
|
||||
|
||||
/* modevent helpers for the AQM modules */
|
||||
static int
|
||||
load_dn_aqm(struct dn_aqm *d)
|
||||
{
|
||||
struct dn_aqm *aqm=NULL;
|
||||
|
||||
if (d == NULL)
|
||||
return 1; /* error */
|
||||
ip_dn_init(); /* just in case, we need the lock */
|
||||
|
||||
/* Check that mandatory funcs exists */
|
||||
if (d->enqueue == NULL || d->dequeue == NULL) {
|
||||
D("missing enqueue or dequeue for %s", d->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Search if AQM already exists */
|
||||
DN_BH_WLOCK();
|
||||
SLIST_FOREACH(aqm, &dn_cfg.aqmlist, next) {
|
||||
if (strcmp(aqm->name, d->name) == 0) {
|
||||
D("%s already loaded", d->name);
|
||||
break; /* AQM already exists */
|
||||
}
|
||||
}
|
||||
if (aqm == NULL)
|
||||
SLIST_INSERT_HEAD(&dn_cfg.aqmlist, d, next);
|
||||
DN_BH_WUNLOCK();
|
||||
D("dn_aqm %s %sloaded", d->name, aqm ? "not ":"");
|
||||
return aqm ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/* Callback to clean up AQM status for queues connected to a flowset
|
||||
* and then deconfigure the flowset.
|
||||
* This function is called before an AQM module is unloaded
|
||||
*/
|
||||
static int
|
||||
fs_cleanup(void *_fs, void *arg)
|
||||
{
|
||||
struct dn_fsk *fs = _fs;
|
||||
uint32_t type = *(uint32_t *)arg;
|
||||
|
||||
if (fs->aqmfp && fs->aqmfp->type == type)
|
||||
aqm_cleanup_deconfig_fs(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
unload_dn_aqm(struct dn_aqm *aqm)
|
||||
{
|
||||
struct dn_aqm *tmp, *r;
|
||||
int err = EINVAL;
|
||||
err = 0;
|
||||
ND("called for %s", aqm->name);
|
||||
|
||||
DN_BH_WLOCK();
|
||||
|
||||
/* clean up AQM status and deconfig flowset */
|
||||
dn_ht_scan(dn_cfg.fshash, fs_cleanup, &aqm->type);
|
||||
|
||||
SLIST_FOREACH_SAFE(r, &dn_cfg.aqmlist, next, tmp) {
|
||||
if (strcmp(aqm->name, r->name) != 0)
|
||||
continue;
|
||||
ND("ref_count = %d", r->ref_count);
|
||||
err = (r->ref_count != 0 || r->cfg_ref_count != 0) ? EBUSY : 0;
|
||||
if (err == 0)
|
||||
SLIST_REMOVE(&dn_cfg.aqmlist, r, dn_aqm, next);
|
||||
break;
|
||||
}
|
||||
DN_BH_WUNLOCK();
|
||||
D("%s %sunloaded", aqm->name, err ? "not ":"");
|
||||
if (err)
|
||||
D("ref_count=%d, cfg_ref_count=%d", r->ref_count, r->cfg_ref_count);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
dn_aqm_modevent(module_t mod, int cmd, void *arg)
|
||||
{
|
||||
struct dn_aqm *aqm = arg;
|
||||
|
||||
if (cmd == MOD_LOAD)
|
||||
return load_dn_aqm(aqm);
|
||||
else if (cmd == MOD_UNLOAD)
|
||||
return unload_dn_aqm(aqm);
|
||||
else
|
||||
return EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* end of file */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue