]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/net/common/v2_0/tests/tcp_echo.c
0748873f038d7ce039bc7e49c740bdd6975ad098
[karo-tx-redboot.git] / packages / net / common / v2_0 / tests / tcp_echo.c
1 //==========================================================================
2 //
3 //      tests/tcp_echo.c
4 //
5 //      Simple TCP throughput test - echo component
6 //
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 //
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.
14 //
15 // -------------------------------------------
16 //
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
20 //
21 // Author(s):    gthomas
22 // Contributors: gthomas
23 // Date:         2000-01-10
24 // Purpose:      
25 // Description:  This is the middle part of a three part test.  The idea is
26 //   to test the throughput of box in a configuration like this:
27 //
28 //      +------+   port   +----+     port    +----+
29 //      |SOURCE|=========>|ECHO|============>|SINK|
30 //      +------+   9990   +----+     9991    +----+
31 // 
32 //
33 //####DESCRIPTIONEND####
34 //
35 //==========================================================================
36
37 #include <pkgconf/system.h>
38 #include <pkgconf/net.h>
39
40 #ifdef CYGBLD_DEVS_ETH_DEVICE_H    // Get the device config if it exists
41 #include CYGBLD_DEVS_ETH_DEVICE_H  // May provide CYGTST_DEVS_ETH_TEST_NET_REALTIME
42 #endif
43
44 #ifdef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS // do we use the rt test?
45 # ifdef CYGTST_DEVS_ETH_TEST_NET_REALTIME // Get the test ancilla if it exists
46 #  include CYGTST_DEVS_ETH_TEST_NET_REALTIME
47 # endif
48 #endif
49
50
51 // Fill in the blanks if necessary
52 #ifndef TNR_OFF
53 # define TNR_OFF()
54 #endif
55 #ifndef TNR_ON
56 # define TNR_ON()
57 #endif
58 #ifndef TNR_INIT
59 # define TNR_INIT()
60 #endif
61 #ifndef TNR_PRINT_ACTIVITY
62 # define TNR_PRINT_ACTIVITY()
63 #endif
64
65
66 // Network throughput test code
67
68 #include <network.h>
69
70 static __inline__ unsigned int
71 max(unsigned int m, unsigned int n)
72 {
73     return m > n ? m : n;
74 }
75
76 #define SOURCE_PORT 9990
77 #define SINK_PORT   9991
78
79 #define MAX_BUF 8192
80 static unsigned char data_buf[MAX_BUF];
81
82 struct test_params {
83     long nbufs;
84     long bufsize;
85     long load;
86 };
87
88 struct test_status {
89     long ok;
90 };
91
92 #ifndef CYGPKG_LIBC_STDIO
93 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
94 #endif
95
96 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
97 static char stack[STACK_SIZE];
98 static cyg_thread thread_data;
99 static cyg_handle_t thread_handle;
100
101 // Background load stuff
102 #define NUM_LOAD_THREADS         20 // Get 5% granularity
103 #define IDLE_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY+3
104 #define LOAD_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-3
105 #define MAIN_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-4
106 #define DESIRED_BACKGROUND_LOAD  50 // should be accurate enough over range
107
108 // starting points for load calculation
109 #define MAX_LOAD_THREAD_LEVEL    100
110 #define MIN_LOAD_THREAD_LEVEL    0
111
112 static char         idle_thread_stack[STACK_SIZE];
113 static cyg_thread   idle_thread_data;
114 static cyg_handle_t idle_thread_handle;
115 static cyg_sem_t    idle_thread_sem;
116 volatile static long long    idle_thread_count;
117 static char         load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
118 static cyg_thread   load_thread_data[NUM_LOAD_THREADS];
119 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
120 static cyg_sem_t    load_thread_sem[NUM_LOAD_THREADS];
121 static long         load_thread_level;
122 static void calibrate_load(int load);
123 static void start_load(int load);
124 static void do_some_random_computation(int p,int id);
125 #define abs(n) ((n) < 0 ? -(n) : (n))
126
127 static long long no_load_idle_count_1_second;
128
129 extern void
130 cyg_test_exit(void);
131
132 void
133 pexit(char *s)
134 {
135     TNR_OFF();
136     perror(s);
137     cyg_test_exit();
138 }
139
140 int
141 do_read(int s, void *_buf, int len)
142 {
143     int total, slen, rlen;
144     unsigned char *buf = (unsigned char *)_buf;
145     total = 0;
146     rlen = len;
147     while (total < len) {
148         slen = read(s, buf, rlen);
149         if (slen != rlen) {
150             if (slen < 0) {
151                 diag_printf("Error after reading %d bytes\n", total);
152                 return -1;
153             }
154             rlen -= slen;
155             buf += slen;
156         }
157         total += slen;
158     }
159     return total;
160 }
161
162 int
163 do_write(int s, void *_buf, int len)
164 {
165     int total, slen, rlen;
166     unsigned char *buf = (unsigned char *)_buf;
167     total = 0;
168     rlen = len;
169     while (total < len) {
170         slen = write(s, buf, rlen);
171         if (slen != rlen) {
172             if (slen < 0) {
173                 diag_printf("Error after writing %d bytes\n", total);
174                 return -1;
175             }
176             rlen -= slen;
177             buf += slen;
178         }
179         total += slen;
180     }
181     return total;
182 }
183
184 //
185 // This function is called to calibrate the "background load" which can be
186 // applied during testing.  It will be called before any commands from the
187 // host are managed.
188 //
189 static void
190 calibrate_load(int desired_load)
191 {
192     long long no_load_idle, load_idle;
193     int percent_load;
194     int high, low;
195
196     // Set limits
197     high = MAX_LOAD_THREAD_LEVEL;
198     low = MIN_LOAD_THREAD_LEVEL;
199
200     // Compute the "no load" idle value
201     idle_thread_count = 0;
202     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
203     cyg_thread_delay(1*100);               // Pause for one second
204     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
205     no_load_idle = idle_thread_count;
206     diag_printf("No load = %d\n", (int)idle_thread_count);
207
208     // First ensure that the HIGH level is indeed higher
209     while (true) {
210         load_thread_level = high;
211         start_load(desired_load);              // Start up a given load
212         idle_thread_count = 0;
213         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
214         cyg_thread_delay(1*100);               // Pause for one second
215         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
216         load_idle = idle_thread_count;
217         start_load(0);                         // Shut down background load
218         percent_load = 100 - ((load_idle * 100) / no_load_idle);
219         diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level, 
220                     (int)idle_thread_count, percent_load);
221         if ( percent_load > desired_load )
222             break; // HIGH level is indeed higher
223         low = load_thread_level; // known to be lower
224         high *= 2; // else double it and try again
225     }
226
227     // Now chop down to the level required
228     while (true) {
229         load_thread_level = (high + low) / 2;
230         start_load(desired_load);              // Start up a given load
231         idle_thread_count = 0;
232         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
233         cyg_thread_delay(1*100);               // Pause for one second
234         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
235         load_idle = idle_thread_count;
236         start_load(0);                         // Shut down background load
237         percent_load = 100 - ((load_idle * 100) / no_load_idle);
238         diag_printf("Load[%d] = %d => %d%%\n", load_thread_level, 
239                     (int)idle_thread_count, percent_load);
240         if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
241         if (percent_load < desired_load) {
242             low = load_thread_level;
243         } else {            
244             high = load_thread_level;
245         }
246     }
247
248     // Now we are within a few percent of the target; scale the load
249     // factor to get a better fit, and test it, print the answer.
250     load_thread_level *= desired_load;
251     load_thread_level /= percent_load;
252     start_load(desired_load);              // Start up a given load
253     idle_thread_count = 0;
254     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
255     cyg_thread_delay(1*100);               // Pause for one second
256     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
257     load_idle = idle_thread_count;
258     start_load(0);                         // Shut down background load
259     percent_load = 100 - ((load_idle * 100) / no_load_idle);
260     diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, 
261                 (int)idle_thread_count, percent_load);
262     no_load_idle_count_1_second = no_load_idle;
263 }
264
265 //
266 // This function is called to set up a load level of 'load' percent (given
267 // as a whole number, e.g. start_load(20) would mean initiate a background
268 // load of 20%, leaving the cpu 80% idle).
269 //
270 static void
271 start_load(int load)
272 {
273     static int prev_load = 0;
274     int i;
275     if (load == 0) {
276         diag_printf("Set no background load\n");
277         if (prev_load == 0) return;  // Nothing out there to stop
278         for (i = 0;  i < prev_load * NUM_LOAD_THREADS/100;  i++) {
279             cyg_semaphore_wait(&load_thread_sem[i]);
280         }
281         prev_load = 0;
282     } else {
283         diag_printf("Set background load = %d%% starting %d threads\n",
284                     load, load * NUM_LOAD_THREADS/100 );
285         for (i = 0;  i < load * NUM_LOAD_THREADS/100;  i++) {
286             cyg_semaphore_post(&load_thread_sem[i]);
287         }
288         prev_load = load;
289     }
290 }
291
292 //
293 // These thread(s) do some amount of "background" computing.  This is used
294 // to simulate a given load level.  They need to be run at a higher priority 
295 // than the network code itself.
296 //
297 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
298 // is enabled.
299 //
300 void
301 net_load(cyg_addrword_t who)
302 {
303     int i;
304     while (true) {
305         cyg_semaphore_wait(&load_thread_sem[who]);
306         for (i = 0;  i < load_thread_level;  i++) {
307             do_some_random_computation(i,who);
308         }
309         cyg_thread_delay(1);  // Wait until the next 'tick'
310         cyg_semaphore_post(&load_thread_sem[who]);
311     }
312 }
313
314 //
315 // Some arbitrary computation, designed to use up the CPU and cause associated
316 // cache "thrash" behaviour - part of background load modelling.
317 //
318 static void
319 do_some_random_computation(int p,int id)
320 {
321     // Just something that might be "hard"
322 #if 0
323     {
324         volatile double x;
325         x = ((p * 10) * 3.14159) / 180.0;  // radians
326     }
327 #endif
328 #if 1
329     {
330         static int footle[0x10001];
331         static int counter = 0;
332         register int i;
333
334         i = (p << 8) + id + counter++;
335         i &= 0xffff;
336         footle[ i+1 ] += footle[ i ] + 1;
337     }
338 #endif
339 }
340
341 //
342 // This thread does nothing but count.  It will be allowed to count
343 // as long as the semaphore is "free".  
344 //
345 void
346 net_idle(cyg_addrword_t param)
347 {
348     while (true) {
349         cyg_semaphore_wait(&idle_thread_sem);
350         idle_thread_count++;
351         cyg_semaphore_post(&idle_thread_sem);
352     }
353 }
354
355 static void
356 echo_test(cyg_addrword_t p)
357 {
358     int s_source, s_sink, e_source, e_sink;
359     struct sockaddr_in e_source_addr, e_sink_addr, local;
360     int one = 1;
361     fd_set in_fds;
362     int i, num, len;
363     struct test_params params,nparams;
364     struct test_status status,nstatus;
365
366     cyg_tick_count_t starttime, stoptime;
367
368     s_source = socket(AF_INET, SOCK_STREAM, 0);
369     if (s_source < 0) {
370         pexit("stream socket");
371     }
372     if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
373         pexit("setsockopt /source/ SO_REUSEADDR");
374     }
375     if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
376         pexit("setsockopt /source/ SO_REUSEPORT");
377     }
378     memset(&local, 0, sizeof(local));
379     local.sin_family = AF_INET;
380     local.sin_len = sizeof(local);
381     local.sin_port = ntohs(SOURCE_PORT);
382     local.sin_addr.s_addr = INADDR_ANY;
383     if(bind(s_source, (struct sockaddr *) &local, sizeof(local)) < 0) {
384         pexit("bind /source/ error");
385     }
386     listen(s_source, SOMAXCONN);
387
388     s_sink = socket(AF_INET, SOCK_STREAM, 0);
389     if (s_sink < 0) {
390         pexit("stream socket");
391     }
392     memset(&local, 0, sizeof(local));
393     local.sin_family = AF_INET;
394     local.sin_len = sizeof(local);
395     local.sin_port = ntohs(SINK_PORT);
396     local.sin_addr.s_addr = INADDR_ANY;
397     if(bind(s_sink, (struct sockaddr *) &local, sizeof(local)) < 0) {
398         pexit("bind /sink/ error");
399     }
400     if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
401         pexit("setsockopt /sink/ SO_REUSEADDR");
402     }
403     if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
404         pexit("setsockopt /sink/ SO_REUSEPORT");
405     }
406     listen(s_sink, SOMAXCONN);
407
408     e_source = 0;  e_sink = 0;
409     while (true) {
410         // Wait for a connection on either of the ports
411         FD_ZERO(&in_fds);
412         FD_SET(s_source, &in_fds);
413         FD_SET(s_sink, &in_fds);
414         num = select(max(s_sink,s_source)+1, &in_fds, 0, 0, 0);
415         if (FD_ISSET(s_source, &in_fds)) {
416             len = sizeof(e_source_addr);
417             if ((e_source = accept(s_source, (struct sockaddr *)&e_source_addr, &len)) < 0) {
418                 pexit("accept /source/");
419             }
420             diag_printf("SOURCE connection from %s:%d\n", 
421                         inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port));
422         }
423         if (FD_ISSET(s_sink, &in_fds)) {
424             len = sizeof(e_sink_addr);
425             if ((e_sink = accept(s_sink, (struct sockaddr *)&e_sink_addr, &len)) < 0) {
426                 pexit("accept /sink/");
427             }
428             diag_printf("SINK connection from %s:%d\n", 
429                         inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port));
430         }
431         // Continue with test once a connection is established in both directions
432         if ((e_source != 0) && (e_sink != 0)) {
433             break;
434         }
435     }
436
437     // Wait for "source" to tell us the testing paramters
438     if (do_read(e_source, &nparams, sizeof(nparams)) != sizeof(nparams)) {
439         pexit("Can't read initialization parameters");
440     }
441   
442     params.nbufs = ntohl(nparams.nbufs);
443     params.bufsize = ntohl(nparams.bufsize);
444     params.load = ntohl(nparams.load);
445   
446     diag_printf("Using %d buffers of %d bytes each, %d%% background load\n", 
447                 params.nbufs, params.bufsize, params.load);
448
449     // Tell the sink what the parameters are
450     if (do_write(e_sink, &nparams, sizeof(nparams)) != sizeof(nparams)) {
451         pexit("Can't write initialization parameters");
452     }
453
454     status.ok = 1;
455     nstatus.ok = htonl(status.ok);
456   
457     // Tell the "source" to start - we're all connected and ready to go!
458     if (do_write(e_source, &nstatus, sizeof(nstatus)) != sizeof(nstatus)) {
459         pexit("Can't send ACK to 'source' host");
460     }
461
462     idle_thread_count = 0;
463     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
464     starttime = cyg_current_time();
465     start_load(params.load);
466
467     TNR_ON();
468
469     // Echo the data from the source to the sink hosts
470     for (i = 0;  i < params.nbufs;  i++) {
471         if ((len = do_read(e_source, data_buf, params.bufsize)) != params.bufsize) {
472             TNR_OFF();
473             diag_printf("Can't read buf #%d: ", i+1);
474             if (len < 0) {
475                 perror("I/O error");
476             } else {
477                 diag_printf("short read - only %d bytes\n", len);
478             }
479             TNR_ON();
480         }
481         if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) {
482             TNR_OFF();
483             diag_printf("Can't write buf #%d: ", i+1);
484             if (len < 0) {
485                 perror("I/O error");
486             } else {
487                 diag_printf("short write - only %d bytes\n", len);
488             }
489             TNR_ON();
490         }
491     }
492
493     TNR_OFF();
494
495     // Wait for the data to drain and the "sink" to tell us all is OK.
496     if (do_read(e_sink, &status, sizeof(status)) != sizeof(status)) {
497         pexit("Can't receive ACK from 'sink' host");
498     }
499
500     start_load(0);
501     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
502     stoptime = cyg_current_time();
503     stoptime -= starttime; // time taken in cS
504     // expected idle loops in that time period for an idle system:
505     starttime = no_load_idle_count_1_second * stoptime / 100;
506     diag_printf( "%d ticks elapsed, %d kloops predicted for an idle system\n",
507                  (int)stoptime, (int)(starttime/1000) );
508     diag_printf( "actual kloops %d, CPU was %d%% idle during transfer\n",
509                  (int)(idle_thread_count/1000),
510                  (int)(idle_thread_count * 100 / starttime) );
511
512     // Now examine how close that loading actually was:
513     start_load(params.load);              // Start up a given load
514     idle_thread_count = 0;
515     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
516     cyg_thread_delay(1*100);               // Pause for one second
517     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
518     start_load(0);                         // Shut down background load
519     i = 100 - ((idle_thread_count * 100) / no_load_idle_count_1_second );
520     diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, 
521                 (int)idle_thread_count, i);
522
523 //#ifdef CYGDBG_USE_ASSERTS
524 #ifdef CYGDBG_NET_TIMING_STATS 
525     {
526         extern void show_net_times(void);
527         show_net_times();
528     }
529 #endif
530 //#endif
531 }
532
533 void
534 net_test(cyg_addrword_t param)
535 {
536     diag_printf("Start TCP test - ECHO mode\n");
537     init_all_network_interfaces();
538     calibrate_load(DESIRED_BACKGROUND_LOAD);
539     TNR_INIT();
540 #ifdef CYGPKG_SNMPAGENT
541     {
542         extern void cyg_net_snmp_init(void);
543         cyg_net_snmp_init();
544     }
545 #endif
546     echo_test(param);
547     TNR_PRINT_ACTIVITY();
548     cyg_test_exit();
549 }
550
551 void
552 cyg_start(void)
553 {
554     int i;
555     // Create a main thread which actually runs the test
556     cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
557                       net_test,             // entry
558                       0,                    // entry parameter
559                       "Network test",       // Name
560                       &stack[0],            // Stack
561                       STACK_SIZE,           // Size
562                       &thread_handle,       // Handle
563                       &thread_data          // Thread data structure
564             );
565     cyg_thread_resume(thread_handle);  // Start it
566     // Create the idle thread environment
567     cyg_semaphore_init(&idle_thread_sem, 0);
568     cyg_thread_create(IDLE_THREAD_PRIORITY,     // Priority
569                       net_idle,                 // entry
570                       0,                        // entry parameter
571                       "Network idle",           // Name
572                       &idle_thread_stack[0],    // Stack
573                       STACK_SIZE,               // Size
574                       &idle_thread_handle,      // Handle
575                       &idle_thread_data         // Thread data structure
576             );
577     cyg_thread_resume(idle_thread_handle);      // Start it
578     // Create the load threads and their environment(s)
579     for (i = 0;  i < NUM_LOAD_THREADS;  i++) {
580         cyg_semaphore_init(&load_thread_sem[i], 0);
581         cyg_thread_create(LOAD_THREAD_PRIORITY,     // Priority
582                           net_load,                 // entry
583                           i,                        // entry parameter
584                           "Background load",        // Name
585                           &load_thread_stack[i][0], // Stack
586                           STACK_SIZE,               // Size
587                           &load_thread_handle[i],   // Handle
588                           &load_thread_data[i]      // Thread data structure
589             );
590         cyg_thread_resume(load_thread_handle[i]);   // Start it
591     }
592     cyg_scheduler_start();
593 }
594
595 // EOF tcp_echo.c