From a27f6534b56bbc6869a74e00bbfe945deb54e408 Mon Sep 17 00:00:00 2001 From: Jef Poskanzer Date: Tue, 9 Apr 2013 11:42:30 -0700 Subject: [PATCH] Added second concurrency model based on SIGALRM. --- examples/mic.c | 4 ++ src/iperf.h | 3 + src/iperf_api.c | 29 +++++++-- src/iperf_api.h | 2 + src/iperf_client_api.c | 142 +++++++++++++++++++++++++++-------------- src/main.c | 3 + 6 files changed, 129 insertions(+), 54 deletions(-) diff --git a/examples/mic.c b/examples/mic.c index a1195d7..fb47459 100644 --- a/examples/mic.c +++ b/examples/mic.c @@ -34,6 +34,10 @@ main( int argc, char** argv ) exit( EXIT_FAILURE ); } iperf_defaults( test ); + + /* This main program doesn't use SIGALRM, so the iperf API may use it. */ + iperf_set_test_may_use_sigalrm(test, 1); + iperf_set_test_role( test, 'c' ); iperf_set_test_server_hostname( test, host ); iperf_set_test_server_port( test, port ); diff --git a/src/iperf.h b/src/iperf.h index 5b0437d..1d82ba5 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -146,6 +146,9 @@ struct iperf_test int json_output; /* -J option - JSON output */ int zerocopy; /* -Z option - use sendfile */ + int multisend; + int may_use_sigalrm; + /* Select related parameters */ int max_fd; fd_set read_set; /* set of read sockets */ diff --git a/src/iperf_api.c b/src/iperf_api.c index 540b9fb..9815711 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -164,6 +164,12 @@ iperf_get_test_zerocopy(struct iperf_test *ipt) return ipt->zerocopy; } +int +iperf_get_test_may_use_sigalrm(struct iperf_test *ipt) +{ + return ipt->may_use_sigalrm; +} + /************** Setter routines for some fields inside iperf_test *************/ void @@ -256,6 +262,12 @@ iperf_set_test_zerocopy(struct iperf_test *ipt, int zerocopy) ipt->zerocopy = zerocopy; } +void +iperf_set_test_may_use_sigalrm(struct iperf_test *ipt, int may_use_sigalrm) +{ + ipt->may_use_sigalrm = may_use_sigalrm; +} + /********************** Get/set test protocol structure ***********************/ struct protocol * @@ -610,18 +622,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) int iperf_send(struct iperf_test *test, fd_set *write_setP) { - int multisend, r; + register int multisend, r; register struct iperf_stream *sp; /* Can we do multisend mode? */ if (test->protocol->id == Pudp && test->settings->rate != 0) multisend = 1; /* nope */ else - multisend = 20; /* arbitrary */ + multisend = test->multisend; for (; multisend > 0; --multisend) { SLIST_FOREACH(sp, &test->streams, streams) { - if (FD_ISSET(sp->socket, write_setP)) { + if (write_setP == NULL || FD_ISSET(sp->socket, write_setP)) { if ((r = sp->snd(sp)) < 0) { if (r == NET_SOFTERROR) break; @@ -634,9 +646,10 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) } } } - SLIST_FOREACH(sp, &test->streams, streams) - if (FD_ISSET(sp->socket, write_setP)) - FD_CLR(sp->socket, write_setP); + if (write_setP != NULL) + SLIST_FOREACH(sp, &test->streams, streams) + if (FD_ISSET(sp->socket, write_setP)) + FD_CLR(sp->socket, write_setP); return 0; } @@ -1182,6 +1195,9 @@ iperf_defaults(struct iperf_test *testp) testp->settings->bytes = 0; memset(testp->cookie, 0, COOKIE_SIZE); + testp->multisend = 10; /* arbitrary */ + testp->may_use_sigalrm = 0; + /* Set up protocol list */ SLIST_INIT(&testp->streams); SLIST_INIT(&testp->protocols); @@ -1315,6 +1331,7 @@ iperf_reset_test(struct iperf_test *test) test->settings->rate = RATE; /* UDP only */ test->settings->mss = 0; memset(test->cookie, 0, COOKIE_SIZE); + test->multisend = 10; /* arbitrary */ } diff --git a/src/iperf_api.h b/src/iperf_api.h index 5097124..bd83f04 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -58,6 +58,7 @@ char* iperf_get_test_server_hostname( struct iperf_test* ipt ); int iperf_get_test_protocol_id( struct iperf_test* ipt ); int iperf_get_test_json_output( struct iperf_test* ipt ); int iperf_get_test_zerocopy( struct iperf_test* ipt ); +int iperf_get_test_may_use_sigalrm( struct iperf_test* ipt ); /* Setter routines for some fields inside iperf_test. */ void iperf_set_control_socket( struct iperf_test* ipt, int ctrl_sck ); @@ -75,6 +76,7 @@ void iperf_set_test_server_hostname( struct iperf_test* ipt, char* server_hostna void iperf_set_test_json_output( struct iperf_test* ipt, int json_output ); int iperf_has_zerocopy( void ); void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy ); +void iperf_set_test_may_use_sigalrm( struct iperf_test* ipt, int may_use_sigalrm ); /** * exchange_parameters - handles the param_Exchange part for client diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index e60ac92..d33262f 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -181,9 +182,22 @@ iperf_client_end(struct iperf_test *test) } +static int sigalrm_triggered; + +static void +sigalrm_handler(int sig) +{ + sigalrm_triggered = 1; +} + + int iperf_run_client(struct iperf_test * test) { + int concurrency_model; + int startup; +#define CM_SELECT 1 +#define CM_SIGALRM 2 int result; fd_set read_set, write_set; struct timeval now; @@ -205,63 +219,95 @@ iperf_run_client(struct iperf_test * test) return -1; } - // Begin calculating CPU utilization + /* Begin calculating CPU utilization */ cpu_util(NULL); + startup = 1; + concurrency_model = CM_SELECT; /* always start in select mode */ (void) gettimeofday(&now, NULL); while (test->state != IPERF_DONE) { - memcpy(&read_set, &test->read_set, sizeof(fd_set)); - memcpy(&write_set, &test->write_set, sizeof(fd_set)); - - result = select(test->max_fd + 1, &read_set, &write_set, NULL, tmr_timeout(&now)); - if (result < 0 && errno != EINTR) { - i_errno = IESELECT; - return -1; - } - if (result > 0) { - if (FD_ISSET(test->ctrl_sck, &read_set)) { - if (iperf_handle_message_client(test) < 0) { - return -1; - } - FD_CLR(test->ctrl_sck, &read_set); - } - - if (test->state == TEST_RUNNING) { - if (test->reverse) { - // Reverse mode. Client receives. - if (iperf_recv(test, &read_set) < 0) { - return -1; + if (concurrency_model == CM_SELECT) { + memcpy(&read_set, &test->read_set, sizeof(fd_set)); + memcpy(&write_set, &test->write_set, sizeof(fd_set)); + result = select(test->max_fd + 1, &read_set, &write_set, NULL, tmr_timeout(&now)); + if (result < 0 && errno != EINTR) { + i_errno = IESELECT; + return -1; + } + if (result > 0) { + if (FD_ISSET(test->ctrl_sck, &read_set)) { + if (iperf_handle_message_client(test) < 0) { + return -1; } - } else { - // Regular mode. Client sends. - if (iperf_send(test, &write_set) < 0) { - return -1; - } - } - - /* Run the timers. */ - (void) gettimeofday(&now, NULL); - tmr_run(&now); - - /* Is the test done yet? */ - if (test->settings->bytes == 0) { - if (!test->done) - continue; /* not done */ - } else { - if (test->bytes_sent < test->settings->bytes) - continue; /* not done */ + FD_CLR(test->ctrl_sck, &read_set); } - /* Yes, done! Send TEST_END. */ - cpu_util(&test->cpu_util); - test->stats_callback(test); - test->state = TEST_END; - if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { - i_errno = IESENDMESSAGE; + } + } + + if (test->state == TEST_RUNNING) { + + /* Is this our first time in TEST_RUNNING mode? */ + if (startup) { + startup = 0; + /* Can we switch to SIGALRM mode? There are a bunch of + ** cases where either it won't work or it's ill-advised. + */ + if (test->may_use_sigalrm && + (test->protocol->id != Pudp || test->settings->rate == 0) && + (test->stats_interval == 0 || test->stats_interval > 1) && + (test->reporter_interval == 0 || test->reporter_interval > 1) && + ! test->reverse) { + concurrency_model = CM_SIGALRM; + test->multisend = 1; + signal(SIGALRM, sigalrm_handler); + sigalrm_triggered = 0; + alarm(1); + } + } + + if (test->reverse) { + // Reverse mode. Client receives. + if (iperf_recv(test, &read_set) < 0) { return -1; } - } - } + } else { + // Regular mode. Client sends. + if (iperf_send(test, concurrency_model == CM_SIGALRM ? NULL : &write_set) < 0) { + return -1; + } + } + + if (concurrency_model == CM_SELECT || + (concurrency_model == CM_SIGALRM && sigalrm_triggered)) { + /* Run the timers. */ + (void) gettimeofday(&now, NULL); + tmr_run(&now); + if (concurrency_model == CM_SIGALRM) { + sigalrm_triggered = 0; + alarm(1); + } + } + + /* Is the test done yet? */ + if (test->settings->bytes == 0) { + if (!test->done) + continue; /* not done */ + } else { + if (test->bytes_sent < test->settings->bytes) + continue; /* not done */ + } + /* Yes, done! Send TEST_END. */ + cpu_util(&test->cpu_util); + test->stats_callback(test); + test->state = TEST_END; + if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { + i_errno = IESENDMESSAGE; + return -1; + } + /* And if we were doing SIGALRM, go back to select for the end. */ + concurrency_model = CM_SELECT; + } } if (test->json_output) { diff --git a/src/main.c b/src/main.c index 786d7d7..dc3f26e 100644 --- a/src/main.c +++ b/src/main.c @@ -78,6 +78,9 @@ main(int argc, char **argv) iperf_errexit(NULL, "create new test error - %s", iperf_strerror(i_errno)); iperf_defaults(test); /* sets defaults */ + /* This main program doesn't use SIGALRM, so the iperf API may use it. */ + iperf_set_test_may_use_sigalrm(test, 1); + // XXX: Check signal for errors? signal(SIGINT, sig_handler); if (setjmp(env)) {