- fix dummynet 'fast' mode for WF2Q case.

- fix printing of pipe profile data.
- introduce new pipe parameter: 'burst' - how much data can be sent through
  pipe bypassing bandwidth limit.
This commit is contained in:
Oleg Bulyzhin 2009-06-24 22:57:07 +00:00
parent bc62d44240
commit 6882bf4d92
6 changed files with 101 additions and 28 deletions

View File

@ -3,6 +3,7 @@
PROG= ipfw
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c altq.c
WARNS?= 2
LDADD= -lutil
MAN= ipfw.8
.include <bsd.prog.mk>

View File

@ -32,6 +32,8 @@
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <libutil.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
@ -70,6 +72,7 @@ static struct _s_x dummynet_params[] = {
{ "src-ipv6", TOK_SRCIP6},
{ "src-ip6", TOK_SRCIP6},
{ "profile", TOK_PIPE_PROFILE},
{ "burst", TOK_BURST},
{ "dummynet-params", TOK_NULL },
{ NULL, 0 } /* terminator */
};
@ -236,7 +239,7 @@ print_flowset_parms(struct dn_flow_set *fs, char *prefix)
plr[0] = '\0';
if (fs->flags_fs & DN_IS_RED) /* RED parameters */
sprintf(red,
"\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
"\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
(fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
1.0 * fs->w_q / (double)(1 << SCALE_RED),
SCALE_VAL(fs->min_th),
@ -250,7 +253,7 @@ print_flowset_parms(struct dn_flow_set *fs, char *prefix)
}
static void
print_extra_delay_parms(struct dn_pipe *p, char *prefix)
print_extra_delay_parms(struct dn_pipe *p)
{
double loss;
if (p->samples_no <= 0)
@ -258,8 +261,8 @@ print_extra_delay_parms(struct dn_pipe *p, char *prefix)
loss = p->loss_level;
loss /= p->samples_no;
printf("%s profile: name \"%s\" loss %f samples %d\n",
prefix, p->name, loss, p->samples_no);
printf("\t profile: name \"%s\" loss %f samples %d\n",
p->name, loss, p->samples_no);
}
void
@ -280,6 +283,7 @@ ipfw_list_pipes(void *data, uint nbytes, int ac, char *av[])
double b = p->bandwidth;
char buf[30];
char prefix[80];
char burst[5 + 7];
if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
break; /* done with pipes, now queues */
@ -311,10 +315,16 @@ ipfw_list_pipes(void *data, uint nbytes, int ac, char *av[])
sprintf(prefix, "%05d: %s %4d ms ",
p->pipe_nr, buf, p->delay);
print_extra_delay_parms(p, prefix);
print_flowset_parms(&(p->fs), prefix);
if (humanize_number(burst, sizeof(burst), p->burst,
"Byte", HN_AUTOSCALE, 0) < 0 || co.verbose)
printf("\t burst: %ju Byte\n", p->burst);
else
printf("\t burst: %s\n", burst);
print_extra_delay_parms(p);
q = (struct dn_flow_queue *)(p+1);
list_queues(&(p->fs), q);
}
@ -933,6 +943,21 @@ ipfw_config_pipe(int ac, char **av)
--ac; ++av;
break;
case TOK_BURST:
if (co.do_pipe != 1)
errx(EX_DATAERR, "burst only valid for pipes");
NEED1("burst needs argument\n");
errno = 0;
if (expand_number(av[0], &p.burst) < 0)
if (errno != ERANGE)
errx(EX_DATAERR,
"burst: invalid argument");
if (errno || p.burst > (1ULL << 48) - 1)
errx(EX_DATAERR,
"burst: out of range (0..2^48-1)");
ac--; av++;
break;
default:
errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
}

View File

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 9, 2009
.Dd June 24, 2009
.Dt IPFW 8
.Os
.Sh NAME
@ -1943,6 +1943,20 @@ to reduce
the granularity to 1ms or less).
Default value is 0, meaning no delay.
.Pp
.It Cm burst Ar size
If the data rate exceeds the pipe bandwith limit
(and pipe was idle long enough),
.Ar size
bytes of data is allowed to bypass the
.Nm dummynet
scheduler (i.e. it will be sent without shaping), then transmission rate
will not exceed pipe bandwidth. Effective burst size calculated as follows:
MAX(
.Ar size
,
.Nm bw
* pipe_idle_time).
.Pp
.It Cm profile Ar filename
A file specifying the additional overhead incurred in the transmission
of a packet on the link.

View File

