summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluigi <luigi@FreeBSD.org>2013-05-30 11:10:42 +0000
committerluigi <luigi@FreeBSD.org>2013-05-30 11:10:42 +0000
commit3136540cb4a51cd930105e9a7f339e29dea5be44 (patch)
tree58c53fd1fd52d8a0c711e617ab65ecf08141da59
parent4b534acddc0748a127889251377e7ae61e9a6554 (diff)
downloadFreeBSD-src-3136540cb4a51cd930105e9a7f339e29dea5be44.zip
FreeBSD-src-3136540cb4a51cd930105e9a7f339e29dea5be44.tar.gz
add support for rate-limiting in the sender (-R ...)
Obtained from: Giuseppe Lettieri
-rw-r--r--tools/tools/netmap/pkt-gen.c156
1 files changed, 139 insertions, 17 deletions
diff --git a/tools/tools/netmap/pkt-gen.c b/tools/tools/netmap/pkt-gen.c
index 7c2ad98..e0a7d95 100644
--- a/tools/tools/netmap/pkt-gen.c
+++ b/tools/tools/netmap/pkt-gen.c
@@ -25,7 +25,7 @@
/*
* $FreeBSD$
- * $Id: pkt-gen.c 12024 2013-01-25 05:41:51Z luigi $
+ * $Id$
*
* Example program to show how to build a multithreaded packet
* source/sink using the netmap device.
@@ -89,6 +89,9 @@ struct glob_arg {
int dev_type;
pcap_t *p;
+ int tx_rate;
+ struct timespec tx_period;
+
int affinity;
int main_fd;
int report_interval;
@@ -114,7 +117,7 @@ struct targ {
struct netmap_if *nifp;
uint16_t qfirst, qlast; /* range of queues to scan */
volatile uint64_t count;
- struct timeval tic, toc;
+ struct timespec tic, toc;
int me;
pthread_t thread;
int affinity;
@@ -669,6 +672,76 @@ ponger_body(void *data)
return NULL;
}
+static __inline int
+timespec_ge(const struct timespec *a, const struct timespec *b)
+{
+
+ if (a->tv_sec > b->tv_sec)
+ return (1);
+ if (a->tv_sec < b->tv_sec)
+ return (0);
+ if (a->tv_nsec >= b->tv_nsec)
+ return (1);
+ return (0);
+}
+
+static __inline struct timespec
+timeval2spec(const struct timeval *a)
+{
+ struct timespec ts = {
+ .tv_sec = a->tv_sec,
+ .tv_nsec = a->tv_usec * 1000
+ };
+ return ts;
+}
+
+static __inline struct timeval
+timespec2val(const struct timespec *a)
+{
+ struct timeval tv = {
+ .tv_sec = a->tv_sec,
+ .tv_usec = a->tv_nsec / 1000
+ };
+ return tv;
+}
+
+
+static int
+wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited)
+{
+ struct timespec curtime;
+
+ curtime.tv_sec = 0;
+ curtime.tv_nsec = 0;
+
+ if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) {
+ D("clock_gettime: %s", strerror(errno));
+ return (-1);
+ }
+ while (timespec_ge(&ts, &curtime)) {
+ if (waited != NULL)
+ (*waited)++;
+ if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) {
+ D("clock_gettime");
+ return (-1);
+ }
+ }
+ if (wakeup_ts != NULL)
+ *wakeup_ts = curtime;
+ return (0);
+}
+
+static __inline void
+timespec_add(struct timespec *tsa, struct timespec *tsb)
+{
+ tsa->tv_sec += tsb->tv_sec;
+ tsa->tv_nsec += tsb->tv_nsec;
+ if (tsa->tv_nsec >= 1000000000) {
+ tsa->tv_sec++;
+ tsa->tv_nsec -= 1000000000;
+ }
+}
+
static void *
sender_body(void *data)
@@ -680,7 +753,10 @@ sender_body(void *data)
struct netmap_ring *txring;
int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
int options = targ->g->options | OPT_COPY;
-D("start");
+ struct timespec tmptime, nexttime = { 0, 0}; // XXX silence compiler
+ int rate_limit = targ->g->tx_rate;
+ long long waited = 0;
+ D("start");
if (setaffinity(targ->thread, targ->affinity))
goto quit;
/* setup poll(2) mechanism. */
@@ -689,8 +765,18 @@ D("start");
fds[0].events = (POLLOUT);
/* main loop.*/
- gettimeofday(&targ->tic, NULL);
-
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
+ if (rate_limit) {
+ tmptime.tv_sec = 2;
+ tmptime.tv_nsec = 0;
+ timespec_add(&targ->tic, &tmptime);
+ targ->tic.tv_nsec = 0;
+ if (wait_time(targ->tic, NULL, NULL) == -1) {
+ D("wait_time: %s", strerror(errno));
+ goto quit;
+ }
+ nexttime = targ->tic;
+ }
if (targ->g->dev_type == DEV_PCAP) {
int size = targ->g->pkt_size;
void *pkt = &targ->pkt;
@@ -718,8 +804,18 @@ D("start");
}
}
} else {
+ int tosend = 0;
while (!targ->cancel && (n == 0 || sent < n)) {
+ if (rate_limit && tosend <= 0) {
+ tosend = targ->g->burst;
+ timespec_add(&nexttime, &targ->g->tx_period);
+ if (wait_time(nexttime, &tmptime, &waited) == -1) {
+ D("wait_time");
+ goto quit;
+ }
+ }
+
/*
* wait for available room in the send queue(s)
*/
@@ -737,7 +833,7 @@ D("start");
options &= ~OPT_COPY;
}
for (i = targ->qfirst; i < targ->qlast; i++) {
- int m, limit = targ->g->burst;
+ int m, limit = rate_limit ? tosend : targ->g->burst;
if (n > 0 && n - sent < limit)
limit = n - sent;
txring = NETMAP_TXRING(nifp, i);
@@ -746,6 +842,7 @@ D("start");
m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
limit, options);
sent += m;
+ tosend -= m;
targ->count = sent;
}
}
@@ -762,7 +859,7 @@ D("start");
}
}
- gettimeofday(&targ->toc, NULL);
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
targ->completed = 1;
targ->count = sent;
@@ -834,7 +931,7 @@ receiver_body(void *data)
}
/* main loop, exit after 1s silence */
- gettimeofday(&targ->tic, NULL);
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
if (targ->g->dev_type == DEV_PCAP) {
while (!targ->cancel) {
/* XXX should we poll ? */
@@ -852,8 +949,8 @@ receiver_body(void *data)
while (!targ->cancel) {
/* Once we started to receive packets, wait at most 1 seconds
before quitting. */
- if (poll(fds, 1, 1 * 1000) <= 0 && targ->g->forever == 0) {
- gettimeofday(&targ->toc, NULL);
+ if (poll(fds, 1, 1 * 1000) <= 0 && !targ->g->forever) {
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
targ->toc.tv_sec -= 1; /* Subtract timeout time. */
break;
}
@@ -1085,11 +1182,13 @@ main_thread(struct glob_arg *g)
timerclear(&tic);
timerclear(&toc);
for (i = 0; i < g->nthreads; i++) {
+ struct timespec t_tic, t_toc;
/*
* Join active threads, unregister interfaces and close
* file descriptors.
*/
- pthread_join(targs[i].thread, NULL);
+ if (targs[i].used)
+ pthread_join(targs[i].thread, NULL);
close(targs[i].fd);
if (targs[i].completed == 0)
@@ -1100,10 +1199,12 @@ main_thread(struct glob_arg *g)
* how long it took to send all the packets.
*/
count += targs[i].count;
- if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
- tic = targs[i].tic;
- if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
- toc = targs[i].toc;
+ t_tic = timeval2spec(&tic);
+ t_toc = timeval2spec(&toc);
+ if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic))
+ tic = timespec2val(&targs[i].tic);
+ if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc))
+ toc = timespec2val(&targs[i].toc);
}
/* print output. */
@@ -1115,7 +1216,6 @@ main_thread(struct glob_arg *g)
rx_output(count, delta_t);
if (g->dev_type == DEV_NETMAP) {
- ioctl(g->main_fd, NIOCUNREGIF, NULL); // XXX deprecated
munmap(g->mmap_addr, g->mmap_size);
close(g->main_fd);
}
@@ -1224,9 +1324,10 @@ main(int arc, char **argv)
g.burst = 512; // default
g.nthreads = 1;
g.cpus = 1;
+ g.tx_rate = 0;
while ( (ch = getopt(arc, argv,
- "a:f:n:i:t:r:l:d:s:D:S:b:c:o:p:PT:w:Wv")) != -1) {
+ "a:f:n:i:t:r:l:d:s:D:S:b:c:o:p:PT:w:WvR:")) != -1) {
struct sf *fn;
switch(ch) {
@@ -1325,6 +1426,10 @@ main(int arc, char **argv)
break;
case 'v':
verbose++;
+ break;
+ case 'R':
+ g.tx_rate = atoi(optarg);
+ break;
}
}
@@ -1473,6 +1578,23 @@ main(int arc, char **argv)
g.options & OPT_MEMCPY ? " memcpy" : "",
g.options & OPT_COPY ? " copy" : "");
}
+
+ if (g.tx_rate == 0) {
+ g.tx_period.tv_sec = 0;
+ g.tx_period.tv_nsec = 0;
+ } else if (g.tx_rate == 1) {
+ g.tx_period.tv_sec = 1;
+ g.tx_period.tv_nsec = 0;
+ } else {
+ g.tx_period.tv_sec = 0;
+ g.tx_period.tv_nsec = (1e9 / g.tx_rate) * g.burst;
+ if (g.tx_period.tv_nsec > 1000000000) {
+ g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000;
+ g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000;
+ }
+ }
+ D("Sending %d packets every %d.%09d ns",
+ g.burst, (int)g.tx_period.tv_sec, (int)g.tx_period.tv_nsec);
/* Wait for PHY reset. */
D("Wait %d secs for phy reset", wait_link);
sleep(wait_link);
OpenPOWER on IntegriCloud