1 //==========================================================================
3 // tests/nc_test_slave.c
5 // Network characterizations test (slave portion)
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Portions created by Nick Garnett are
12 // Copyright (C) 2003 eCosCentric Ltd.
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
37 // -------------------------------------------
38 //####ECOSGPLCOPYRIGHTEND####
39 //####BSDCOPYRIGHTBEGIN####
41 // -------------------------------------------
43 // Portions of this software may have been derived from OpenBSD,
44 // FreeBSD or other sources, and are covered by the appropriate
45 // copyright disclaimers included herein.
47 // -------------------------------------------
49 //####BSDCOPYRIGHTEND####
50 //==========================================================================
51 //#####DESCRIPTIONBEGIN####
54 // Contributors: gthomas
60 //####DESCRIPTIONEND####
62 //==========================================================================
64 // Network characterization test code - slave portion
66 #include <cyg/ppp/ppp.h>
68 #include <cyg/infra/testcase.h>
70 #include "nc_test_framework.h"
75 #include "ppp_test_support.inl"
77 #ifndef CYGPKG_LIBC_STDIO
78 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
80 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
81 #define MAX_LOAD_THREAD_LEVEL 20
82 #define MIN_LOAD_THREAD_LEVEL 0
83 #define NUM_LOAD_THREADS 10
84 #define IDLE_THREAD_PRIORITY CYGNUM_PPP_PPPD_THREAD_PRIORITY+3
85 #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1
86 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2
87 #define DESIRED_BACKGROUND_LOAD 20
88 static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
89 static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS];
90 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
91 static char idle_thread_stack[STACK_SIZE];
92 static cyg_thread idle_thread_data;
93 static cyg_handle_t idle_thread_handle;
94 static cyg_sem_t idle_thread_sem;
95 volatile static long long idle_thread_count;
96 static cyg_tick_count_t idle_thread_start_time;
97 static cyg_tick_count_t idle_thread_stop_time;
98 static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
99 static cyg_thread load_thread_data[NUM_LOAD_THREADS];
100 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
101 static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS];
102 static long load_thread_level;
103 static void calibrate_load(int load);
104 static void start_load(int load);
105 static void do_some_random_computation(int p);
106 #define abs(n) ((n) < 0 ? -(n) : (n))
110 #define test_param_t cyg_addrword_t
111 #ifdef CYGDBG_NET_TIMING_STATS
112 extern void show_net_times(void);
115 #define test_param_t int
119 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
128 test_printf("... Done\n");
140 test_delay(int ticks)
142 cyg_thread_delay(ticks);
148 test_delay(int ticks)
150 usleep(ticks * 10000);
158 #ifdef CYGDBG_NET_TIMING_STATS
169 do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
171 int i, s, td_len, seq, seq_errors, lost;
172 struct sockaddr_in test_chan_slave, test_chan_master;
174 struct timeval timeout;
175 struct nc_test_results results;
176 struct nc_test_data *tdp;
178 int need_recv, need_send;
180 need_recv = true; need_send = true;
181 switch (ntohl(req->type)) {
182 case NC_REQUEST_UDP_SEND:
186 case NC_REQUEST_UDP_RECV:
190 case NC_REQUEST_UDP_ECHO:
194 s = socket(AF_INET, SOCK_DGRAM, 0);
196 pexit("datagram socket");
199 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
200 test_chan_slave.sin_family = AF_INET;
202 test_chan_slave.sin_len = sizeof(test_chan_slave);
204 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
205 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
207 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
212 memcpy(&test_chan_master, master, sizeof(*master));
213 test_chan_master.sin_port = htons(ntohl(req->master_port));
214 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
215 for (i = 0; i < ntohl(req->nbufs); i++) {
219 timeout.tv_sec = NC_TEST_TIMEOUT;
221 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
222 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
223 if (++lost > MAX_ERRORS) {
224 test_printf("... giving up\n");
229 tdp = (struct nc_test_data *)in_buf;
230 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
231 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
236 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
237 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
238 if (ntohl(tdp->seq) != seq) {
239 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
240 ntohl(tdp->seq), seq);
241 seq = ntohl(tdp->seq);
245 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
246 ntohl(tdp->key1), ntohl(tdp->key2),
256 tdp = (struct nc_test_data *)out_buf;
257 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
258 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
259 tdp->seq = htonl(seq);
260 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
261 tdp->len = htonl(td_len);
262 while (!sent && (--retries >= 0)) {
263 res = sendto(s, tdp, td_len, 0,
264 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master));
269 if (errno == ENOBUFS) {
270 // Saturated the system
271 test_delay(1); // Time for 200 500 byte 10-baseT packets
286 results.key1 = htonl(NC_TEST_RESULT_KEY1);
287 results.key2 = htonl(NC_TEST_RESULT_KEY2);
288 results.seq = req->seq;
289 results.nsent = htonl(nsent);
290 results.nrecvd = htonl(nrecvd);
291 if (sendto(s, &results, sizeof(results), 0,
292 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) {
293 perror("sendto results");
299 // Read data from a stream, accounting for the fact that packet 'boundaries'
300 // are not preserved. This can also timeout (which would probably wreck the
305 do_read(int fd, void *buf, int buflen)
307 char *p = (char *)buf;
311 res = read(fd, p, len);
322 return (buflen - len);
330 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
332 int i, s, len, td_len, seq, seq_errors, lost, test_chan, res;
333 struct sockaddr_in test_chan_slave, test_chan_master;
334 struct nc_test_results results;
335 struct nc_test_data *tdp;
337 int need_recv, need_send;
339 static int slave_tcp_port = -1;
341 need_recv = true; need_send = true;
342 switch (ntohl(req->type)) {
343 case NC_REQUEST_TCP_SEND:
347 case NC_REQUEST_TCP_RECV:
351 case NC_REQUEST_TCP_ECHO:
355 if (slave_tcp_port < 0) {
356 s = socket(AF_INET, SOCK_STREAM, 0);
358 pexit("datagram socket");
361 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
362 perror("setsockopt SO_REUSEADDR");
366 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
367 perror("setsockopt SO_REUSEPORT");
371 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
372 test_chan_slave.sin_family = AF_INET;
374 test_chan_slave.sin_len = sizeof(test_chan_slave);
376 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
377 test_chan_slave.sin_port = htons(ntohl(req->slave_port));
379 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
383 listen(s, SOMAXCONN);
388 len = sizeof(test_chan_master);
389 if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
392 len = sizeof(test_chan_master);
393 getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
394 test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr),
395 ntohs(test_chan_master.sin_port));
397 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
398 for (i = 0; i < ntohl(req->nbufs); i++) {
400 tdp = (struct nc_test_data *)in_buf;
401 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
402 res = do_read(test_chan, tdp, td_len);
404 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
405 if (++lost > MAX_ERRORS) {
406 test_printf("... giving up\n");
411 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
412 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
413 if (ntohl(tdp->seq) != seq) {
414 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
415 ntohl(tdp->seq), seq);
416 seq = ntohl(tdp->seq);
420 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
421 ntohl(tdp->key1), ntohl(tdp->key2),
427 tdp = (struct nc_test_data *)out_buf;
428 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
429 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
430 tdp->seq = htonl(seq);
431 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
432 tdp->len = htonl(td_len);
433 if (write(test_chan, tdp, td_len) != td_len) {
435 if (errno == ENOBUFS) {
436 // Saturated the system
449 results.key1 = htonl(NC_TEST_RESULT_KEY1);
450 results.key2 = htonl(NC_TEST_RESULT_KEY2);
451 results.seq = req->seq;
452 results.nsent = htonl(nsent);
453 results.nrecvd = htonl(nrecvd);
454 if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
461 // Protocol driver for testing slave.
463 // This function is the main routine running here, handling requests sent from
464 // the master and providing various responses.
467 nc_slave(test_param_t param)
470 struct sockaddr_in my_addr, master;
471 struct nc_request req;
472 struct nc_reply reply;
475 test_printf("Start test for eth%d\n", param);
477 s = socket(AF_INET, SOCK_DGRAM, 0);
479 pexit("datagram socket");
482 memset((char *) &my_addr, 0, sizeof(my_addr));
483 my_addr.sin_family = AF_INET;
485 my_addr.sin_len = sizeof(my_addr);
487 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
488 my_addr.sin_port = htons(NC_SLAVE_PORT);
490 if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
495 masterlen = sizeof(master);
496 if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
500 test_printf("Request %d from %s:%d\n", ntohl(req.type),
501 inet_ntoa(master.sin_addr), ntohs(master.sin_port));
503 reply.response = htonl(NC_REPLY_ACK);
505 switch (ntohl(req.type)) {
506 case NC_REQUEST_DISCONNECT:
509 case NC_REQUEST_UDP_SEND:
510 test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
512 case NC_REQUEST_UDP_RECV:
513 test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
515 case NC_REQUEST_UDP_ECHO:
516 test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
518 case NC_REQUEST_TCP_SEND:
519 test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
521 case NC_REQUEST_TCP_RECV:
522 test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
524 case NC_REQUEST_TCP_ECHO:
525 test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
528 case NC_REQUEST_SET_LOAD:
529 start_load(ntohl(req.nbufs));
531 case NC_REQUEST_START_IDLE:
532 test_printf("Start IDLE thread\n");
533 idle_thread_count = 0;
534 idle_thread_start_time = cyg_current_time();
535 cyg_semaphore_post(&idle_thread_sem);
537 case NC_REQUEST_STOP_IDLE:
538 cyg_semaphore_wait(&idle_thread_sem);
539 idle_thread_stop_time = cyg_current_time();
540 test_printf("Stop IDLE thread\n");
541 reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
542 reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
543 reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
547 test_printf("Unrecognized request: %d\n", ntohl(req.type));
548 reply.response = htonl(NC_REPLY_NAK);
549 reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
552 if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
555 if (reply.response == ntohl(NC_REPLY_NAK)) {
558 switch (ntohl(req.type)) {
559 case NC_REQUEST_UDP_SEND:
560 case NC_REQUEST_UDP_RECV:
561 case NC_REQUEST_UDP_ECHO:
562 do_udp_test(s, &req, &master);
564 case NC_REQUEST_TCP_SEND:
565 case NC_REQUEST_TCP_RECV:
566 case NC_REQUEST_TCP_ECHO:
567 do_tcp_test(s, &req, &master);
569 case NC_REQUEST_START_IDLE:
570 case NC_REQUEST_STOP_IDLE:
571 case NC_REQUEST_SET_LOAD:
580 net_test(test_param_t param)
583 cyg_serial_baud_rate_t old;
584 cyg_ppp_options_t options;
585 cyg_ppp_handle_t ppp_handle;
592 test_printf("Start Network Characterization - SLAVE\n");
594 init_all_network_interfaces();
595 calibrate_load(DESIRED_BACKGROUND_LOAD);
597 // I can see what this is trying to do, but I get "bind: Address already in
598 // use" errors from the 2nd interface - and the parameter is not used
599 // anyway, so one thread does quite well enough (but only tests one i/f at
602 // Comment in the 'int i' above too.
603 for (i = 1; i < CYGHWR_NET_DRIVERS; i++) {
604 cyg_thread_resume(main_thread_handle[i]); // Start other threads
612 old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 );
614 ppp_test_announce( "NC_TEST_SLAVE" );
616 cyg_ppp_options_init( &options );
618 // options.debug = 1;
619 // options.kdebugflag = 1;
621 // options.script = script;
622 // options.flowctl = CYG_PPP_FLOWCTL_SOFTWARE;
624 ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options );
626 CYG_TEST_INFO( "Waiting for PPP to come up");
628 cyg_ppp_wait_up( ppp_handle );
632 #ifdef CYGDBG_NET_TIMING_STATS
637 CYG_TEST_INFO( "Bringing PPP down");
639 cyg_ppp_down( ppp_handle );
641 CYG_TEST_INFO( "Waiting for PPP to go down");
643 cyg_ppp_wait_down( ppp_handle );
645 cyg_thread_delay( 200 );
647 ppp_test_set_baud( old );
651 CYG_TEST_PASS_FINISH( "Network Characterization - SLAVE" );
661 // This function is called to calibrate the "background load" which can be
662 // applied during testing. It will be called before any commands from the
666 calibrate_load(int desired_load)
668 long long no_load_idle, load_idle;
673 high = MAX_LOAD_THREAD_LEVEL;
674 low = MIN_LOAD_THREAD_LEVEL;
676 // Compute the "no load" idle value
677 idle_thread_count = 0;
678 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
679 cyg_thread_delay(1*100); // Pause for one second
680 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
681 no_load_idle = idle_thread_count;
682 diag_printf("No load = %d\n", (int)idle_thread_count);
684 // First ensure that the HIGH level is indeed higher
686 load_thread_level = high;
687 start_load(desired_load); // Start up a given load
688 idle_thread_count = 0;
689 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
690 cyg_thread_delay(1*100); // Pause for one second
691 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
692 load_idle = idle_thread_count;
693 start_load(0); // Shut down background load
694 percent_load = 100 - ((load_idle * 100) / no_load_idle);
695 diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level,
696 (int)idle_thread_count, percent_load);
697 if ( percent_load > desired_load )
698 break; // HIGH level is indeed higher
699 low = load_thread_level; // known to be lower
700 high *= 2; // else double it and try again
703 // Now chop down to the level required
705 load_thread_level = (high + low) / 2;
706 start_load(desired_load); // Start up a given load
707 idle_thread_count = 0;
708 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
709 cyg_thread_delay(1*100); // Pause for one second
710 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
711 load_idle = idle_thread_count;
712 start_load(0); // Shut down background load
713 percent_load = 100 - ((load_idle * 100) / no_load_idle);
714 diag_printf("Load[%d] = %d => %d%%\n", load_thread_level,
715 (int)idle_thread_count, percent_load);
716 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
717 if (percent_load < desired_load) {
718 low = load_thread_level;
720 high = load_thread_level;
724 // Now we are within a few percent of the target; scale the load
725 // factor to get a better fit, and test it, print the answer.
726 load_thread_level *= desired_load;
727 load_thread_level /= percent_load;
728 start_load(desired_load); // Start up a given load
729 idle_thread_count = 0;
730 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
731 cyg_thread_delay(1*100); // Pause for one second
732 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
733 load_idle = idle_thread_count;
734 start_load(0); // Shut down background load
735 percent_load = 100 - ((load_idle * 100) / no_load_idle);
736 diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level,
737 (int)idle_thread_count, percent_load);
738 // no_load_idle_count_1_second = no_load_idle;
742 // This function is called to set up a load level of 'load' percent (given
743 // as a whole number, e.g. start_load(20) would mean initiate a background
744 // load of 20%, leaving the cpu 80% idle).
749 static int prev_load = 0;
751 test_printf("Set background load = %d%%\n", load);
753 if (prev_load == 0) return; // Nothing out there to stop
754 for (i = 0; i < prev_load/10; i++) {
755 cyg_semaphore_wait(&load_thread_sem[i]);
759 for (i = 0; i < load/10; i++) {
760 cyg_semaphore_post(&load_thread_sem[i]);
767 // These thread(s) do some amount of "background" computing. This is used
768 // to simulate a given load level. They need to be run at a higher priority
769 // than the network code itself.
771 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
775 net_load(cyg_addrword_t who)
779 cyg_semaphore_wait(&load_thread_sem[who]);
780 for (i = 0; i < load_thread_level; i++) {
781 do_some_random_computation(i);
783 cyg_thread_delay(1); // Wait until the next 'tick'
784 cyg_semaphore_post(&load_thread_sem[who]);
789 // Some arbitrary computation, designed to use up the CPU and cause associated
790 // cache "thrash" behaviour - part of background load modelling.
793 do_some_random_computation(int p)
795 // Just something that might be "hard"
797 x = ((p * 10) * 3.14159) / 180.0; // radians
801 // This thread does nothing but count. It will be allowed to count
802 // as long as the semaphore is "free".
805 net_idle(cyg_addrword_t param)
808 cyg_semaphore_wait(&idle_thread_sem);
810 cyg_semaphore_post(&idle_thread_sem);
818 // Create processing threads
819 for (i = 0; i < CYGHWR_NET_DRIVERS; i++) {
820 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
822 i, // entry parameter
823 "Network test", // Name
824 &main_thread_stack[i][0], // Stack
826 &main_thread_handle[i], // Handle
827 &main_thread_data[i] // Thread data structure
830 cyg_thread_resume(main_thread_handle[0]); // Start first one
831 // Create the idle thread environment
832 cyg_semaphore_init(&idle_thread_sem, 0);
833 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority
835 0, // entry parameter
836 "Network idle", // Name
837 &idle_thread_stack[0], // Stack
839 &idle_thread_handle, // Handle
840 &idle_thread_data // Thread data structure
842 cyg_thread_resume(idle_thread_handle); // Start it
843 // Create the load threads and their environment(s)
844 for (i = 0; i < NUM_LOAD_THREADS; i++) {
845 cyg_semaphore_init(&load_thread_sem[i], 0);
846 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority
848 i, // entry parameter
849 "Background load", // Name
850 &load_thread_stack[i][0], // Stack
852 &load_thread_handle[i], // Handle
853 &load_thread_data[i] // Thread data structure
855 cyg_thread_resume(load_thread_handle[i]); // Start it
857 cyg_scheduler_start();
863 main(int argc, char *argv[])