summaryrefslogtreecommitdiff
path: root/trafgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'trafgen.c')
-rw-r--r--trafgen.c140
1 files changed, 131 insertions, 9 deletions
diff --git a/trafgen.c b/trafgen.c
index df2ecf4..a1d5463 100644
--- a/trafgen.c
+++ b/trafgen.c
@@ -55,6 +55,27 @@
#include "ring_tx.h"
#include "csum.h"
+#ifndef timeval_to_timespec
+#define timeval_to_timespec(tv, ts) { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+}
+#endif
+
+enum shaper_type {
+ SHAPER_NONE,
+ SHAPER_PKTS,
+ SHAPER_BYTES,
+};
+
+struct shaper {
+ enum shaper_type type;
+ unsigned long long sent;
+ unsigned long long rate;
+ struct timeval start;
+ struct timeval end;
+};
+
struct ctx {
bool rand, rfraw, jumbo_support, verbose, smoke_test, enforce, qdisc_path;
size_t reserve_size;
@@ -64,6 +85,7 @@ struct ctx {
char *device, *device_trans, *rhost;
struct timespec gap;
struct sockaddr_in dest;
+ struct shaper sh;
char *packet_str;
};
@@ -83,7 +105,7 @@ size_t plen = 0;
struct packet_dyn *packet_dyn = NULL;
size_t dlen = 0;
-static const char *short_options = "d:c:n:t:vJhS:rk:i:o:VRs:P:eE:pu:g:CHQqD:";
+static const char *short_options = "d:c:n:t:vJhS:rk:i:o:VRs:P:eE:pu:g:CHQqD:b:";
static const struct option long_options[] = {
{"dev", required_argument, NULL, 'd'},
{"out", required_argument, NULL, 'o'},
@@ -91,6 +113,7 @@ static const struct option long_options[] = {
{"conf", required_argument, NULL, 'c'},
{"num", required_argument, NULL, 'n'},
{"gap", required_argument, NULL, 't'},
+ {"rate", required_argument, NULL, 'b'},
{"cpus", required_argument, NULL, 'P'},
{"ring-size", required_argument, NULL, 'S'},
{"kernel-pull", required_argument, NULL, 'k'},
@@ -172,6 +195,7 @@ static void __noreturn help(void)
" -r|--rand Randomize packet selection (def: round robin)\n"
" -P|--cpus <uint> Specify number of forks(<= CPUs) (def: #CPUs)\n"
" -t|--gap <time> Set approx. interpacket gap (s/ms/us/ns, def: us)\n"
+ " -b|--rate <rate> Send traffic at specified rate (pps/B/kB/MB/GB/kBit/Mbit/Gbit/KiB/MiB/GiB)\n"
" -S|--ring-size <size> Manually set mmap size (KiB/MiB/GiB)\n"
" -E|--seed <uint> Manually set srand(3) seed\n"
" -u|--user <userid> Drop privileges and change to userid\n"
@@ -535,6 +559,52 @@ static int xmit_smoke_probe(int icmp_sock, struct ctx *ctx)
return -1;
}
+static bool shaper_is_set(struct shaper *sh)
+{
+ return sh->type != SHAPER_NONE;
+}
+
+static void shaper_start(struct shaper *sh)
+{
+ bug_on(gettimeofday(&sh->start, NULL));
+ sh->sent = 0;
+}
+
+static void shaper_init(struct shaper *sh, unsigned long long rate, enum shaper_type type)
+{
+ memset(sh, 0, sizeof(struct shaper));
+ sh->rate = rate;
+ sh->type = type;
+}
+
+static void shaper_delay(struct shaper *sh, unsigned long pkt_len)
+{
+ if ((sh->start.tv_sec | sh->start.tv_usec) <= 0)
+ return;
+
+ sh->sent += sh->type == SHAPER_BYTES ? pkt_len : 1;
+
+ if (sh->sent >= sh->rate) {
+ struct timeval delay_us;
+ struct timespec delay_ns;
+ struct timeval time_sent;
+ struct timeval time_1s = { .tv_sec = 1 };
+
+ bug_on(gettimeofday(&sh->end, NULL));
+ timersub(&sh->end, &sh->start, &time_sent);
+
+ if (timercmp(&time_1s, &time_sent, > )) {
+ timersub(&time_1s, &time_sent, &delay_us);
+ timeval_to_timespec(&delay_us, &delay_ns);
+
+ if ((delay_ns.tv_sec | delay_ns.tv_nsec) > 0)
+ nanosleep(&delay_ns, NULL);
+ }
+
+ shaper_start(sh);
+ }
+}
+
static void xmit_slowpath_or_die(struct ctx *ctx, unsigned int cpu, unsigned long orig_num)
{
int ret, icmp_sock = -1;
@@ -560,6 +630,9 @@ static void xmit_slowpath_or_die(struct ctx *ctx, unsigned int cpu, unsigned lon
bug_on(gettimeofday(&start, NULL));
+ if (shaper_is_set(&ctx->sh))
+ shaper_start(&ctx->sh);
+
while (likely(sigint == 0 && num > 0 && plen > 0)) {
pktd = &packet_dyn[i];
if (packet_dyn_has_elems(pktd)) {
@@ -605,6 +678,9 @@ retry:
if (ctx->num > 0)
num--;
+ if (shaper_is_set(&ctx->sh))
+ shaper_delay(&ctx->sh, packets[i].len);
+
if ((ctx->gap.tv_sec | ctx->gap.tv_nsec) > 0)
nanosleep(&ctx->gap, NULL);
}
@@ -901,6 +977,8 @@ int main(int argc, char **argv)
int min_opts = 5;
char **cpp_argv = NULL;
size_t cpp_argc = 0;
+ unsigned long long rate;
+ enum shaper_type shape_type;
fmemset(&ctx, 0, sizeof(ctx));
ctx.cpus = get_number_cpus_online();
@@ -999,9 +1077,7 @@ int main(int argc, char **argv)
ctx.num = orig_num;
break;
case 't':
- slow = true;
ptr = optarg;
- prctl(PR_SET_TIMERSLACK, 1UL);
gap = strtoul(optarg, NULL, 0);
for (j = i = strlen(optarg); i > 0; --i) {
@@ -1025,14 +1101,51 @@ int main(int argc, char **argv)
} else if (!strncmp(ptr, "s", strlen("s"))) {
ctx.gap.tv_sec = gap;
ctx.gap.tv_nsec = 0;
- } else
+ } else {
panic("Syntax error in time param!\n");
+ }
+ break;
+ case 'b':
+ rate = strtoul(optarg, &ptr, 0);
+ if (!rate || optarg == ptr)
+ panic("Invalid rate param\n");
+
+ if (strncmp(ptr, "pps", strlen("pps")) == 0) {
+ shape_type = SHAPER_PKTS;
+ } else if (strncmp(ptr, "B", strlen("B")) == 0) {
+ shape_type = SHAPER_BYTES;
+ } else if (strncmp(ptr, "kB", strlen("kB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000;
+ } else if (strncmp(ptr, "MB", strlen("MB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000 * 1000;
+ } else if (strncmp(ptr, "GB", strlen("GB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000 * 1000 * 1000;
+ } else if (strncmp(ptr, "kbit", strlen("kbit")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000 / 8;
+ } else if (strncmp(ptr, "Mbit", strlen("Mbit")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000 * 1000 / 8;
+ } else if (strncmp(ptr, "Gbit", strlen("Gbit")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1000 * 1000 * 1000 / 8;
+ } else if (strncmp(ptr, "KiB", strlen("KiB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1 << 10;
+ } else if (strncmp(ptr, "MiB", strlen("MiB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1 << 20;
+ } else if (strncmp(ptr, "GiB", strlen("GiB")) == 0) {
+ shape_type = SHAPER_BYTES;
+ rate *= 1 << 30;
+ } else {
+ panic("Invalid unit type for rate\n");
+ }
- if (gap > 0)
- /* Fall back to single core to not mess up
- * correct timing. We are slow anyway!
- */
- ctx.cpus = 1;
+ shaper_init(&ctx.sh, rate, shape_type);
break;
case 'S':
ptr = optarg;
@@ -1118,6 +1231,15 @@ int main(int argc, char **argv)
sleep(0);
}
+ if (shaper_is_set(&ctx.sh) || (ctx.gap.tv_sec | ctx.gap.tv_nsec) > 0) {
+ prctl(PR_SET_TIMERSLACK, 1UL);
+ /* Fall back to single core to not mess up correct timing.
+ * We are slow anyway!
+ */
+ ctx.cpus = 1;
+ slow = true;
+ }
+
/*
* If number of packets is smaller than number of CPUs use only as
* many CPUs as there are packets. Otherwise we end up sending more