1 //==========================================================================
3 // tests/nc_test_slave.c
5 // Network characterizations test (slave 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 - slave portion
34 #include "nc_test_framework.h"
38 #ifndef CYGPKG_LIBC_STDIO
39 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
41 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
42 #define MAX_LOAD_THREAD_LEVEL 20
43 #define MIN_LOAD_THREAD_LEVEL 0
44 #define NUM_LOAD_THREADS 10
45 #define IDLE_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY+3
46 #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1
47 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2
48 #define DESIRED_BACKGROUND_LOAD 20
49 static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
50 static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS];
51 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
52 static char idle_thread_stack[STACK_SIZE];
53 static cyg_thread idle_thread_data;
54 static cyg_handle_t idle_thread_handle;
55 static cyg_sem_t idle_thread_sem;
56 volatile static long long idle_thread_count;
57 static cyg_tick_count_t idle_thread_start_time;
58 static cyg_tick_count_t idle_thread_stop_time;
59 static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
60 static cyg_thread load_thread_data[NUM_LOAD_THREADS];
61 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
62 static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS];
63 static long load_thread_level;
64 static void calibrate_load(int load);
65 static void start_load(int load);
66 static void do_some_random_computation(int p);
67 #define abs(n) ((n) < 0 ? -(n) : (n))
71 #define test_param_t cyg_addrword_t
72 #ifdef CYGDBG_NET_TIMING_STATS
73 extern void show_net_times(void);
76 #define test_param_t int
80 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
89 test_printf("... Done\n");
101 test_delay(int ticks)
103 cyg_thread_delay(ticks);
109 test_delay(int ticks)
111 usleep(ticks * 10000);
119 #ifdef CYGDBG_NET_TIMING_STATS
130 do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
132 int i, s, td_len, seq, seq_errors, lost;
133 struct sockaddr_in test_chan_slave, test_chan_master;
135 struct timeval timeout;
136 struct nc_test_results results;
137 struct nc_test_data *tdp;
139 int need_recv, need_send;
141 need_recv = true; need_send = true;
142 switch (ntohl(req->type)) {
143 case NC_REQUEST_UDP_SEND:
147 case NC_REQUEST_UDP_RECV:
151 case NC_REQUEST_UDP_ECHO:
155 s = socket(AF_INET, SOCK_DGRAM, 0);
157 pexit("datagram socket");
160 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
161 test_chan_slave.sin_family = AF_INET;
163 test_chan_slave.sin_len = sizeof(test_chan_slave);
165 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
166 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
168 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
173 memcpy(&test_chan_master, master, sizeof(*master));
174 test_chan_master.sin_port = htons(ntohl(req->master_port));
175 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
176 for (i = 0; i < ntohl(req->nbufs); i++) {
180 timeout.tv_sec = NC_TEST_TIMEOUT;
182 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
183 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
184 if (++lost > MAX_ERRORS) {
185 test_printf("... giving up\n");
190 tdp = (struct nc_test_data *)in_buf;
191 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
192 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
197 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
198 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
199 if (ntohl(tdp->seq) != seq) {
200 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
201 ntohl(tdp->seq), seq);
202 seq = ntohl(tdp->seq);
206 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
207 ntohl(tdp->key1), ntohl(tdp->key2),
217 tdp = (struct nc_test_data *)out_buf;
218 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
219 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
220 tdp->seq = htonl(seq);
221 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
222 tdp->len = htonl(td_len);
223 while (!sent && (--retries >= 0)) {
224 res = sendto(s, tdp, td_len, 0,
225 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master));
230 if (errno == ENOBUFS) {
231 // Saturated the system
232 test_delay(1); // Time for 200 500 byte 10-baseT packets
247 results.key1 = htonl(NC_TEST_RESULT_KEY1);
248 results.key2 = htonl(NC_TEST_RESULT_KEY2);
249 results.seq = req->seq;
250 results.nsent = htonl(nsent);
251 results.nrecvd = htonl(nrecvd);
252 if (sendto(s, &results, sizeof(results), 0,
253 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) {
254 perror("sendto results");
260 // Read data from a stream, accounting for the fact that packet 'boundaries'
261 // are not preserved. This can also timeout (which would probably wreck the
266 do_read(int fd, void *buf, int buflen)
268 char *p = (char *)buf;
272 res = read(fd, p, len);
283 return (buflen - len);
291 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
293 int i, s, td_len, seq, seq_errors, lost, test_chan, res;
295 struct sockaddr_in test_chan_slave, test_chan_master;
296 struct nc_test_results results;
297 struct nc_test_data *tdp;
299 int need_recv, need_send;
301 static int slave_tcp_port = -1;
303 need_recv = true; need_send = true;
304 switch (ntohl(req->type)) {
305 case NC_REQUEST_TCP_SEND:
309 case NC_REQUEST_TCP_RECV:
313 case NC_REQUEST_TCP_ECHO:
317 if (slave_tcp_port < 0) {
318 s = socket(AF_INET, SOCK_STREAM, 0);
320 pexit("datagram socket");
323 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
324 perror("setsockopt SO_REUSEADDR");
328 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
329 perror("setsockopt SO_REUSEPORT");
333 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
334 test_chan_slave.sin_family = AF_INET;
336 test_chan_slave.sin_len = sizeof(test_chan_slave);
338 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
339 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
341 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
345 listen(s, SOMAXCONN);
350 len = sizeof(test_chan_master);
351 if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
354 len = sizeof(test_chan_master);
355 getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
356 test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr),
357 ntohs(test_chan_master.sin_port));
359 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
360 for (i = 0; i < ntohl(req->nbufs); i++) {
362 tdp = (struct nc_test_data *)in_buf;
363 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
364 res = do_read(test_chan, tdp, td_len);
366 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
367 if (++lost > MAX_ERRORS) {
368 test_printf("... giving up\n");
373 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
374 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
375 if (ntohl(tdp->seq) != seq) {
376 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
377 ntohl(tdp->seq), seq);
378 seq = ntohl(tdp->seq);
382 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
383 ntohl(tdp->key1), ntohl(tdp->key2),
389 tdp = (struct nc_test_data *)out_buf;
390 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
391 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
392 tdp->seq = htonl(seq);
393 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
394 tdp->len = htonl(td_len);
395 if (write(test_chan, tdp, td_len) != td_len) {
397 if (errno == ENOBUFS) {
398 // Saturated the system
411 results.key1 = htonl(NC_TEST_RESULT_KEY1);
412 results.key2 = htonl(NC_TEST_RESULT_KEY2);
413 results.seq = req->seq;
414 results.nsent = htonl(nsent);
415 results.nrecvd = htonl(nrecvd);
416 if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
423 // Protocol driver for testing slave.
425 // This function is the main routine running here, handling requests sent from
426 // the master and providing various responses.
429 nc_slave(test_param_t param)
433 struct sockaddr_in my_addr, master;
434 struct nc_request req;
435 struct nc_reply reply;
438 test_printf("Start test for eth%d\n", param);
440 s = socket(AF_INET, SOCK_DGRAM, 0);
442 pexit("datagram socket");
445 memset((char *) &my_addr, 0, sizeof(my_addr));
446 my_addr.sin_family = AF_INET;
448 my_addr.sin_len = sizeof(my_addr);
450 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
451 my_addr.sin_port = htons(NC_SLAVE_PORT);
453 if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
458 masterlen = sizeof(master);
459 if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
463 test_printf("Request %d from %s:%d\n", ntohl(req.type),
464 inet_ntoa(master.sin_addr), ntohs(master.sin_port));
466 reply.response = htonl(NC_REPLY_ACK);
468 switch (ntohl(req.type)) {
469 case NC_REQUEST_DISCONNECT:
472 case NC_REQUEST_UDP_SEND:
473 test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
475 case NC_REQUEST_UDP_RECV:
476 test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
478 case NC_REQUEST_UDP_ECHO:
479 test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
481 case NC_REQUEST_TCP_SEND:
482 test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
484 case NC_REQUEST_TCP_RECV:
485 test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
487 case NC_REQUEST_TCP_ECHO:
488 test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
491 case NC_REQUEST_SET_LOAD:
492 start_load(ntohl(req.nbufs));
494 case NC_REQUEST_START_IDLE:
495 test_printf("Start IDLE thread\n");
496 idle_thread_count = 0;
497 idle_thread_start_time = cyg_current_time();
498 cyg_semaphore_post(&idle_thread_sem);
500 case NC_REQUEST_STOP_IDLE:
501 cyg_semaphore_wait(&idle_thread_sem);
502 idle_thread_stop_time = cyg_current_time();
503 test_printf("Stop IDLE thread\n");
504 reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
505 reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
506 reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
510 test_printf("Unrecognized request: %d\n", ntohl(req.type));
511 reply.response = htonl(NC_REPLY_NAK);
512 reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
515 if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
518 if (reply.response == ntohl(NC_REPLY_NAK)) {
521 switch (ntohl(req.type)) {
522 case NC_REQUEST_UDP_SEND:
523 case NC_REQUEST_UDP_RECV:
524 case NC_REQUEST_UDP_ECHO:
525 do_udp_test(s, &req, &master);
527 case NC_REQUEST_TCP_SEND:
528 case NC_REQUEST_TCP_RECV:
529 case NC_REQUEST_TCP_ECHO:
530 do_tcp_test(s, &req, &master);
532 case NC_REQUEST_START_IDLE:
533 case NC_REQUEST_STOP_IDLE:
534 case NC_REQUEST_SET_LOAD:
543 net_test(test_param_t param)
547 test_printf("Start Network Characterization - SLAVE\n");
549 init_all_network_interfaces();
550 calibrate_load(DESIRED_BACKGROUND_LOAD);
552 // I can see what this is trying to do, but I get "bind: Address already in
553 // use" errors from the 2nd interface - and the parameter is not used
554 // anyway, so one thread does quite well enough (but only tests one i/f at
557 // Comment in the 'int i' above too.
558 for (i = 1; i < CYGHWR_NET_DRIVERS; i++) {
559 cyg_thread_resume(main_thread_handle[i]); // Start other threads
565 #ifdef CYGDBG_NET_TIMING_STATS
574 // This function is called to calibrate the "background load" which can be
575 // applied during testing. It will be called before any commands from the
579 calibrate_load(int desired_load)
581 long long no_load_idle, load_idle;
586 high = MAX_LOAD_THREAD_LEVEL;
587 low = MIN_LOAD_THREAD_LEVEL;
589 // Compute the "no load" idle value
590 idle_thread_count = 0;
591 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
592 cyg_thread_delay(1*100); // Pause for one second
593 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
594 no_load_idle = idle_thread_count;
595 diag_printf("No load = %d\n", (int)idle_thread_count);
597 // First ensure that the HIGH level is indeed higher
599 load_thread_level = high;
600 start_load(desired_load); // Start up a given load
601 idle_thread_count = 0;
602 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
603 cyg_thread_delay(1*100); // Pause for one second
604 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
605 load_idle = idle_thread_count;
606 start_load(0); // Shut down background load
607 percent_load = 100 - ((load_idle * 100) / no_load_idle);
608 diag_printf("High Load[%ld] = %d => %d%%\n", load_thread_level,
609 (int)idle_thread_count, percent_load);
610 if ( percent_load > desired_load )
611 break; // HIGH level is indeed higher
612 low = load_thread_level; // known to be lower
613 high *= 2; // else double it and try again
616 // Now chop down to the level required
618 load_thread_level = (high + low) / 2;
619 start_load(desired_load); // Start up a given load
620 idle_thread_count = 0;
621 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
622 cyg_thread_delay(1*100); // Pause for one second
623 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
624 load_idle = idle_thread_count;
625 start_load(0); // Shut down background load
626 percent_load = 100 - ((load_idle * 100) / no_load_idle);
627 diag_printf("Load[%ld] = %d => %d%%\n", load_thread_level,
628 (int)idle_thread_count, percent_load);
629 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
630 if (percent_load < desired_load) {
631 low = load_thread_level;
633 high = load_thread_level;
637 // Now we are within a few percent of the target; scale the load
638 // factor to get a better fit, and test it, print the answer.
639 load_thread_level *= desired_load;
640 load_thread_level /= percent_load;
641 start_load(desired_load); // Start up a given load
642 idle_thread_count = 0;
643 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
644 cyg_thread_delay(1*100); // Pause for one second
645 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
646 load_idle = idle_thread_count;
647 start_load(0); // Shut down background load
648 percent_load = 100 - ((load_idle * 100) / no_load_idle);
649 diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level,
650 (int)idle_thread_count, percent_load);
651 // no_load_idle_count_1_second = no_load_idle;
655 // This function is called to set up a load level of 'load' percent (given
656 // as a whole number, e.g. start_load(20) would mean initiate a background
657 // load of 20%, leaving the cpu 80% idle).
662 static int prev_load = 0;
664 test_printf("Set background load = %d%%\n", load);
666 if (prev_load == 0) return; // Nothing out there to stop
667 for (i = 0; i < prev_load/10; i++) {
668 cyg_semaphore_wait(&load_thread_sem[i]);
672 for (i = 0; i < load/10; i++) {
673 cyg_semaphore_post(&load_thread_sem[i]);
680 // These thread(s) do some amount of "background" computing. This is used
681 // to simulate a given load level. They need to be run at a higher priority
682 // than the network code itself.
684 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
688 net_load(cyg_addrword_t who)
692 cyg_semaphore_wait(&load_thread_sem[who]);
693 for (i = 0; i < load_thread_level; i++) {
694 do_some_random_computation(i);
696 cyg_thread_delay(1); // Wait until the next 'tick'
697 cyg_semaphore_post(&load_thread_sem[who]);
702 // Some arbitrary computation, designed to use up the CPU and cause associated
703 // cache "thrash" behaviour - part of background load modelling.
706 do_some_random_computation(int p)
708 // Just something that might be "hard"
710 x = ((p * 10) * 3.14159) / 180.0; // radians
714 // This thread does nothing but count. It will be allowed to count
715 // as long as the semaphore is "free".
718 net_idle(cyg_addrword_t param)
721 cyg_semaphore_wait(&idle_thread_sem);
723 cyg_semaphore_post(&idle_thread_sem);
731 // Create processing threads
732 for (i = 0; i < CYGHWR_NET_DRIVERS; i++) {
733 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
735 i, // entry parameter
736 "Network test", // Name
737 &main_thread_stack[i][0], // Stack
739 &main_thread_handle[i], // Handle
740 &main_thread_data[i] // Thread data structure
743 cyg_thread_resume(main_thread_handle[0]); // Start first one
744 // Create the idle thread environment
745 cyg_semaphore_init(&idle_thread_sem, 0);
746 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority
748 0, // entry parameter
749 "Network idle", // Name
750 &idle_thread_stack[0], // Stack
752 &idle_thread_handle, // Handle
753 &idle_thread_data // Thread data structure
755 cyg_thread_resume(idle_thread_handle); // Start it
756 // Create the load threads and their environment(s)
757 for (i = 0; i < NUM_LOAD_THREADS; i++) {
758 cyg_semaphore_init(&load_thread_sem[i], 0);
759 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority
761 i, // entry parameter
762 "Background load", // Name
763 &load_thread_stack[i][0], // Stack
765 &load_thread_handle[i], // Handle
766 &load_thread_data[i] // Thread data structure
768 cyg_thread_resume(load_thread_handle[i]); // Start it
770 cyg_scheduler_start();
776 main(int argc, char *argv[])