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
33 #include <cyg/kernel/kapi.h>
34 #include <cyg/hal/hal_arch.h>
36 #include "nc_test_framework.h"
38 #include <cyg/infra/diag.h>
40 #include <lwip/inet.h>
41 #include <lwip/arch.h>
43 #define LWIP_TIMEVAL_PRIVATE 1
44 #include <lwip/sockets.h>
45 #include <cyg/infra/testcase.h>
47 #ifndef CYGPKG_LIBC_STDIO
48 #include <cyg/error/errno.h>
49 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
53 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
54 #define MAX_LOAD_THREAD_LEVEL 20
55 #define MIN_LOAD_THREAD_LEVEL 0
56 #define NUM_LOAD_THREADS 10
57 #define CYGPKG_NET_THREAD_PRIORITY 7
58 #define IDLE_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY+3
59 #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1
60 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2
61 #define DESIRED_BACKGROUND_LOAD 20
62 #define CYGHWR_NET_DRIVERS 1
65 #ifdef CYGPKG_LWIP_TCP
66 #ifdef CYGPKG_LWIP_UDP
70 static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
71 static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS];
72 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
76 static char idle_thread_stack[STACK_SIZE];
77 static cyg_thread idle_thread_data;
78 static cyg_handle_t idle_thread_handle;
79 static cyg_sem_t idle_thread_sem;
80 volatile static long long idle_thread_count;
81 static cyg_tick_count_t idle_thread_start_time;
82 static cyg_tick_count_t idle_thread_stop_time;
83 static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
84 static cyg_thread load_thread_data[NUM_LOAD_THREADS];
85 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
86 static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS];
87 static long load_thread_level;
88 static void calibrate_load(int load);
89 static void start_load(int load);
90 static void do_some_random_computation(int p);
91 #define abs(n) ((n) < 0 ? -(n) : (n))
93 #define test_param_t cyg_addrword_t
94 #ifdef CYGDBG_NET_TIMING_STATS
95 extern void show_net_times(void);
99 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
105 test_delay(int ticks)
107 cyg_thread_delay(ticks);
115 #ifdef CYGDBG_NET_TIMING_STATS
126 do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
128 int i, s, td_len, seq, seq_errors, lost;
129 struct sockaddr_in test_chan_slave, test_chan_master;
131 struct timeval timeout;
132 struct nc_test_results results;
133 struct nc_test_data *tdp;
135 int need_recv, need_send;
137 need_recv = true; need_send = true;
138 switch (ntohl(req->type)) {
139 case NC_REQUEST_UDP_SEND:
143 case NC_REQUEST_UDP_RECV:
147 case NC_REQUEST_UDP_ECHO:
151 s = socket(AF_INET, SOCK_DGRAM, 0);
153 pexit("datagram socket");
156 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
157 test_chan_slave.sin_family = AF_INET;
158 test_chan_slave.sin_len = sizeof(test_chan_slave);
159 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
160 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
162 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
167 memcpy(&test_chan_master, master, sizeof(*master));
168 test_chan_master.sin_port = htons(ntohl(req->master_port));
169 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
170 for (i = 0; i < ntohl(req->nbufs); i++) {
174 timeout.tv_sec = NC_TEST_TIMEOUT;
176 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
177 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
178 if (++lost > MAX_ERRORS) {
179 test_printf("... giving up\n");
184 tdp = (struct nc_test_data *)in_buf;
185 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
186 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
191 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
192 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
193 if (ntohl(tdp->seq) != seq) {
194 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
195 ntohl(tdp->seq), seq);
196 seq = ntohl(tdp->seq);
200 test_printf("Bad data packet - key: %lx/%lx, seq: %d\n",
201 ntohl(tdp->key1), ntohl(tdp->key2),
211 tdp = (struct nc_test_data *)out_buf;
212 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
213 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
214 tdp->seq = htonl(seq);
215 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
216 tdp->len = htonl(td_len);
217 while (!sent && (--retries >= 0)) {
218 res = sendto(s, tdp, td_len, 0,
219 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master));
224 if (errno == ENOBUFS) {
225 // Saturated the system
226 test_delay(1); // Time for 200 500 byte 10-baseT packets
241 results.key1 = htonl(NC_TEST_RESULT_KEY1);
242 results.key2 = htonl(NC_TEST_RESULT_KEY2);
243 results.seq = req->seq;
244 results.nsent = htonl(nsent);
245 results.nrecvd = htonl(nrecvd);
246 if (sendto(s, &results, sizeof(results), 0,
247 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) {
248 perror("sendto results");
254 // Read data from a stream, accounting for the fact that packet 'boundaries'
255 // are not preserved. This can also timeout (which would probably wreck the
260 do_read(int fd, void *buf, int buflen)
262 char *p = (char *)buf;
266 res = read(fd, p, len);
277 return (buflen - len);
285 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
287 int i, s, len, td_len, seq, seq_errors, lost, test_chan, res;
288 struct sockaddr_in test_chan_slave, test_chan_master;
289 struct nc_test_results results;
290 struct nc_test_data *tdp;
292 int need_recv, need_send;
294 static int slave_tcp_port = -1;
296 need_recv = true; need_send = true;
297 switch (ntohl(req->type)) {
298 case NC_REQUEST_TCP_SEND:
302 case NC_REQUEST_TCP_RECV:
306 case NC_REQUEST_TCP_ECHO:
310 if (slave_tcp_port < 0) {
311 s = socket(AF_INET, SOCK_STREAM, 0);
313 pexit("datagram socket");
316 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
317 perror("setsockopt SO_REUSEADDR");
321 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
322 perror("setsockopt SO_REUSEPORT");
326 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
327 test_chan_slave.sin_family = AF_INET;
328 test_chan_slave.sin_len = sizeof(test_chan_slave);
329 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
330 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
332 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
341 len = sizeof(test_chan_master);
342 if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
345 len = sizeof(test_chan_master);
346 getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
347 // test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr),
348 // ntohs(test_chan_master.sin_port));
350 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
351 for (i = 0; i < ntohl(req->nbufs); i++) {
353 tdp = (struct nc_test_data *)in_buf;
354 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
355 res = do_read(test_chan, tdp, td_len);
357 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
358 if (++lost > MAX_ERRORS) {
359 test_printf("... giving up\n");
364 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
365 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
366 if (ntohl(tdp->seq) != seq) {
367 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
368 ntohl(tdp->seq), seq);
369 seq = ntohl(tdp->seq);
373 test_printf("Bad data packet - key: %lx/%lx, seq: %d\n",
374 ntohl(tdp->key1), ntohl(tdp->key2),
380 tdp = (struct nc_test_data *)out_buf;
381 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
382 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
383 tdp->seq = htonl(seq);
384 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
385 tdp->len = htonl(td_len);
386 if (write(test_chan, tdp, td_len) != td_len) {
388 if (errno == ENOBUFS) {
389 // Saturated the system
402 results.key1 = htonl(NC_TEST_RESULT_KEY1);
403 results.key2 = htonl(NC_TEST_RESULT_KEY2);
404 results.seq = req->seq;
405 results.nsent = htonl(nsent);
406 results.nrecvd = htonl(nrecvd);
407 if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
414 // Protocol driver for testing slave.
416 // This function is the main routine running here, handling requests sent from
417 // the master and providing various responses.
420 nc_slave(test_param_t param)
423 struct sockaddr_in my_addr, master;
424 struct nc_request req;
425 struct nc_reply reply;
428 test_printf("Start test for eth%d\n", param);
430 s = socket(AF_INET, SOCK_DGRAM, 0);
432 pexit("datagram socket");
435 memset((char *) &my_addr, 0, sizeof(my_addr));
436 my_addr.sin_family = AF_INET;
437 my_addr.sin_len = sizeof(my_addr);
438 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
439 my_addr.sin_port = htons(NC_SLAVE_PORT);
441 if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
446 masterlen = sizeof(master);
447 if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
451 test_printf("Request %d from %s:%d\n", ntohl(req.type),
452 inet_ntoa(master.sin_addr), ntohs(master.sin_port));
454 reply.response = htonl(NC_REPLY_ACK);
456 switch (ntohl(req.type)) {
457 case NC_REQUEST_DISCONNECT:
460 case NC_REQUEST_UDP_SEND:
461 test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
463 case NC_REQUEST_UDP_RECV:
464 test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
466 case NC_REQUEST_UDP_ECHO:
467 test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
469 case NC_REQUEST_TCP_SEND:
470 test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
472 case NC_REQUEST_TCP_RECV:
473 test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
475 case NC_REQUEST_TCP_ECHO:
476 test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
478 case NC_REQUEST_SET_LOAD:
479 start_load(ntohl(req.nbufs));
481 case NC_REQUEST_START_IDLE:
482 test_printf("Start IDLE thread\n");
483 idle_thread_count = 0;
484 idle_thread_start_time = cyg_current_time();
485 cyg_semaphore_post(&idle_thread_sem);
487 case NC_REQUEST_STOP_IDLE:
488 cyg_semaphore_wait(&idle_thread_sem);
489 idle_thread_stop_time = cyg_current_time();
490 test_printf("Stop IDLE thread\n");
491 reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
492 reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
493 reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
496 test_printf("Unrecognized request: %d\n", ntohl(req.type));
497 reply.response = htonl(NC_REPLY_NAK);
498 reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
501 if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
504 if (reply.response == ntohl(NC_REPLY_NAK)) {
507 switch (ntohl(req.type)) {
508 case NC_REQUEST_UDP_SEND:
509 case NC_REQUEST_UDP_RECV:
510 case NC_REQUEST_UDP_ECHO:
511 do_udp_test(s, &req, &master);
513 case NC_REQUEST_TCP_SEND:
514 case NC_REQUEST_TCP_RECV:
515 case NC_REQUEST_TCP_ECHO:
516 do_tcp_test(s, &req, &master);
518 case NC_REQUEST_START_IDLE:
519 case NC_REQUEST_STOP_IDLE:
520 case NC_REQUEST_SET_LOAD:
531 test_param_t param = (test_param_t)arg;
534 test_printf("Start Network Characterization - SLAVE\n");
535 calibrate_load(DESIRED_BACKGROUND_LOAD);
538 #ifdef CYGDBG_NET_TIMING_STATS
541 #if LWIP_STATS_DISPLAY
549 // This function is called to calibrate the "background load" which can be
550 // applied during testing. It will be called before any commands from the
554 calibrate_load(int desired_load)
556 long long no_load_idle, load_idle;
561 high = MAX_LOAD_THREAD_LEVEL;
562 low = MIN_LOAD_THREAD_LEVEL;
563 test_printf("Start Network Characterization - SLAVE\n");
565 // Compute the "no load" idle value
566 idle_thread_count = 0;
567 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
568 test_printf("Start Network Characterization - SLAVE\n");
569 cyg_thread_delay(1*100); // Pause for one second
570 test_printf("Start Network Characterization - SLAVE\n");
571 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
572 test_printf("Start Network Characterization - SLAVE\n");
573 no_load_idle = idle_thread_count;
574 diag_printf("No load = %d\n", (int)idle_thread_count);
576 // First ensure that the HIGH level is indeed higher
578 load_thread_level = high;
579 start_load(desired_load); // Start up a given load
580 idle_thread_count = 0;
581 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
582 cyg_thread_delay(1*100); // Pause for one second
583 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
584 load_idle = idle_thread_count;
585 start_load(0); // Shut down background load
586 percent_load = 100 - ((load_idle * 100) / no_load_idle);
587 diag_printf("High Load[%ld] = %d => %d%%\n", load_thread_level,
588 (int)idle_thread_count, percent_load);
589 if ( percent_load > desired_load )
590 break; // HIGH level is indeed higher
591 low = load_thread_level; // known to be lower
592 high *= 2; // else double it and try again
595 // Now chop down to the level required
597 load_thread_level = (high + low) / 2;
598 start_load(desired_load); // Start up a given load
599 idle_thread_count = 0;
600 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
601 cyg_thread_delay(1*100); // Pause for one second
602 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
603 load_idle = idle_thread_count;
604 start_load(0); // Shut down background load
605 percent_load = 100 - ((load_idle * 100) / no_load_idle);
606 diag_printf("Load[%ld] = %d => %d%%\n", load_thread_level,
607 (int)idle_thread_count, percent_load);
608 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
609 if (percent_load < desired_load) {
610 low = load_thread_level;
612 high = load_thread_level;
616 // Now we are within a few percent of the target; scale the load
617 // factor to get a better fit, and test it, print the answer.
618 load_thread_level *= desired_load;
619 load_thread_level /= percent_load;
620 start_load(desired_load); // Start up a given load
621 idle_thread_count = 0;
622 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
623 cyg_thread_delay(1*100); // Pause for one second
624 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
625 load_idle = idle_thread_count;
626 start_load(0); // Shut down background load
627 percent_load = 100 - ((load_idle * 100) / no_load_idle);
628 diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level,
629 (int)idle_thread_count, percent_load);
630 // no_load_idle_count_1_second = no_load_idle;
634 // This function is called to set up a load level of 'load' percent (given
635 // as a whole number, e.g. start_load(20) would mean initiate a background
636 // load of 20%, leaving the cpu 80% idle).
641 static int prev_load = 0;
643 test_printf("Set background load = %d%%\n", load);
645 if (prev_load == 0) return; // Nothing out there to stop
646 for (i = 0; i < prev_load/10; i++) {
647 cyg_semaphore_wait(&load_thread_sem[i]);
651 for (i = 0; i < load/10; i++) {
652 cyg_semaphore_post(&load_thread_sem[i]);
659 // These thread(s) do some amount of "background" computing. This is used
660 // to simulate a given load level. They need to be run at a higher priority
661 // than the network code itself.
663 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
667 net_load(cyg_addrword_t who)
671 cyg_semaphore_wait(&load_thread_sem[who]);
672 for (i = 0; i < load_thread_level; i++) {
673 do_some_random_computation(i);
675 cyg_thread_delay(1); // Wait until the next 'tick'
676 cyg_semaphore_post(&load_thread_sem[who]);
681 // Some arbitrary computation, designed to use up the CPU and cause associated
682 // cache "thrash" behaviour - part of background load modelling.
685 do_some_random_computation(int p)
687 // Just something that might be "hard"
689 x = ((p * 10) * 3.14159) / 180.0; // radians
693 // This thread does nothing but count. It will be allowed to count
694 // as long as the semaphore is "free".
697 net_idle(cyg_addrword_t param)
700 cyg_semaphore_wait(&idle_thread_sem);
702 cyg_semaphore_post(&idle_thread_sem);
710 // Create processing threads
711 for (i = 0; i < CYGHWR_NET_DRIVERS; i++) {
712 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
714 i, // entry parameter
715 "Network test", // Name
716 &main_thread_stack[i][0], // Stack
718 &main_thread_handle[i], // Handle
719 &main_thread_data[i] // Thread data structure
722 cyg_thread_resume(main_thread_handle[0]); // Start first one
723 // Create the idle thread environment
724 cyg_semaphore_init(&idle_thread_sem, 0);
725 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority
727 0, // entry parameter
728 "Network idle", // Name
729 &idle_thread_stack[0], // Stack
731 &idle_thread_handle, // Handle
732 &idle_thread_data // Thread data structure
734 cyg_thread_resume(idle_thread_handle); // Start it
735 // Create the load threads and their environment(s)
736 for (i = 0; i < NUM_LOAD_THREADS; i++) {
737 cyg_semaphore_init(&load_thread_sem[i], 0);
738 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority
740 i, // entry parameter
741 "Background load", // Name
742 &load_thread_stack[i][0], // Stack
744 &load_thread_handle[i], // Handle
745 &load_thread_data[i] // Thread data structure
747 cyg_thread_resume(load_thread_handle[i]); // Start it
749 cyg_scheduler_start();
755 tmain(cyg_addrword_t p)
758 sys_thread_new(net_test, 0, MAIN_THREAD_PRIORITY);
761 static char stack[STACK_SIZE];
762 static cyg_thread thread_data;
763 static cyg_handle_t thread_handle;
771 // Create the idle thread environment
772 cyg_semaphore_init(&idle_thread_sem, 0);
773 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority
775 0, // entry parameter
776 "Network idle", // Name
777 &idle_thread_stack[0], // Stack
779 &idle_thread_handle, // Handle
780 &idle_thread_data // Thread data structure
782 cyg_thread_resume(idle_thread_handle); // Start it
783 // Create the load threads and their environment(s)
784 for (i = 0; i < NUM_LOAD_THREADS; i++) {
785 cyg_semaphore_init(&load_thread_sem[i], 0);
786 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority
788 i, // entry parameter
789 "Background load", // Name
790 &load_thread_stack[i][0], // Stack
792 &load_thread_handle[i], // Handle
793 &load_thread_data[i] // Thread data structure
795 cyg_thread_resume(load_thread_handle[i]); // Start it
797 // Create a main thread, so we can run the scheduler and have time 'pass'
798 cyg_thread_create(10, // Priority - just a number
800 0, // entry parameter
801 "socket echo test", // Name
804 &thread_handle, // Handle
805 &thread_data // Thread data structure
807 cyg_thread_resume(thread_handle); // Start it
808 cyg_scheduler_start();
809 CYG_TEST_FAIL_FINISH("Not reached");
819 #else // def CYGPKG_LWIP_UDP
820 #define N_A_MSG "UDP support disabled"
821 #endif // def CYGPKG_LWIP_UDP
822 #else // def CYGPKG_LWIP_TCP
823 #define N_A_MSG "TCP support disabled"
824 #endif // def CYGPKG_LWIP_TCP
826 #define N_A_MSG "SO_REUSE currently unavailable"
834 CYG_TEST_NA(N_A_MSG);