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[%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
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[%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;
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[%d] = %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;
363 struct test_params params,nparams;
364 struct test_status status,nstatus;
366 cyg_tick_count_t starttime, stoptime;
368 s_source = socket(AF_INET, SOCK_STREAM, 0);
370 pexit("stream socket");
372 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
373 pexit("setsockopt /source/ SO_REUSEADDR");
375 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
376 pexit("setsockopt /source/ SO_REUSEPORT");
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");
386 listen(s_source, SOMAXCONN);
388 s_sink = socket(AF_INET, SOCK_STREAM, 0);
390 pexit("stream socket");
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");
400 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
401 pexit("setsockopt /sink/ SO_REUSEADDR");
403 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
404 pexit("setsockopt /sink/ SO_REUSEPORT");
406 listen(s_sink, SOMAXCONN);
408 e_source = 0; e_sink = 0;
410 // Wait for a connection on either of the ports
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/");
420 diag_printf("SOURCE connection from %s:%d\n",
421 inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port));
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/");
428 diag_printf("SINK connection from %s:%d\n",
429 inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port));
431 // Continue with test once a connection is established in both directions
432 if ((e_source != 0) && (e_sink != 0)) {
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");
442 params.nbufs = ntohl(nparams.nbufs);
443 params.bufsize = ntohl(nparams.bufsize);
444 params.load = ntohl(nparams.load);
446 diag_printf("Using %d buffers of %d bytes each, %d%% background load\n",
447 params.nbufs, params.bufsize, params.load);
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");
455 nstatus.ok = htonl(status.ok);
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");
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);
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) {
473 diag_printf("Can't read buf #%d: ", i+1);
477 diag_printf("short read - only %d bytes\n", len);
481 if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) {
483 diag_printf("Can't write buf #%d: ", i+1);
487 diag_printf("short write - only %d bytes\n", len);
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");
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) );
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);
523 //#ifdef CYGDBG_USE_ASSERTS
524 #ifdef CYGDBG_NET_TIMING_STATS
526 extern void show_net_times(void);
534 net_test(cyg_addrword_t param)
536 diag_printf("Start TCP test - ECHO mode\n");
537 init_all_network_interfaces();
538 calibrate_load(DESIRED_BACKGROUND_LOAD);
540 #ifdef CYGPKG_SNMPAGENT
542 extern void cyg_net_snmp_init(void);
547 TNR_PRINT_ACTIVITY();
555 // Create a main thread which actually runs the test
556 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
558 0, // entry parameter
559 "Network test", // Name
562 &thread_handle, // Handle
563 &thread_data // Thread data structure
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
570 0, // entry parameter
571 "Network idle", // Name
572 &idle_thread_stack[0], // Stack
574 &idle_thread_handle, // Handle
575 &idle_thread_data // Thread data structure
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
583 i, // entry parameter
584 "Background load", // Name
585 &load_thread_stack[i][0], // Stack
587 &load_thread_handle[i], // Handle
588 &load_thread_data[i] // Thread data structure
590 cyg_thread_resume(load_thread_handle[i]); // Start it
592 cyg_scheduler_start();