@ -154,6 +154,7 @@ enum tokens {
TOK_BW,
TOK_DELAY,
TOK_PIPE_PROFILE,
TOK_BURST,
TOK_RED,
TOK_GRED,
TOK_DROPTAIL,

View File

@ -229,7 +229,7 @@ struct dn_flow_queue {
int avg ; /* average queue length est. (scaled) */
int count ; /* arrivals since last RED drop */
int random ; /* random value (scaled) */
dn_key q_time; /* start of queue idle time */
dn_key idle_time; /* start of queue idle time */
/* WF2Q+ support */
struct dn_flow_set *fs ; /* parent flow set */
@ -341,8 +341,10 @@ struct dn_pipe { /* a pipe */
/* Same as in dn_flow_queue, numbytes can become large */
int64_t numbytes; /* bits I can transmit (more or less). */
uint64_t burst; /* burst size, scaled: bits * hz */
dn_key sched_time ; /* time pipe was scheduled in ready_heap */
dn_key idle_time; /* start of pipe idle time */
/*
* When the tx clock come from an interface (if_name[0] != '\0'), its name

View File

@ -661,7 +661,7 @@ ready_event(struct dn_flow_queue *q, struct mbuf **head, struct mbuf **tail)
* queue on error hoping next time we are luckier.
*/
} else /* RED needs to know when the queue becomes empty. */
q->q_time = curr_time;
q->idle_time = curr_time;
/*
* If the delay line was empty call transmit_event() now.
@ -761,23 +761,26 @@ ready_event_wfq(struct dn_pipe *p, struct mbuf **head, struct mbuf **tail)
break;
}
}
if (sch->elements == 0 && neh->elements == 0 && p->numbytes >= 0 &&
p->idle_heap.elements > 0) {
if (sch->elements == 0 && neh->elements == 0 && p->numbytes >= 0) {
p->idle_time = curr_time;
/*
* No traffic and no events scheduled.
* We can get rid of idle-heap.
*/
int i;
if (p->idle_heap.elements > 0) {
int i;
for (i = 0; i < p->idle_heap.elements; i++) {
struct dn_flow_queue *q = p->idle_heap.p[i].object;
q->F = 0;
q->S = q->F + 1;
for (i = 0; i < p->idle_heap.elements; i++) {
struct dn_flow_queue *q;
q = p->idle_heap.p[i].object;
q->F = 0;
q->S = q->F + 1;
}
p->sum = 0;
p->V = 0;
p->idle_heap.elements = 0;
}
p->sum = 0;
p->V = 0;
p->idle_heap.elements = 0;
}
/*
* If we are getting clocks from dummynet (not a real interface) and
@ -1042,7 +1045,7 @@ create_queue(struct dn_flow_set *fs, int i)
q->hash_slot = i;
q->next = fs->rq[i];
q->S = q->F + 1; /* hack - mark timestamp as invalid. */
q->numbytes = io_fast ? fs->pipe->bandwidth : 0;
q->numbytes = fs->pipe->burst + (io_fast ? fs->pipe->bandwidth : 0);
fs->rq[i] = q;
fs->rq_elements++;
return (q);
@ -1204,7 +1207,7 @@ red_drops(struct dn_flow_set *fs, struct dn_flow_queue *q, int len)
* XXX check wraps...
*/
if (q->avg) {
u_int t = (curr_time - q->q_time) / fs->lookup_step;
u_int t = (curr_time - q->idle_time) / fs->lookup_step;
q->avg = (t < fs->lookup_depth) ?
SCALE_MUL(q->avg, fs->w_q_lookup[t]) : 0;
@ -1401,9 +1404,30 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa)
if (q->head != m) /* Flow was not idle, we are done. */
goto done;
if (q->q_time < curr_time)
q->numbytes = io_fast ? fs->pipe->bandwidth : 0;
q->q_time = curr_time;
if (is_pipe) { /* Fixed rate queues. */
if (q->idle_time < curr_time) {
/* Calculate available burst size. */
q->numbytes +=
(curr_time - q->idle_time) * pipe->bandwidth;
if (q->numbytes > pipe->burst)
q->numbytes = pipe->burst;
if (io_fast)
q->numbytes += pipe->bandwidth;
}
} else { /* WF2Q. */
if (pipe->idle_time < curr_time) {
/* Calculate available burst size. */
pipe->numbytes +=
(curr_time - pipe->idle_time) * pipe->bandwidth;
if (pipe->numbytes > pipe->burst)
pipe->numbytes = pipe->burst;
if (io_fast)
pipe->numbytes += pipe->bandwidth;
}
pipe->idle_time = curr_time;
}
/* Necessary for both: fixed rate & WF2Q queues. */
q->idle_time = curr_time;
/*
* If we reach this point the flow was previously idle, so we need
@ -1731,6 +1755,8 @@ config_pipe(struct dn_pipe *p)
* qsize = slots/bytes
*/
p->delay = (p->delay * hz) / 1000;
/* Scale burst size: bytes -> bits * hz */
p->burst *= 8 * hz;
/* We need either a pipe number or a flow_set number. */
if (p->pipe_nr == 0 && pfs->fs_nr == 0)
return (EINVAL);
@ -1762,11 +1788,14 @@ config_pipe(struct dn_pipe *p)
} else
/* Flush accumulated credit for all queues. */
for (i = 0; i <= pipe->fs.rq_size; i++)
for (q = pipe->fs.rq[i]; q; q = q->next)
q->numbytes = io_fast ? p->bandwidth : 0;
for (q = pipe->fs.rq[i]; q; q = q->next) {
q->numbytes = p->burst +
(io_fast ? p->bandwidth : 0);
}
pipe->bandwidth = p->bandwidth;
pipe->numbytes = 0; /* just in case... */
pipe->burst = p->burst;
pipe->numbytes = pipe->burst + (io_fast ? pipe->bandwidth : 0);
bcopy(p->if_name, pipe->if_name, sizeof(p->if_name));
pipe->ifp = NULL; /* reset interface ptr */
pipe->delay = p->delay;
@ -2107,6 +2136,7 @@ dummynet_get(struct sockopt *sopt)
*/
bcopy(pipe, bp, sizeof(*pipe));
pipe_bp->delay = (pipe_bp->delay * 1000) / hz;
pipe_bp->burst /= 8 * hz;
/*
* XXX the following is a hack based on ->next being the
* first field in dn_pipe and dn_flow_set. The correct