]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/common/v2_0/tests/tcp_echo.c
unified MX27, MX25, MX37 trees
[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[%ld] = %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[%ld] = %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[%ld] = %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;
363     socklen_t len;
364     struct test_params params,nparams;
365     struct test_status status,nstatus;
366
367     cyg_tick_count_t starttime, stoptime;
368
369     s_source = socket(AF_INET, SOCK_STREAM, 0);
370     if (s_source < 0) {
371         pexit("stream socket");
372     }
373     if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
374         pexit("setsockopt /source/ SO_REUSEADDR");
375     }
376     if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
377         pexit("setsockopt /source/ SO_REUSEPORT");
378     }
379     memset(&local, 0, sizeof(local));
380     local.sin_family = AF_INET;
381     local.sin_len = sizeof(local);
382     local.sin_port = ntohs(SOURCE_PORT);
383     local.sin_addr.s_addr = INADDR_ANY;
384     if(bind(s_source, (struct sockaddr *) &local, sizeof(local)) < 0) {
385         pexit("bind /source/ error");
386     }
387     listen(s_source, SOMAXCONN);
388
389     s_sink = socket(AF_INET, SOCK_STREAM, 0);
390     if (s_sink < 0) {
391         pexit("stream socket");
392     }
393     memset(&local, 0, sizeof(local));
394     local.sin_family = AF_INET;
395     local.sin_len = sizeof(local);
396     local.sin_port = ntohs(SINK_PORT);
397     local.sin_addr.s_addr = INADDR_ANY;
398     if(bind(s_sink, (struct sockaddr *) &local, sizeof(local)) < 0) {
399         pexit("bind /sink/ error");
400     }
401     if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
402         pexit("setsockopt /sink/ SO_REUSEADDR");
403     }
404     if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
405         pexit("setsockopt /sink/ SO_REUSEPORT");
406     }
407     listen(s_sink, SOMAXCONN);
408
409     e_source = 0;  e_sink = 0;
410     while (true) {
411         // Wait for a connection on either of the ports
412         FD_ZERO(&in_fds);
413         FD_SET(s_source, &in_fds);
414         FD_SET(s_sink, &in_fds);
415         num = select(max(s_sink,s_source)+1, &in_fds, 0, 0, 0);
416         if (FD_ISSET(s_source, &in_fds)) {
417             len = sizeof(e_source_addr);
418             if ((e_source = accept(s_source, (struct sockaddr *)&e_source_addr, &len)) < 0) {
419                 pexit("accept /source/");
420             }
421             diag_printf("SOURCE connection from %s:%d\n", 
422                         inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port));
423         }
424         if (FD_ISSET(s_sink, &in_fds)) {
425             len = sizeof(e_sink_addr);
426             if ((e_sink = accept(s_sink, (struct sockaddr *)&e_sink_addr, &len)) < 0) {
427                 pexit("accept /sink/");
428             }
429             diag_printf("SINK connection from %s:%d\n", 
430                         inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port));
431         }
432         // Continue with test once a connection is established in both directions
433         if ((e_source != 0) && (e_sink != 0)) {
434             break;
435         }
436     }
437
438     // Wait for "source" to tell us the testing paramters
439     if (do_read(e_source, &nparams, sizeof(nparams)) != sizeof(nparams)) {
440         pexit("Can't read initialization parameters");
441     }
442   
443     params.nbufs = ntohl(nparams.nbufs);
444     params.bufsize = ntohl(nparams.bufsize);
445     params.load = ntohl(nparams.load);
446   
447     diag_printf("Using %ld buffers of %ld bytes each, %ld%% background load\n", 
448                 params.nbufs, params.bufsize, params.load);
449
450     // Tell the sink what the parameters are
451     if (do_write(e_sink, &nparams, sizeof(nparams)) != sizeof(nparams)) {
452         pexit("Can't write initialization parameters");
453     }
454
455     status.ok = 1;
456     nstatus.ok = htonl(status.ok);
457   
458     // Tell the "source" to start - we're all connected and ready to go!
459     if (do_write(e_source, &nstatus, sizeof(nstatus)) != sizeof(nstatus)) {
460         pexit("Can't send ACK to 'source' host");
461     }
462
463     idle_thread_count = 0;
464     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
465     starttime = cyg_current_time();
466     start_load(params.load);
467
468     TNR_ON();
469
470     // Echo the data from the source to the sink hosts
471     for (i = 0;  i < params.nbufs;  i++) {
472         if ((len = do_read(e_source, data_buf, params.bufsize)) != params.bufsize) {
473             TNR_OFF();
474             diag_printf("Can't read buf #%d: ", i+1);
475             if (len < 0) {
476                 perror("I/O error");
477             } else {
478                 diag_printf("short read - only %d bytes\n", len);
479             }
480             TNR_ON();
481         }
482         if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) {
483             TNR_OFF();
484             diag_printf("Can't write buf #%d: ", i+1);
485             if (len < 0) {
486                 perror("I/O error");
487             } else {
488                 diag_printf("short write - only %d bytes\n", len);
489             }
490             TNR_ON();
491         }
492     }
493
494     TNR_OFF();
495
496     // Wait for the data to drain and the "sink" to tell us all is OK.
497     if (do_read(e_sink, &status, sizeof(status)) != sizeof(status)) {
498         pexit("Can't receive ACK from 'sink' host");
499     }
500
501     start_load(0);
502     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
503     stoptime = cyg_current_time();
504     stoptime -= starttime; // time taken in cS
505     // expected idle loops in that time period for an idle system:
506     starttime = no_load_idle_count_1_second * stoptime / 100;
507     diag_printf( "%d ticks elapsed, %d kloops predicted for an idle system\n",
508                  (int)stoptime, (int)(starttime/1000) );
509     diag_printf( "actual kloops %d, CPU was %d%% idle during transfer\n",
510                  (int)(idle_thread_count/1000),
511                  (int)(idle_thread_count * 100 / starttime) );
512
513     // Now examine how close that loading actually was:
514     start_load(params.load);              // Start up a given load
515     idle_thread_count = 0;
516     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
517     cyg_thread_delay(1*100);               // Pause for one second
518     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
519     start_load(0);                         // Shut down background load
520     i = 100 - ((idle_thread_count * 100) / no_load_idle_count_1_second );
521     diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level, 
522                 (int)idle_thread_count, i);
523
524 //#ifdef CYGDBG_USE_ASSERTS
525 #ifdef CYGDBG_NET_TIMING_STATS 
526     {
527         extern void show_net_times(void);
528         show_net_times();
529     }
530 #endif
531 //#endif
532 }
533
534 void
535 net_test(cyg_addrword_t param)
536 {
537     diag_printf("Start TCP test - ECHO mode\n");
538     init_all_network_interfaces();
539     calibrate_load(DESIRED_BACKGROUND_LOAD);
540     TNR_INIT();
541 #ifdef CYGPKG_SNMPAGENT
542     {
543         extern void cyg_net_snmp_init(void);
544         cyg_net_snmp_init();
545     }
546 #endif
547     echo_test(param);
548     TNR_PRINT_ACTIVITY();
549     cyg_test_exit();
550 }
551
552 void
553 cyg_start(void)
554 {
555     int i;
556     // Create a main thread which actually runs the test
557     cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
558                       net_test,             // entry
559                       0,                    // entry parameter
560                       "Network test",       // Name
561                       &stack[0],            // Stack
562                       STACK_SIZE,           // Size
563                       &thread_handle,       // Handle
564                       &thread_data          // Thread data structure
565             );
566     cyg_thread_resume(thread_handle);  // Start it
567     // Create the idle thread environment
568     cyg_semaphore_init(&idle_thread_sem, 0);
569     cyg_thread_create(IDLE_THREAD_PRIORITY,     // Priority
570                       net_idle,                 // entry
571                       0,                        // entry parameter
572                       "Network idle",           // Name
573                       &idle_thread_stack[0],    // Stack
574                       STACK_SIZE,               // Size
575                       &idle_thread_handle,      // Handle
576                       &idle_thread_data         // Thread data structure
577             );
578     cyg_thread_resume(idle_thread_handle);      // Start it
579     // Create the load threads and their environment(s)
580     for (i = 0;  i < NUM_LOAD_THREADS;  i++) {
581         cyg_semaphore_init(&load_thread_sem[i], 0);
582         cyg_thread_create(LOAD_THREAD_PRIORITY,     // Priority
583                           net_load,                 // entry
584                           i,                        // entry parameter
585                           "Background load",        // Name
586                           &load_thread_stack[i][0], // Stack
587                           STACK_SIZE,               // Size
588                           &load_thread_handle[i],   // Handle
589                           &load_thread_data[i]      // Thread data structure
590             );
591         cyg_thread_resume(load_thread_handle[i]);   // Start it
592     }
593     cyg_scheduler_start();
594 }
595
596 // EOF tcp_echo.c