diff options
-rw-r--r-- | trafgen.8 | 4 | ||||
-rw-r--r-- | trafgen.c | 140 |
2 files changed, 135 insertions, 9 deletions
@@ -136,6 +136,10 @@ probing for a given interval, iii) ping-like debugging with specific payload patterns. Furthermore, the TX_RING interface does not cope with interpacket gaps. .PP +.SS -b <rate>, --rate <rate> +Specify the packet send rate <num>pps/B/kB/MB/GB/kBit/Mbit/Gbit/KiB/MiB/GiB units. +Like with the -t,--gap option, the packets are sent in slow mode. +.PP .SS -S <size>, --ring-size <size> Manually define the TX_RING resp. TX_RING size in ''<num>KiB/MiB/GiB''. On default the size is being determined based on the network connectivity rate. @@ -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 |