Skip to content

Commit a61d1a1

Browse files
committed
Merge pull request appneta#236 from appneta/Bug_#235_pps_inaccurate
appneta#235 fix 'first_packet' logic
2 parents e615e96 + e7241b5 commit a61d1a1

File tree

5 files changed

+61
-46
lines changed

5 files changed

+61
-46
lines changed

docs/CHANGELOG

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
--/--/---- Version 4.1.1-beta4
2-
- netmap reports impossibly hi capacity (#176)
2+
- Improve --pps accuracy and performance (#236)
3+
- netmap reports impossibly high capacity (#176)
34
- netmap optimizations (#93)
45

56
12/19/2015 Version 4.1.1-beta3

src/common/sendpacket.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ struct sendpacket_s {
125125
sendpacket_type_t handle_type;
126126
union sendpacket_handle handle;
127127
struct tcpr_ether_addr ether;
128+
#if defined HAVE_QUICK_TX || defined HAVE_NETMAP
129+
int first_packet;
130+
#endif
131+
128132
#ifdef HAVE_QUICK_TX
129133
struct quick_tx* qtx_dev;
130134
#endif /* HAVE_QUICK_TX */

src/send_packets.c

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ fast_edit_packet_dl(struct pcap_pkthdr *pkthdr, u_char **pktdata,
235235
*dst_ptr = htonl(dst_ip);
236236
}
237237

238+
#if defined HAVE_QUICK_TX || defined HAVE_NETMAP
238239
static inline void wake_send_queues(sendpacket_t *sp, tcpreplay_opt_t *options)
239240
{
240241
#ifdef HAVE_QUICK_TX
@@ -247,6 +248,7 @@ static inline void wake_send_queues(sendpacket_t *sp, tcpreplay_opt_t *options)
247248
ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */
248249
#endif
249250
}
251+
#endif /* HAVE_QUICK_TX || HAVE_NETMAP */
250252

251253
static inline void
252254
fast_edit_packet(struct pcap_pkthdr *pkthdr, u_char **pktdata,
@@ -488,9 +490,10 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
488490
COUNTER iteration = ctx->iteration;
489491
bool unique_ip = options->unique_ip;
490492
bool preload = options->file_cache[idx].cached;
491-
bool top_speed = options->speed.mode == speed_topspeed;
493+
bool top_speed = (options->speed.mode == speed_topspeed);
492494
bool now_is_now = false;
493495

496+
ctx->skip_packets = 0;
494497
start_us = TIMEVAL_TO_MICROSEC(&ctx->stats.start_time);
495498

496499
if (options->limit_time > 0)
@@ -565,12 +568,16 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
565568
if (skip_length && pktlen < skip_length) {
566569
skip_length -= pktlen;
567570
now_is_now = false;
571+
} else if (ctx->skip_packets) {
572+
--ctx->skip_packets;
573+
now_is_now = false;
568574
} else {
569575
/*
570576
* time stamping is expensive, but now is the
571577
* time to do it.
572578
*/
573579
skip_length = 0;
580+
ctx->skip_packets = 0;
574581
now_is_now = true;
575582
gettimeofday(&now, NULL);
576583

@@ -580,7 +587,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
580587
* This also sets skip_length which will avoid timestamping for
581588
* a given number of packets.
582589
*/
583-
calc_sleep_time(ctx, (struct timeval *)&pkthdr.ts, &ctx->stats.last_time, pktlen, sp, packetnum,
590+
calc_sleep_time(ctx, &pkthdr.ts, &ctx->stats.last_time, pktlen, sp, packetnum,
584591
&ctx->stats.end_time, &start_us, &skip_length);
585592

586593
/*
@@ -634,14 +641,12 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
634641
}
635642
}
636643

637-
if (sp == ctx->intf1 && ctx->first_time_intf1) {
638-
wake_send_queues(sp, options);
639-
ctx->first_time_intf1 = 0;
640-
} else if (sp == ctx->intf2 && ctx->first_time_intf2) {
644+
#if defined HAVE_QUICK_TX || defined HAVE_NETMAP
645+
if (sp->first_packet) {
641646
wake_send_queues(sp, options);
642-
ctx->first_time_intf2 = 0;
647+
sp->first_packet = false;
643648
}
644-
649+
#endif
645650
/* stop sending based on the duration limit... */
646651
if ((end_us > 0 && TIMEVAL_TO_MICROSEC(&now) > end_us) ||
647652
/* ... or stop sending based on the limit -L? */
@@ -699,10 +704,10 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
699704
COUNTER start_us;
700705
COUNTER end_us;
701706
COUNTER skip_length = 0;
702-
bool top_speed = options->speed.mode == speed_topspeed ||
703-
(options->speed.mode == speed_mbpsrate && !options->speed.speed);
707+
bool top_speed = (options->speed.mode == speed_topspeed);
704708
bool now_is_now = false;
705709

710+
ctx->skip_packets = 0;
706711
start_us = TIMEVAL_TO_MICROSEC(&ctx->stats.start_time);
707712

708713
if (options->limit_time > 0)
@@ -809,12 +814,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
809814
if (skip_length && pktlen < skip_length) {
810815
skip_length -= pktlen;
811816
now_is_now = false;
817+
} else if (ctx->skip_packets) {
818+
--ctx->skip_packets;
819+
now_is_now = false;
812820
} else {
813821
/*
814822
* time stamping is expensive, but now is the
815823
* time to do it.
816824
*/
817825
skip_length = 0;
826+
ctx->skip_packets = 0;
818827
now_is_now = true;
819828
gettimeofday(&now, NULL);
820829

@@ -824,7 +833,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
824833
* This also sets skip_length which will avoid timestamping for
825834
* a given number of packets.
826835
*/
827-
calc_sleep_time(ctx, (struct timeval *)&pkthdr_ptr->ts, &ctx->stats.last_time, pktlen, sp, packetnum,
836+
calc_sleep_time(ctx, &pkthdr_ptr->ts, &ctx->stats.last_time, pktlen, sp, packetnum,
828837
&ctx->stats.end_time, &start_us, &skip_length);
829838

830839
/*
@@ -875,13 +884,12 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
875884
}
876885
}
877886

878-
if (sp == ctx->intf1 && ctx->first_time_intf1) {
879-
wake_send_queues(sp, options);
880-
ctx->first_time_intf1 = 0;
881-
} else if (sp == ctx->intf2 && ctx->first_time_intf2) {
887+
#if defined HAVE_QUICK_TX || defined HAVE_NETMAP
888+
if (sp->first_packet) {
882889
wake_send_queues(sp, options);
883-
ctx->first_time_intf2 = 0;
890+
sp->first_packet = false;
884891
}
892+
#endif
885893

886894
/* get the next packet for this file handle depending on which we last used */
887895
if (sp == ctx->intf2) {
@@ -1060,44 +1068,32 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_time,
10601068
{
10611069
tcpreplay_opt_t *options = ctx->options;
10621070
struct timeval nap_for;
1063-
uint64_t ppnsec; /* packets per nsec */
10641071
COUNTER now_us;
10651072

10661073
timesclear(&ctx->nap);
10671074

1068-
/* accelerator time? */
1069-
if (ctx->skip_packets > 0) {
1070-
(ctx->skip_packets)--;
1071-
return;
1072-
}
1073-
10741075
/*
1075-
* pps_multi accelerator. This uses the existing send accelerator above
1076+
* pps_multi accelerator. This uses the existing send accelerator
10761077
* and hence requires the funky math to get the expected timings.
10771078
*/
10781079
if (options->speed.mode == speed_packetrate && options->speed.pps_multi) {
10791080
ctx->skip_packets = options->speed.pps_multi - 1;
1080-
if (ctx->first_time_intf1) {
1081+
if (ctx->first_time) {
1082+
ctx->first_time = false;
10811083
return;
10821084
}
10831085
}
10841086

10851087
dbgx(4, "This packet time: " TIMEVAL_FORMAT, pkt_time->tv_sec, pkt_time->tv_usec);
10861088
dbgx(4, "Last packet time: " TIMEVAL_FORMAT, last->tv_sec, last->tv_usec);
10871089

1088-
/* If top speed, you shouldn't even be here */
1089-
// assert(options->speed.mode != speed_topspeed);
1090-
10911090
switch(options->speed.mode) {
10921091
case speed_multiplier:
10931092
/*
10941093
* Replay packets a factor of the time they were originally sent.
10951094
*/
10961095
if (timerisset(last)) {
1097-
if (timercmp(pkt_time, last, <)) {
1098-
/* Packet has gone back in time! Don't sleep and warn user */
1099-
warnx("Packet #" COUNTER_SPEC " has gone back in time!", counter);
1100-
} else {
1096+
if (timercmp(pkt_time, last, >=)) {
11011097
/* pkt_time has increased or is the same, so handle normally */
11021098
timersub(pkt_time, last, &nap_for);
11031099
dbgx(3, "original packet delta pkt_time: " TIMEVAL_FORMAT, nap_for.tv_sec, nap_for.tv_usec);
@@ -1125,27 +1121,41 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_time,
11251121
/* bits * 1000000 divided by bps = microseconds */
11261122
COUNTER next_tx_us = (bits_sent * 1000000) / bps;
11271123
COUNTER tx_us = now_us - *start_us;
1128-
if (next_tx_us > tx_us)
1129-
NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000LL, &ctx->nap);
1130-
else if (tx_us > next_tx_us) {
1124+
if (next_tx_us > tx_us) {
1125+
NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap);
1126+
} else if (tx_us > next_tx_us) {
11311127
tx_us = now_us - *start_us;
11321128
*skip_length = ((tx_us - next_tx_us) * bps) / 8000000;
11331129
}
11341130
update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_us, tx_us, next_tx_us);
11351131
}
11361132

1137-
dbgx(3, "packet size " COUNTER_SPEC "\t\tequals\tnap " TIMESPEC_FORMAT, len,
1133+
dbgx(3, "packet size=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, len,
11381134
ctx->nap.tv_sec, ctx->nap.tv_nsec);
11391135
break;
11401136

11411137
case speed_packetrate:
1142-
/* only need to calculate this the first time */
1143-
if (! timesisset(&ctx->nap)) {
1144-
/* run in packets/sec */
1145-
ppnsec = 1000000000 / options->speed.speed * (options->speed.pps_multi > 0 ? options->speed.pps_multi : 1);
1146-
NANOSEC_TO_TIMESPEC(ppnsec, &ctx->nap);
1147-
dbgx(1, "sending %d packet(s) per %lu nsec", (options->speed.pps_multi > 0 ? options->speed.pps_multi : 1), ctx->nap.tv_nsec);
1148-
}
1138+
/*
1139+
* Ignore the time supplied by the capture file and send data at
1140+
* a constant rate (packets per second).
1141+
*/
1142+
now_us = TIMSTAMP_TO_MICROSEC(sent_timestamp);
1143+
if (now_us) {
1144+
COUNTER pps = ctx->options->speed.speed * (ctx->options->speed.pps_multi > 0 ? ctx->options->speed.pps_multi : 1);;
1145+
COUNTER pkts_sent = ctx->stats.pkts_sent;
1146+
/* packets * 1000000 divided by pps = microseconds */
1147+
COUNTER next_tx_us = (pkts_sent * 1000000) / pps;
1148+
COUNTER tx_us = now_us - *start_us;
1149+
if (next_tx_us > tx_us)
1150+
NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap);
1151+
else
1152+
ctx->skip_packets = options->speed.pps_multi;
1153+
1154+
update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_us, tx_us, next_tx_us);
1155+
}
1156+
1157+
dbgx(3, "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, ctx->stats.pkts_sent,
1158+
ctx->nap.tv_sec, ctx->nap.tv_nsec);
11491159
break;
11501160

11511161
case speed_oneatatime:

src/tcpreplay_api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ tcpreplay_init()
127127
ctx->intf1dlt = -1;
128128
ctx->intf2dlt = -1;
129129
ctx->abort = false;
130+
ctx->first_time = true;
130131
return ctx;
131132
}
132133

src/tcpreplay_api.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ typedef struct tcpreplay_s {
188188
/* sleep helpers */
189189
struct timespec nap;
190190
uint32_t skip_packets;
191-
int first_time_intf1;
192-
int first_time_intf2;
191+
bool first_time;
193192

194193
/* counter stats */
195194
tcpreplay_stats_t stats;

0 commit comments

Comments
 (0)