1 //==========================================================================
3 // tests/nc_test_master.c
5 // Network characterizations test (master portion)
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
10 // -------------------------------------------
12 // Portions of this software may have been derived from OpenBSD or other sources,
13 // and are covered by the appropriate copyright disclaimers included herein.
15 // -------------------------------------------
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
22 // Contributors: gthomas
28 //####DESCRIPTIONEND####
30 //==========================================================================
32 // Network characterization test code - master portion
34 #include "nc_test_framework.h"
37 #ifndef CYGPKG_LIBC_STDIO
38 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
40 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
41 static char stack[STACK_SIZE];
42 static cyg_thread thread_data;
43 static cyg_handle_t thread_handle;
51 #define MAX_BUF 32*1024
52 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
54 static int test_seq = 1;
55 static long long idle_count;
56 static long idle_ticks;
57 #define IDLE_TEST_TIME 10
63 #define LENGTH(x) (sizeof(x)/sizeof(x[0]))
72 test_printf("... Done\n");
81 cyg_thread_delay(ticks);
89 usleep(ticks * 10000);
101 #ifndef CYGPKG_SNMPLIB
103 gettimeofday(struct timeval *tv, struct timezone *tz)
105 cyg_tick_count_t cur_time;
106 cur_time = cyg_current_time();
107 tv->tv_sec = cur_time / 100;
108 tv->tv_usec = (cur_time % 100) * 10000;
112 gettimeofday(struct timeval *tv, struct timezone *tz);
116 show_results(const char *msg, struct timeval *start,
117 struct timeval *end, int nbufs, int buflen,
118 int lost, int seq_errors)
120 struct timeval tot_time;
122 double real_time, thru;
123 long tot_bytes = nbufs * buflen;
125 timersub(end, start, &tot_time);
126 test_printf("%s - %d bufs of %d bytes in %d.%02d seconds",
128 tot_time.tv_sec, tot_time.tv_usec / 10000);
130 real_time = tot_time.tv_sec + ((tot_time.tv_usec / 10000) * .01);
131 // Compute bytes / second (rounded up)
132 thru = tot_bytes / real_time;
133 // Convert to Mb / second
134 test_printf(" - %.2f KB/S", thru / 1024.0);
135 test_printf(" - %.4f Mbit/S (M = 10^6)", thru * 8.0 / 1000000.0);
138 test_printf(", %d lost", lost);
141 test_printf(", %d out of sequence", seq_errors);
153 nc_message(int s, struct nc_request *req,
154 struct nc_reply *reply, struct sockaddr_in *slave)
158 struct timeval timeout;
159 len = sizeof(*slave);
160 req->seq = htonl(test_seq);
161 if (sendto(s, req, sizeof(*req), 0, (struct sockaddr *)slave, len) < 0) {
167 timeout.tv_sec = NC_REPLY_TIMEOUT;
169 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
170 test_printf("No response to command\n");
173 if (recvfrom(s, reply, sizeof(*reply), 0, 0, 0) < 0) {
177 if (reply->seq != req->seq) {
178 test_printf("Response out of order - sent: %d, recvd: %d\n",
179 ntohl(req->seq), ntohl(reply->seq));
186 show_test_results(struct nc_test_results *results)
188 if ((ntohl(results->key1) == NC_TEST_RESULT_KEY1) &&
189 (ntohl(results->key2) == NC_TEST_RESULT_KEY2) &&
190 (ntohl(results->seq) == test_seq)) {
191 test_printf(" slave sent %d, recvd %d\n",
192 ntohl(results->nsent), ntohl(results->nrecvd));
194 test_printf(" ... invalid results - keys: %x/%x, seq: %d/%d\n",
195 ntohl(results->key1), ntohl(results->key2),
196 ntohl(results->seq), test_seq);
201 do_udp_test(int s1, int type, struct sockaddr_in *slave,
202 int nbufs, int buflen, int pause_time, int pause_threshold)
204 int i, s, td_len, seq, seq_errors, total_packets;
205 struct sockaddr_in test_chan_master, test_chan_slave;
206 struct timeval start_time, end_time;
207 struct nc_request req;
208 struct nc_reply reply;
209 struct nc_test_results results;
210 struct nc_test_data *tdp;
212 struct timeval timeout;
213 int lost_packets = 0;
214 int need_send, need_recv;
215 const char *type_name;
218 need_recv = true; need_send = true; type_name = "UDP echo";
220 case NC_REQUEST_UDP_RECV:
223 type_name = "UDP recv";
225 case NC_REQUEST_UDP_SEND:
228 type_name = "UDP send";
230 case NC_REQUEST_UDP_ECHO:
235 req.type = htonl(type);
236 req.nbufs = htonl(nbufs);
237 req.buflen = htonl(buflen);
238 req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
239 req.master_port = htonl(NC_TESTING_MASTER_PORT);
240 nc_message(s1, &req, &reply, slave);
241 if (reply.response != ntohl(NC_REPLY_ACK)) {
242 test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
246 s = socket(AF_INET, SOCK_DGRAM, 0);
248 pexit("datagram socket");
251 memset(&test_chan_master, 0, sizeof(test_chan_master));
252 test_chan_master.sin_family = AF_INET;
254 test_chan_master.sin_len = sizeof(test_chan_master);
256 test_chan_master.sin_port = htons(ntohl(req.master_port));
257 test_chan_master.sin_addr.s_addr = INADDR_ANY;
259 if (bind(s, (struct sockaddr *) &test_chan_master, sizeof(test_chan_master)) < 0) {
264 test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
266 test_printf(" - %dms delay after %d packet%s\n", pause_time*10,
267 pause_threshold, pause_threshold > 1 ? "s" : "");
269 test_printf(" - no delays\n");
272 gettimeofday(&start_time, 0);
273 memcpy(&test_chan_slave, slave, sizeof(*slave));
274 test_chan_slave.sin_port = htons(ntohl(req.slave_port));
275 seq = 0; seq_errors = 0; total_packets = 0;
276 for (i = 0; i < nbufs; i++) {
277 td_len = buflen + sizeof(struct nc_test_data);
279 tdp = (struct nc_test_data *)out_buf;
280 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
281 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
283 tdp->len = htonl(td_len);
284 if (sendto(s, tdp, td_len, 0,
285 (struct sockaddr *)&test_chan_slave, sizeof(test_chan_slave)) < 0) {
295 timeout.tv_sec = NC_TEST_TIMEOUT;
297 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
298 test_printf("Slave timed out after %d buffers\n", i);
301 tdp = (struct nc_test_data *)in_buf;
302 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
307 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
308 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
309 if (ntohl(tdp->seq) != seq) {
310 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
311 ntohl(tdp->seq), seq);
314 // Reset sequence to what the slave wants
315 seq = ntohl(tdp->seq);
319 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
320 ntohl(tdp->key1), ntohl(tdp->key2),
329 if (pause_time && (++pkt_ctr == pause_threshold)) {
331 test_delay(pause_time);
335 gettimeofday(&end_time, 0);
336 show_results(type_name, &start_time, &end_time, total_packets, buflen,
337 lost_packets, seq_errors);
338 // Fetch results record
341 timeout.tv_sec = NC_RESULTS_TIMEOUT;
343 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
344 test_printf("No results record sent\n");
346 if (recvfrom(s, &results, sizeof(results), 0, 0, 0) < 0) {
349 show_test_results(&results);
355 // Read data from a stream, accounting for the fact that packet 'boundaries'
356 // are not preserved. This can also timeout (which would probably wreck the
361 do_read(int fd, void *buf, int buflen)
363 char *p = (char *)buf;
367 res = read(fd, p, len);
378 return (buflen - len);
382 do_tcp_test(int s1, int type, struct sockaddr_in *slave,
383 int nbufs, int buflen, int pause_time, int pause_threshold)
385 int i, s, td_len, len, seq, seq_errors, total_packets, res;
386 struct sockaddr_in test_chan_slave;
387 struct timeval start_time, end_time;
388 struct nc_request req;
389 struct nc_reply reply;
390 struct nc_test_results results;
391 struct nc_test_data *tdp;
392 int lost_packets = 0;
393 int conn_failures = 0;
394 int need_send, need_recv;
395 const char *type_name;
398 need_recv = true; need_send = true; type_name = "TCP echo";
400 case NC_REQUEST_TCP_RECV:
403 type_name = "TCP recv";
405 case NC_REQUEST_TCP_SEND:
408 type_name = "TCP send";
410 case NC_REQUEST_TCP_ECHO:
415 req.type = htonl(type);
416 req.nbufs = htonl(nbufs);
417 req.buflen = htonl(buflen);
418 req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
419 req.master_port = htonl(NC_TESTING_MASTER_PORT);
420 nc_message(s1, &req, &reply, slave);
421 if (reply.response != ntohl(NC_REPLY_ACK)) {
422 test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
426 s = socket(AF_INET, SOCK_STREAM, 0);
428 pexit("datagram socket");
431 test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
433 test_printf(" - %dms delay after %d packet%s\n", pause_time*10,
434 pause_threshold, pause_threshold > 1 ? "s" : "");
436 test_printf(" - no delays\n");
440 memcpy(&test_chan_slave, slave, sizeof(*slave));
441 test_chan_slave.sin_port = htons(ntohl(req.slave_port));
442 while (connect(s, (struct sockaddr *)&test_chan_slave, sizeof(*slave)) < 0) {
443 perror("Can't connect to slave");
444 if (++conn_failures > MAX_ERRORS) {
445 test_printf("Too many connection failures - giving up\n");
448 if (errno == ECONNREFUSED) {
449 // Give the slave a little time
450 test_delay(100); // 1 second
456 gettimeofday(&start_time, 0);
457 seq = 0; seq_errors = 0; total_packets = 0;
458 for (i = 0; i < nbufs; i++) {
459 td_len = buflen + sizeof(struct nc_test_data);
461 tdp = (struct nc_test_data *)out_buf;
462 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
463 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
465 tdp->len = htonl(td_len);
466 if ((len = write(s, tdp, td_len)) != td_len) {
470 test_printf("short write - only %d or %d bytes written\n", len, td_len);
478 tdp = (struct nc_test_data *)in_buf;
479 res = do_read(s, tdp, td_len);
481 test_printf("Slave timed out after %d buffers\n", i);
484 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
485 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
486 if (ntohl(tdp->seq) != seq) {
487 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
488 ntohl(tdp->seq), seq);
491 // Reset sequence to what the slave wants
492 seq = ntohl(tdp->seq);
496 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
497 ntohl(tdp->key1), ntohl(tdp->key2),
506 if (pause_time && (++pkt_ctr == pause_threshold)) {
508 test_delay(pause_time);
512 gettimeofday(&end_time, 0);
513 show_results(type_name, &start_time, &end_time, total_packets, buflen,
514 lost_packets, seq_errors);
515 // Fetch results record
516 if (do_read(s, &results, sizeof(results)) != sizeof(results)) {
517 test_printf("No results record sent\n");
519 show_test_results(&results);
525 do_set_load(int s, struct sockaddr_in *slave, int load_level)
527 struct nc_request req;
528 struct nc_reply reply;
529 req.type = htonl(NC_REQUEST_SET_LOAD);
530 req.nbufs = htonl(load_level);
531 nc_message(s, &req, &reply, slave);
532 return (reply.response == ntohl(NC_REPLY_ACK));
536 do_start_idle(int s, struct sockaddr_in *slave)
538 struct nc_request req;
539 struct nc_reply reply;
540 req.type = htonl(NC_REQUEST_START_IDLE);
541 nc_message(s, &req, &reply, slave);
542 return (reply.response == ntohl(NC_REPLY_ACK));
546 do_stop_idle(int s, struct sockaddr_in *slave, int calibrate)
548 struct nc_request req;
549 struct nc_reply reply;
550 long long res_idle_count;
552 int idle, res_idle_ticks;
553 req.type = htonl(NC_REQUEST_STOP_IDLE);
554 nc_message(s, &req, &reply, slave);
555 if (reply.response == ntohl(NC_REPLY_ACK)) {
556 res_idle_ticks = ntohl(reply.misc.idle_results.elapsed_time);
557 res_idle_count = ((long long)ntohl(reply.misc.idle_results.count[0]) << 32) |
558 ntohl(reply.misc.idle_results.count[1]);
559 test_printf("IDLE - ticks: %d, count: %ld",
560 res_idle_ticks, res_idle_count);
562 idle_count = res_idle_count;
563 idle_ticks = res_idle_ticks;
565 adj_count = res_idle_count / res_idle_ticks;
566 adj_count *= idle_ticks;
567 idle = (int) ((adj_count * 100) / idle_count);
568 test_printf(", %d%% idle", idle);
572 test_printf("Slave failed on IDLE\n");
577 do_disconnect(int s, struct sockaddr_in *slave)
579 struct nc_request req;
580 struct nc_reply reply;
581 req.type = htonl(NC_REQUEST_DISCONNECT);
582 nc_message(s, &req, &reply, slave);
586 nc_master(struct test_params *p)
589 struct sockaddr_in slave, my_addr;
590 struct hostent *host;
591 struct pause pause_times[] = {
592 {0,0}, {1,10}, {5,10}, {10,10}, {1,1} };
595 test_printf("Need exactly 'master <host>'\n");
599 s = socket(AF_INET, SOCK_DGRAM, 0);
601 pexit("datagram socket");
604 memset(&my_addr, 0, sizeof(my_addr));
605 my_addr.sin_family = AF_INET;
607 my_addr.sin_len = sizeof(my_addr);
609 my_addr.sin_port = htons(NC_MASTER_PORT);
610 my_addr.sin_addr.s_addr = INADDR_ANY;
612 if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
616 host = gethostbyname(p->argv[1]);
617 if (host == (struct hostent *)NULL) {
618 pexit("gethostbyname");
621 memset(&slave, 0, sizeof(slave));
622 slave.sin_family = AF_INET;
624 slave.sin_len = sizeof(slave);
626 slave.sin_port = htons(NC_SLAVE_PORT);
627 memcpy(&slave.sin_addr.s_addr, host->h_addr, host->h_length);
629 test_printf("================== No load, master at 100%% ========================\n");
631 do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 640, 1024, 0, 0);
632 do_udp_test(s, NC_REQUEST_UDP_SEND, &slave, 640, 1024, 0, 0);
633 do_udp_test(s, NC_REQUEST_UDP_RECV, &slave, 640, 1024, 0, 0);
634 do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 640, 1024, 0, 0);
635 do_tcp_test(s, NC_REQUEST_TCP_SEND, &slave, 640, 1024, 0, 0);
636 do_tcp_test(s, NC_REQUEST_TCP_RECV, &slave, 640, 1024, 0, 0);
638 do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 64, 10240, 0, 0);
640 if (do_set_load(s, &slave, 0)) {
641 test_printf("\n====================== Various slave compute loads ===================\n");
642 for (i = 0; i < 60; i += 10) {
643 test_printf(">>>>>>>>>>>> slave processing load at %d%%\n", i);
644 do_set_load(s, &slave, i);
645 do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 2048, 1024, 0, 0);
646 do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 2048, 1024, 0, 0);
650 if (do_start_idle(s, &slave)) {
651 test_printf("\n====================== Various master loads ===================\n");
652 test_printf("Testing IDLE for %d seconds\n", IDLE_TEST_TIME);
653 test_delay(IDLE_TEST_TIME*100);
654 do_stop_idle(s, &slave, true);
655 for (i = 0; i < LENGTH(pause_times); i++) {
656 do_start_idle(s, &slave);
657 do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 2048, 1024,
658 pause_times[i].pause_ticks, pause_times[i].pause_threshold);
659 do_stop_idle(s, &slave, false);
660 do_start_idle(s, &slave);
661 do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 2048, 1024,
662 pause_times[i].pause_ticks, pause_times[i].pause_threshold);
663 do_stop_idle(s, &slave, false);
667 do_disconnect(s, &slave);
672 net_test(test_param_t p)
674 test_printf("Start Network Characterization - MASTER\n");
676 init_all_network_interfaces();
678 nc_master((struct test_params *)p);
686 static struct test_params p;
687 // Create a main thread, so we can run the scheduler and have time 'pass'
688 cyg_thread_create(10, // Priority - just a number
690 (cyg_addrword_t)&p,// entry parameter
691 "Network test", // Name
694 &thread_handle, // Handle
695 &thread_data // Thread data structure
697 cyg_thread_resume(thread_handle); // Start it
698 cyg_scheduler_start();
704 main(int argc, char *argv[])
706 struct test_params p;