comparison packages/net/common/current/tests/tcp_echo.c @ 208:e0c0827131d1 ecos

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