1 //==========================================================================
5 // Simple TCP throughput test - echo component
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
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:
28 // +------+ port +----+ port +----+
29 // |SOURCE|=========>|ECHO|============>|SINK|
30 // +------+ 9990 +----+ 9991 +----+
33 //####DESCRIPTIONEND####
35 //==========================================================================
37 #include <pkgconf/system.h>
38 #include <pkgconf/net.h>
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
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
51 // Fill in the blanks if necessary
61 #ifndef TNR_PRINT_ACTIVITY
62 # define TNR_PRINT_ACTIVITY()
66 // Network throughput test code
70 static __inline__ unsigned int
71 max(unsigned int m, unsigned int n)
76 #define SOURCE_PORT 9990
77 #define SINK_PORT 9991
80 static unsigned char data_buf[MAX_BUF];
92 #ifndef CYGPKG_LIBC_STDIO
93 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
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;
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
108 // starting points for load calculation
109 #define MAX_LOAD_THREAD_LEVEL 100
110 #define MIN_LOAD_THREAD_LEVEL 0
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))
127 static long long no_load_idle_count_1_second;
141 do_read(int s, void *_buf, int len)
143 int total, slen, rlen;
144 unsigned char *buf = (unsigned char *)_buf;
147 while (total < len) {
148 slen = read(s, buf, rlen);
151 diag_printf("Error after reading %d bytes\n", total);
163 do_write(int s, void *_buf, int len)
165 int total, slen, rlen;
166 unsigned char *buf = (unsigned char *)_buf;
169 while (total < len) {
170 slen = write(s, buf, rlen);
173 diag_printf("Error after writing %d bytes\n", total);
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
190 calibrate_load(int desired_load)
192 long long no_load_idle, load_idle;
197 high = MAX_LOAD_THREAD_LEVEL;
198 low = MIN_LOAD_THREAD_LEVEL;
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);
208 // First ensure that the HIGH level is indeed higher
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
227 // Now chop down to the level required
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;
244 high = load_thread_level;
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;
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).
273 static int prev_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]);
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]);
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.
297 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
301 net_load(cyg_addrword_t who)
305 cyg_semaphore_wait(&load_thread_sem[who]);
306 for (i = 0; i < load_thread_level; i++) {
307 do_some_random_computation(i,who);
309 cyg_thread_delay(1); // Wait until the next 'tick'
310 cyg_semaphore_post(&load_thread_sem[who]);
315 // Some arbitrary computation, designed to use up the CPU and cause associated
316 // cache "thrash" behaviour - part of background load modelling.
319 do_some_random_computation(int p,int id)
321 // Just something that might be "hard"
325 x = ((p * 10) * 3.14159) / 180.0; // radians
330 static int footle[0x10001];
331 static int counter = 0;
334 i = (p << 8) + id + counter++;
336 footle[ i+1 ] += footle[ i ] + 1;
342 // This thread does nothing but count. It will be allowed to count
343 // as long as the semaphore is "free".
346 net_idle(cyg_addrword_t param)
349 cyg_semaphore_wait(&idle_thread_sem);
351 cyg_semaphore_post(&idle_thread_sem);
356 echo_test(cyg_addrword_t p)
358 int s_source, s_sink, e_source, e_sink;
359 struct sockaddr_in e_source_addr, e_sink_addr, local;
364 struct test_params params,nparams;
365 struct test_status status,nstatus;
367 cyg_tick_count_t starttime, stoptime;
369 s_source = socket(AF_INET, SOCK_STREAM, 0);
371 pexit("stream socket");
373 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
374 pexit("setsockopt /source/ SO_REUSEADDR");
376 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
377 pexit("setsockopt /source/ SO_REUSEPORT");
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");
387 listen(s_source, SOMAXCONN);
389 s_sink = socket(AF_INET, SOCK_STREAM, 0);
391 pexit("stream socket");
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");
401 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
402 pexit("setsockopt /sink/ SO_REUSEADDR");
404 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
405 pexit("setsockopt /sink/ SO_REUSEPORT");
407 listen(s_sink, SOMAXCONN);
409 e_source = 0; e_sink = 0;
411 // Wait for a connection on either of the ports
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/");
421 diag_printf("SOURCE connection from %s:%d\n",
422 inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port));
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/");
429 diag_printf("SINK connection from %s:%d\n",
430 inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port));
432 // Continue with test once a connection is established in both directions
433 if ((e_source != 0) && (e_sink != 0)) {
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");
443 params.nbufs = ntohl(nparams.nbufs);
444 params.bufsize = ntohl(nparams.bufsize);
445 params.load = ntohl(nparams.load);
447 diag_printf("Using %ld buffers of %ld bytes each, %ld%% background load\n",
448 params.nbufs, params.bufsize, params.load);
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");
456 nstatus.ok = htonl(status.ok);
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");
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);
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) {
474 diag_printf("Can't read buf #%d: ", i+1);
478 diag_printf("short read - only %d bytes\n", len);
482 if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) {
484 diag_printf("Can't write buf #%d: ", i+1);
488 diag_printf("short write - only %d bytes\n", len);
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");
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) );
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);
524 //#ifdef CYGDBG_USE_ASSERTS
525 #ifdef CYGDBG_NET_TIMING_STATS
527 extern void show_net_times(void);
535 net_test(cyg_addrword_t param)
537 diag_printf("Start TCP test - ECHO mode\n");
538 init_all_network_interfaces();
539 calibrate_load(DESIRED_BACKGROUND_LOAD);
541 #ifdef CYGPKG_SNMPAGENT
543 extern void cyg_net_snmp_init(void);
548 TNR_PRINT_ACTIVITY();
556 // Create a main thread which actually runs the test
557 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
559 0, // entry parameter
560 "Network test", // Name
563 &thread_handle, // Handle
564 &thread_data // Thread data structure
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
571 0, // entry parameter
572 "Network idle", // Name
573 &idle_thread_stack[0], // Stack
575 &idle_thread_handle, // Handle
576 &idle_thread_data // Thread data structure
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
584 i, // entry parameter
585 "Background load", // Name
586 &load_thread_stack[i][0], // Stack
588 &load_thread_handle[i], // Handle
589 &load_thread_data[i] // Thread data structure
591 cyg_thread_resume(load_thread_handle[i]); // Start it
593 cyg_scheduler_start();