comparison packages/net/tcpip/current/tests/tcp_echo.c @ 97:ced4577552cd ecos-sw-2000-06-06

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