diff options
Diffstat (limited to 'src/tools/operf')
-rw-r--r-- | src/tools/operf/operf.c | 101 | ||||
-rw-r--r-- | src/tools/operf/operf_client.c | 119 | ||||
-rw-r--r-- | src/tools/operf/operf_server.c | 69 |
3 files changed, 205 insertions, 84 deletions
diff --git a/src/tools/operf/operf.c b/src/tools/operf/operf.c index fe387724..137e8647 100644 --- a/src/tools/operf/operf.c +++ b/src/tools/operf/operf.c @@ -62,19 +62,32 @@ #define OPERF_MAX_FLOWS 256 +#define TEST_TYPE_UNI 0 +#define TEST_TYPE_BI 1 + +struct conf { + uint32_t test_type; +} __attribute__((packed)); + +struct msg { + uint32_t id; +} __attribute__((packed)); + struct c { - char * s_apn; - int size; + char * server_name; long rate; bool flood; bool sleep; int duration; + int size; unsigned long sent; unsigned long rcvd; pthread_t reader_pt; pthread_t writer_pt; + + struct conf conf; } client; struct s { @@ -82,6 +95,7 @@ struct s { fset_t * flows; fqueue_t * fq; pthread_mutex_t lock; + struct conf conf[OPERF_MAX_FLOWS]; uint8_t buffer[OPERF_BUF_SIZE]; ssize_t timeout; @@ -99,26 +113,62 @@ static void usage(void) printf("Usage: operf [OPTION]...\n" "Measures bandwidth between a client and a server\n" " -l, --listen Run in server mode\n" - "\n" - " -n, --server-apn Name of the operf server\n" - " -d, --duration Test duration (s, default 60)\n" + " -t, --test The type of test [uni, bi]" + " (default uni)\n" + " -n, --server-name Name of the operf server\n" + " -d, --duration Test duration (default 60s)\n" " -r, --rate Rate (b/s)\n" " -s, --size Payload size (B, default 1500)\n" " -f, --flood Send SDUs as fast as possible\n" " --sleep Sleep in between sending SDUs\n" + "\n" " --help Display this help text and exit\n"); } +/* Times are in ms. */ +static int time_mul(const char * rem) +{ + if (strcmp(rem, "ms") == 0 || strcmp(rem, "") == 0) + return 1; + else if(strcmp(rem, "s") == 0) + return 1000; + else if (strcmp(rem, "m") == 0) + return 60 * 1000; + else if (strcmp(rem, "h") == 0) + return 60 * 60 * 1000; + else if (strcmp(rem, "d") == 0) + return 60 * 60 * 24 * 1000; + + printf("Unknown time unit: %s.\n", rem); + + exit(EXIT_FAILURE); +} + +static int rate_mul(const char * rem) +{ + if (strcmp(rem, "k") == 0 || strcmp(rem, "") == 0) + return 1000; + else if(strcmp(rem, "M") == 0) + return MILLION; + else if (strcmp(rem, "G") == 0) + return BILLION; + + printf("Unknown rate unit: %s.\n", rem); + + exit(EXIT_FAILURE); +} + int main(int argc, char ** argv) { - int ret = -1; - char * rem = NULL; - bool serv = false; + int ret = -1; + char * rem = NULL; + bool serv = false; + char * type = "uni"; argc--; argv++; - client.s_apn = NULL; + client.server_name = NULL; client.size = 1500; client.duration = 60000; server.timeout = 1000; /* ms */ @@ -128,8 +178,8 @@ int main(int argc, char ** argv) while (argc > 0) { if (strcmp(*argv, "-n") == 0 || - strcmp(*argv, "--server_apn") == 0) { - client.s_apn = *(++argv); + strcmp(*argv, "--server-name") == 0) { + client.server_name = *(++argv); --argc; } else if (strcmp(*argv, "-s") == 0 || strcmp(*argv, "--size") == 0) { @@ -137,17 +187,13 @@ int main(int argc, char ** argv) --argc; } else if (strcmp(*argv, "-d") == 0 || strcmp(*argv, "--duration") == 0) { - client.duration = strtol(*(++argv), &rem, 10) * 1000; + client.duration = strtol(*(++argv), &rem, 10); + client.duration *= time_mul(rem); --argc; } else if (strcmp(*argv, "-r") == 0 || strcmp(*argv, "--rate") == 0) { client.rate = strtol(*(++argv), &rem, 10); - if (*rem == 'k') - client.rate *= 1000; - if (*rem == 'M') - client.rate *= MILLION; - if (*rem == 'G') - client.rate *= BILLION; + client.rate *= rate_mul(rem); --argc; } else if (strcmp(*argv, "-f") == 0 || strcmp(*argv, "--flood") == 0) { @@ -157,6 +203,10 @@ int main(int argc, char ** argv) } else if (strcmp(*argv, "-l") == 0 || strcmp(*argv, "--listen") == 0) { serv = true; + } else if (strcmp(*argv, "-t") == 0 || + strcmp(*argv, "--test") == 0) { + type = *(++argv); + --argc; } else { usage(); exit(EXIT_SUCCESS); @@ -168,11 +218,20 @@ int main(int argc, char ** argv) if (serv) { ret = server_main(); } else { - if (client.s_apn == NULL) { + if (client.server_name == NULL) { printf("No server specified.\n"); - usage(); - exit(EXIT_SUCCESS); + exit(EXIT_FAILURE); } + + if (strcmp(type, "uni") == 0) + client.conf.test_type = TEST_TYPE_UNI; + else if (strcmp(type, "bi") == 0) + client.conf.test_type = TEST_TYPE_BI; + else { + printf("Invalid test type.\n"); + exit(EXIT_FAILURE); + } + if (client.size > OPERF_BUF_SIZE) { printf("Packet size truncated to %d bytes.\n", OPERF_BUF_SIZE); diff --git a/src/tools/operf/operf_client.c b/src/tools/operf/operf_client.c index d299e239..c8873c54 100644 --- a/src/tools/operf/operf_client.c +++ b/src/tools/operf/operf_client.c @@ -36,6 +36,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +bool stop; + static void busy_wait_until(const struct timespec * deadline) { struct timespec now; @@ -45,7 +47,6 @@ static void busy_wait_until(const struct timespec * deadline) while (now.tv_sec == deadline->tv_sec && now.tv_nsec < deadline->tv_nsec) clock_gettime(CLOCK_REALTIME, &now); - pthread_testcancel(); } void shutdown_client(int signo, siginfo_t * info, void * c) @@ -57,8 +58,7 @@ void shutdown_client(int signo, siginfo_t * info, void * c) case SIGINT: case SIGTERM: case SIGHUP: - pthread_cancel(client.reader_pt); - pthread_cancel(client.writer_pt); + stop = true; default: return; } @@ -74,7 +74,14 @@ void * reader(void * o) fccntl(fd, FLOWSRCVTIMEO, &timeout); - while ((msg_len = flow_read(fd, buf, OPERF_BUF_SIZE)) != -ETIMEDOUT) { + while (!stop) { + msg_len = flow_read(fd, buf, OPERF_BUF_SIZE); + if (msg_len == -ETIMEDOUT) { + printf("Server timed out.\n"); + stop = true; + break; + } + if (msg_len != client.size) { printf("Invalid message on fd %d.\n", fd); continue; @@ -96,7 +103,10 @@ void * writer(void * o) struct timespec intv = {(gap / BILLION), gap % BILLION}; struct timespec end = {0, 0}; - char * buf = malloc(client.size); + char * buf; + struct msg * msg; + + buf = malloc(client.size); if (buf == NULL) return (void *) -ENOMEM; @@ -107,53 +117,49 @@ void * writer(void * o) memset(buf, 0, client.size); + msg = (struct msg *) buf; + if (client.flood) printf("Flooding %s with %d byte SDUs for %d seconds.\n\n", - client.s_apn, client.size, client.duration / 1000); + client.server_name, client.size, + client.duration / 1000); else printf("Sending %d byte SDUs for %d s to %s at %.3lf Mb/s.\n\n", - client.size, client.duration / 1000, client.s_apn, + client.size, client.duration / 1000, + client.server_name, client.rate / (double) MILLION); clock_gettime(CLOCK_REALTIME, &start); clock_gettime(CLOCK_REALTIME, &now); - pthread_cleanup_push((void (*) (void *)) free, buf); - - if (client.flood) { - while (ts_diff_ms(&start, &now) < client.duration) { - if (flow_write(*fdp, buf, client.size) == -1) { - printf("Failed to send SDU.\n"); - flow_dealloc(*fdp); - free(buf); - return (void *) -1; - } + while (!stop && ts_diff_ms(&start, &now) < client.duration) { + if (!client.flood) { + clock_gettime(CLOCK_REALTIME, &now); + ts_add(&now, &intv, &end); + } - ++client.sent; + msg->id = client.sent; - clock_gettime(CLOCK_REALTIME, &now); + if (flow_write(*fdp, buf, client.size) == -1) { + printf("Failed to send SDU.\n"); + flow_dealloc(*fdp); + free(buf); + return (void *) -1; } - } else { - while (ts_diff_ms(&start, &now) < client.duration) { - clock_gettime(CLOCK_REALTIME, &now); - ts_add(&now, &intv, &end); - if (flow_write(*fdp, buf, client.size) == -1) { - printf("Failed to send SDU.\n"); - flow_dealloc(*fdp); - free(buf); - return (void *) -1; - } + ++client.sent; - ++client.sent; + if (!client.flood) { if (client.sleep) nanosleep(&intv, NULL); else busy_wait_until(&end); + } else { + clock_gettime(CLOCK_REALTIME, &now); } } - pthread_cleanup_pop(true); + free(buf); printf("Test finished.\n"); @@ -183,34 +189,51 @@ int client_main(void) client.sent = 0; client.rcvd = 0; + stop = false; - fd = flow_alloc(client.s_apn, NULL, NULL); + /* FIXME: Allow selecting QoS. */ + fd = flow_alloc(client.server_name, NULL, NULL); if (fd < 0) { printf("Failed to allocate flow.\n"); return -1; } - clock_gettime(CLOCK_REALTIME, &tic); + if (client.conf.test_type == TEST_TYPE_BI) + printf("Doing a bidirectional test.\n"); + else + printf("Doing a unidirectional test.\n"); - pthread_create(&client.reader_pt, NULL, reader, &fd); - pthread_create(&client.writer_pt, NULL, writer, &fd); + if (flow_write(fd, &client.conf, sizeof(client.conf))) { + printf("Failed to send configuration.\n"); + flow_dealloc(fd); + return -1; + } - pthread_join(client.writer_pt, NULL); + sleep(1); + + clock_gettime(CLOCK_REALTIME, &tic); - clock_gettime(CLOCK_REALTIME, &toc); + if (client.conf.test_type == TEST_TYPE_BI) + pthread_create(&client.reader_pt, NULL, reader, &fd); - pthread_join(client.reader_pt, NULL); + pthread_create(&client.writer_pt, NULL, writer, &fd); + pthread_join(client.writer_pt, NULL); - printf("\n"); - printf("--- %s perf statistics ---\n", client.s_apn); - printf("%ld SDUs transmitted, ", client.sent); - printf("%ld received, ", client.rcvd); - printf("%ld%% packet loss, ", client.sent == 0 ? 0 : - 100 - ((100 * client.rcvd) / client.sent)); - printf("time: %.3f ms, ", ts_diff_us(&tic, &toc) / 1000.0); - printf("bandwidth: %.3lf Mb/s.\n", - (client.rcvd * client.size * 8) - / (double) ts_diff_us(&tic, &toc)); + if (client.conf.test_type == TEST_TYPE_BI){ + clock_gettime(CLOCK_REALTIME, &toc); + pthread_join(client.reader_pt, NULL); + + printf("\n"); + printf("--- %s perf statistics ---\n", client.server_name); + printf("%ld SDUs transmitted, ", client.sent); + printf("%ld received, ", client.rcvd); + printf("%ld%% packet loss, ", client.sent == 0 ? 0 : + 100 - ((100 * client.rcvd) / client.sent)); + printf("time: %.3f ms, ", ts_diff_us(&tic, &toc) / 1000.0); + printf("bandwidth: %.3lf Mb/s.\n", + (client.rcvd * client.size * 8) + / (double) ts_diff_us(&tic, &toc)); + } flow_dealloc(fd); diff --git a/src/tools/operf/operf_server.c b/src/tools/operf/operf_server.c index 11eb92fc..de32f320 100644 --- a/src/tools/operf/operf_server.c +++ b/src/tools/operf/operf_server.c @@ -36,6 +36,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +bool stop; + void shutdown_server(int signo, siginfo_t * info, void * c) { (void) info; @@ -46,6 +48,7 @@ void shutdown_server(int signo, siginfo_t * info, void * c) case SIGTERM: case SIGHUP: pthread_cancel(server.accept_pt); + stop = true; default: return; } @@ -58,7 +61,7 @@ void * cleaner_thread(void * o) (void) o; - while (true) { + while (!stop) { clock_gettime(CLOCK_REALTIME, &now); pthread_mutex_lock(&server.lock); for (i = 0; i < OPERF_MAX_FLOWS; ++i) @@ -73,18 +76,21 @@ void * cleaner_thread(void * o) pthread_mutex_unlock(&server.lock); sleep(1); } + + return (void *) 0; } -void * server_thread(void *o) +void * server_thread(void * o) { - int msg_len = 0; + int msg_len = 0; struct timespec timeout = {0, 100 * MILLION}; - struct timespec now = {0, 0}; - int fd; + struct timespec now = {0, 0}; + int fd; + struct msg * msg; (void) o; - while (fevent(server.flows, server.fq, &timeout)) + while (!stop && fevent(server.flows, server.fq, &timeout)) while ((fd = fqueue_next(server.fq)) >= 0) { msg_len = flow_read(fd, server.buffer, OPERF_BUF_SIZE); if (msg_len < 0) @@ -96,9 +102,21 @@ void * server_thread(void *o) server.times[fd] = now; pthread_mutex_unlock(&server.lock); - if (flow_write(fd, server.buffer, msg_len) < 0) { - printf("Error writing to flow (fd %d).\n", fd); - flow_dealloc(fd); + msg = (struct msg *) server.buffer; + + if (server.conf[fd].test_type == TEST_TYPE_UNI) + printf("Seqno %d from fd %d: %zd.%06zu\n", + msg->id, fd, + (ssize_t) now.tv_sec, + (size_t) now.tv_nsec / 1000); + + if (server.conf[fd].test_type == TEST_TYPE_BI) { + if (flow_write(fd, server.buffer, + msg_len) < 0) { + printf("Error writing to flow " + "(fd %d).\n", fd); + flow_dealloc(fd); + } } } @@ -110,12 +128,13 @@ void * accept_thread(void * o) int fd = 0; struct timespec now = {0, 0}; qosspec_t qs; + int len = 0; (void) o; printf("Ouroboros perf server started.\n"); - while (true) { + while (!stop) { fd = flow_accept(&qs, NULL); if (fd < 0) { printf("Failed to accept flow.\n"); @@ -124,6 +143,27 @@ void * accept_thread(void * o) printf("New flow %d.\n", fd); + /* Read test type. */ + len = flow_read(fd, &(server.conf[fd]), + sizeof(server.conf[fd])); + if (len == -ETIMEDOUT) { + printf("Failed to read config message.\n"); + flow_dealloc(fd); + break; + } + + /* Check if length was correct. */ + if (flow_read(fd, NULL, 0) != 0) { + printf("Invalid config message.\n"); + flow_dealloc(fd); + break; + } + + if (server.conf[fd].test_type == TEST_TYPE_BI) + printf("Doing a bidirectional test.\n"); + else + printf("Doing a unidirectional test.\n"); + clock_gettime(CLOCK_REALTIME, &now); pthread_mutex_lock(&server.lock); @@ -161,20 +201,19 @@ int server_main(void) return -1; } + stop = false; + pthread_create(&server.cleaner_pt, NULL, cleaner_thread, NULL); pthread_create(&server.accept_pt, NULL, accept_thread, NULL); pthread_create(&server.server_pt, NULL, server_thread, NULL); pthread_join(server.accept_pt, NULL); - pthread_cancel(server.server_pt); - pthread_cancel(server.cleaner_pt); + pthread_join(server.server_pt, NULL); + pthread_join(server.cleaner_pt, NULL); fset_destroy(server.flows); fqueue_destroy(server.fq); - pthread_join(server.server_pt, NULL); - pthread_join(server.cleaner_pt, NULL); - return 0; } |