summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/Makefile1
-rw-r--r--sbin/ipfw/dummynet.c37
-rw-r--r--sbin/ipfw/ipfw.816
-rw-r--r--sbin/ipfw/ipfw2.h1
-rw-r--r--sys/netinet/ip_dummynet.h4
-rw-r--r--sys/netinet/ipfw/ip_dummynet.c72
6 files changed, 102 insertions, 29 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index 3205c66..c09ebca 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -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>
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index 9e8356e..1473284 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -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 @@ end_mask:
--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]);
}
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 9777272..de683e3 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -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.
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 5b49f55..d3ce7fb 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -154,6 +154,7 @@ enum tokens {
TOK_BW,
TOK_DELAY,
TOK_PIPE_PROFILE,
+ TOK_BURST,
TOK_RED,
TOK_GRED,
TOK_DROPTAIL,
diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h
index 5b6019d..b5ef19e 100644
--- a/sys/netinet/ip_dummynet.h
+++ b/sys/netinet/ip_dummynet.h
@@ -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
diff --git a/sys/netinet/ipfw/ip_dummynet.c b/sys/netinet/ipfw/ip_dummynet.c
index bf54839..d562099 100644
--- a/sys/netinet/ipfw/ip_dummynet.c
+++ b/sys/netinet/ipfw/ip_dummynet.c
@@ -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;
-
- 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;
+ if (p->idle_heap.elements > 0) {
+ int i;
+
+ 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
OpenPOWER on IntegriCloud