]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/common/v2_0/tests/nc_test_slave.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / common / v2_0 / tests / nc_test_slave.c
1 //==========================================================================
2 //
3 //      tests/nc_test_slave.c
4 //
5 //      Network characterizations test (slave portion)
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:  
26 //              
27 //
28 //####DESCRIPTIONEND####
29 //
30 //==========================================================================
31
32 // Network characterization test code - slave portion
33
34 #include "nc_test_framework.h"
35 #include <math.h>
36
37 #ifdef __ECOS
38 #ifndef CYGPKG_LIBC_STDIO
39 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
40 #endif
41 #define STACK_SIZE               (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
42 #define MAX_LOAD_THREAD_LEVEL    20
43 #define MIN_LOAD_THREAD_LEVEL    0
44 #define NUM_LOAD_THREADS         10
45 #define IDLE_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY+3
46 #define LOAD_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-1
47 #define MAIN_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-2
48 #define DESIRED_BACKGROUND_LOAD  20
49 static char         main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
50 static cyg_thread   main_thread_data[CYGHWR_NET_DRIVERS];
51 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
52 static char         idle_thread_stack[STACK_SIZE];
53 static cyg_thread   idle_thread_data;
54 static cyg_handle_t idle_thread_handle;
55 static cyg_sem_t    idle_thread_sem;
56 volatile static long long    idle_thread_count;
57 static cyg_tick_count_t idle_thread_start_time;
58 static cyg_tick_count_t idle_thread_stop_time;
59 static char         load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
60 static cyg_thread   load_thread_data[NUM_LOAD_THREADS];
61 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
62 static cyg_sem_t    load_thread_sem[NUM_LOAD_THREADS];
63 static long         load_thread_level;
64 static void calibrate_load(int load);
65 static void start_load(int load);
66 static void do_some_random_computation(int p);
67 #define abs(n) ((n) < 0 ? -(n) : (n))
68 #endif
69
70 #ifdef __ECOS
71 #define test_param_t cyg_addrword_t
72 #ifdef CYGDBG_NET_TIMING_STATS
73 extern void show_net_times(void);
74 #endif
75 #else
76 #define test_param_t int
77 #endif
78
79 #define MAX_BUF 8192
80 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
81
82 #ifdef __ECOS
83 extern void
84 cyg_test_exit(void);
85 #else
86 void
87 cyg_test_exit(void)
88 {
89     test_printf("... Done\n");
90     exit(1);
91 }
92
93 static void
94 show_net_times(void)
95 {
96 }
97 #endif
98
99 #ifdef __ECOS
100 static void
101 test_delay(int ticks)
102 {
103     cyg_thread_delay(ticks);
104 }
105
106 #else
107
108 static void
109 test_delay(int ticks)
110 {
111     usleep(ticks * 10000);
112 }
113 #endif
114
115 void
116 pexit(char *s)
117 {
118     perror(s);
119 #ifdef CYGDBG_NET_TIMING_STATS
120     show_net_times();
121 #endif
122     cyg_test_exit();
123 }
124
125 //
126 // Generic UDP test
127 //
128
129 static void
130 do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
131 {
132     int i, s, td_len, seq, seq_errors, lost;
133     struct sockaddr_in test_chan_slave, test_chan_master;
134     fd_set fds;
135     struct timeval timeout;
136     struct nc_test_results results;
137     struct nc_test_data *tdp;
138     int nsent, nrecvd;
139     int need_recv, need_send;
140
141     need_recv = true;  need_send = true;
142     switch (ntohl(req->type)) {
143     case NC_REQUEST_UDP_SEND:
144         need_recv = false;
145         need_send = true;
146         break;
147     case NC_REQUEST_UDP_RECV:
148         need_recv = true;
149         need_send = false;
150         break;
151     case NC_REQUEST_UDP_ECHO:
152         break;
153     }
154
155     s = socket(AF_INET, SOCK_DGRAM, 0);
156     if (s < 0) {
157         pexit("datagram socket");
158     }
159
160     memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
161     test_chan_slave.sin_family = AF_INET;
162 #ifdef __ECOS
163     test_chan_slave.sin_len = sizeof(test_chan_slave);
164 #endif
165     test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
166     test_chan_slave.sin_port = htons(ntohl(req->slave_port));
167     
168     if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
169         perror("bind");
170         close(s);
171     }
172
173     memcpy(&test_chan_master, master, sizeof(*master));
174     test_chan_master.sin_port = htons(ntohl(req->master_port));
175     nsent = 0;  nrecvd = 0;  seq = 0;  seq_errors = 0;  lost = 0;
176     for (i = 0;  i < ntohl(req->nbufs);  i++) {
177         if (need_recv) {
178             FD_ZERO(&fds);
179             FD_SET(s, &fds);
180             timeout.tv_sec = NC_TEST_TIMEOUT;
181             timeout.tv_usec = 0;
182             if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
183                 test_printf("recvfrom timeout, expecting seq #%d\n", seq);            
184                 if (++lost > MAX_ERRORS) {
185                     test_printf("... giving up\n");
186                     break;
187                 }
188             } else {
189                 nrecvd++;
190                 tdp = (struct nc_test_data *)in_buf;
191                 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
192                 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
193                     perror("recvfrom");
194                     close(s);
195                     return;
196                 }
197                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
198                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
199                     if (ntohl(tdp->seq) != seq) {
200                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
201                                     ntohl(tdp->seq), seq);
202                         seq = ntohl(tdp->seq);
203                         seq_errors++;
204                     }
205                 } else {
206                     test_printf("Bad data packet - key: %x/%x, seq: %d\n",
207                                 ntohl(tdp->key1), ntohl(tdp->key2),
208                                 ntohl(tdp->seq));
209                 }
210             }
211         }
212         if (need_send) {
213             int retries = 10;
214             int sent = false;
215             int res;
216
217             tdp = (struct nc_test_data *)out_buf;
218             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
219             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
220             tdp->seq = htonl(seq);
221             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
222             tdp->len = htonl(td_len);
223             while (!sent && (--retries >= 0)) {
224                 res = sendto(s, tdp, td_len, 0, 
225                              (struct sockaddr *)&test_chan_master, sizeof(test_chan_master));
226                 if (res > 0) {
227                     sent = true;
228                     break;
229                 }
230                 if (errno == ENOBUFS) {
231                     // Saturated the system
232                     test_delay(1);   // Time for 200 500 byte 10-baseT packets 
233                 } else {
234                     // What else to do?
235                     close(s);
236                     return;
237                 }
238             }
239             if (sent) {
240                 nsent++;
241             } else {
242                 perror("sendto");
243             }
244         }
245         seq++;
246     }
247     results.key1 = htonl(NC_TEST_RESULT_KEY1);
248     results.key2 = htonl(NC_TEST_RESULT_KEY2);
249     results.seq = req->seq;
250     results.nsent = htonl(nsent);
251     results.nrecvd = htonl(nrecvd);
252     if (sendto(s, &results, sizeof(results), 0, 
253                (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) {
254         perror("sendto results");
255     }
256     close(s);
257 }
258
259 //
260 // Read data from a stream, accounting for the fact that packet 'boundaries'
261 // are not preserved.  This can also timeout (which would probably wreck the
262 // data boundaries).
263 //
264
265 int
266 do_read(int fd, void *buf, int buflen)
267 {
268     char *p = (char *)buf;
269     int len = buflen;
270     int res;
271     while (len) {
272         res = read(fd, p, len);
273         if (res < 0) {
274             perror("read");
275         } else {
276             len -= res;
277             p += res;
278             if (res == 0) {
279                 break;
280             }
281         }
282     }
283     return (buflen - len);
284 }
285
286 //
287 // Generic TCP test
288 //
289
290 static void
291 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
292 {
293     int i, s, td_len, seq, seq_errors, lost, test_chan, res;
294     socklen_t len;
295     struct sockaddr_in test_chan_slave, test_chan_master;
296     struct nc_test_results results;
297     struct nc_test_data *tdp;
298     int nsent, nrecvd;
299     int need_recv, need_send;
300     int one = 1;
301     static int slave_tcp_port = -1;
302
303     need_recv = true;  need_send = true;
304     switch (ntohl(req->type)) {
305     case NC_REQUEST_TCP_SEND:
306         need_recv = false;
307         need_send = true;
308         break;
309     case NC_REQUEST_TCP_RECV:
310         need_recv = true;
311         need_send = false;
312         break;
313     case NC_REQUEST_TCP_ECHO:
314         break;
315     }
316
317     if (slave_tcp_port < 0) {
318         s = socket(AF_INET, SOCK_STREAM, 0);
319         if (s < 0) {
320             pexit("datagram socket");
321         }
322
323         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
324             perror("setsockopt SO_REUSEADDR");
325             return;
326         }
327 #ifdef SO_REUSEPORT
328         if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
329             perror("setsockopt SO_REUSEPORT");
330             return;
331         }
332 #endif
333         memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
334         test_chan_slave.sin_family = AF_INET;
335 #ifdef __ECOS
336         test_chan_slave.sin_len = sizeof(test_chan_slave);
337 #endif
338         test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
339         test_chan_slave.sin_port = htons(ntohl(req->slave_port));
340     
341         if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
342             perror("bind");
343             close(s);
344         }
345         listen(s, SOMAXCONN);
346         slave_tcp_port = s;
347     }
348
349     s = slave_tcp_port;
350     len = sizeof(test_chan_master);
351     if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
352         pexit("accept");
353     }
354     len = sizeof(test_chan_master);
355     getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
356     test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), 
357                 ntohs(test_chan_master.sin_port));
358
359     nsent = 0;  nrecvd = 0;  seq = 0;  seq_errors = 0;  lost = 0;
360     for (i = 0;  i < ntohl(req->nbufs);  i++) {
361         if (need_recv) {
362             tdp = (struct nc_test_data *)in_buf;
363             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
364             res = do_read(test_chan, tdp, td_len);
365             if (res != td_len) {
366                 test_printf("recvfrom timeout, expecting seq #%d\n", seq);            
367                 if (++lost > MAX_ERRORS) {
368                     test_printf("... giving up\n");
369                     break;
370                 }
371             } else {
372                 nrecvd++;
373                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
374                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
375                     if (ntohl(tdp->seq) != seq) {
376                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
377                                     ntohl(tdp->seq), seq);
378                         seq = ntohl(tdp->seq);
379                         seq_errors++;
380                     }
381                 } else {
382                     test_printf("Bad data packet - key: %x/%x, seq: %d\n",
383                                 ntohl(tdp->key1), ntohl(tdp->key2),
384                                 ntohl(tdp->seq));
385                 }
386             }
387         }
388         if (need_send) {
389             tdp = (struct nc_test_data *)out_buf;
390             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
391             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
392             tdp->seq = htonl(seq);
393             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
394             tdp->len = htonl(td_len);
395             if (write(test_chan, tdp, td_len) != td_len) {
396                 perror("write");
397                 if (errno == ENOBUFS) {
398                     // Saturated the system
399                     test_delay(25);
400                 } else {
401                     // What else to do?
402                     close(test_chan);
403                     return;
404                 }
405             } else {
406                 nsent++;
407             }
408         }
409         seq++;
410     }
411     results.key1 = htonl(NC_TEST_RESULT_KEY1);
412     results.key2 = htonl(NC_TEST_RESULT_KEY2);
413     results.seq = req->seq;
414     results.nsent = htonl(nsent);
415     results.nrecvd = htonl(nrecvd);
416     if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
417         perror("write");
418     }
419     close(test_chan);
420 }
421
422 //
423 // Protocol driver for testing slave.
424 //
425 // This function is the main routine running here, handling requests sent from
426 // the master and providing various responses.
427 //
428 static void
429 nc_slave(test_param_t param)
430 {
431     int s;
432     socklen_t masterlen;
433     struct sockaddr_in my_addr, master;
434     struct nc_request req;
435     struct nc_reply reply;
436     int done = false;
437
438     test_printf("Start test for eth%d\n", param);
439
440     s = socket(AF_INET, SOCK_DGRAM, 0);
441     if (s < 0) {
442         pexit("datagram socket");
443     }
444
445     memset((char *) &my_addr, 0, sizeof(my_addr));
446     my_addr.sin_family = AF_INET;
447 #ifdef __ECOS
448     my_addr.sin_len = sizeof(my_addr);
449 #endif
450     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
451     my_addr.sin_port = htons(NC_SLAVE_PORT);
452     
453     if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
454         pexit("bind");
455     }
456
457     while (!done) {
458         masterlen = sizeof(master);
459         if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
460             pexit("recvfrom");
461         }
462 #if 0
463         test_printf("Request %d from %s:%d\n", ntohl(req.type), 
464                     inet_ntoa(master.sin_addr), ntohs(master.sin_port));
465 #endif
466         reply.response = htonl(NC_REPLY_ACK);
467         reply.seq = req.seq;
468         switch (ntohl(req.type)) {
469         case NC_REQUEST_DISCONNECT:
470             done = true;
471             break;
472         case NC_REQUEST_UDP_SEND:
473             test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
474             break;
475         case NC_REQUEST_UDP_RECV:
476             test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
477             break;
478         case NC_REQUEST_UDP_ECHO:
479             test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
480             break;
481         case NC_REQUEST_TCP_SEND:
482             test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
483             break;
484         case NC_REQUEST_TCP_RECV:
485             test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
486             break;
487         case NC_REQUEST_TCP_ECHO:
488             test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
489             break;
490 #ifdef __ECOS
491         case NC_REQUEST_SET_LOAD:
492             start_load(ntohl(req.nbufs));
493             break;
494         case NC_REQUEST_START_IDLE:
495             test_printf("Start IDLE thread\n");
496             idle_thread_count = 0;
497             idle_thread_start_time = cyg_current_time();
498             cyg_semaphore_post(&idle_thread_sem);
499             break;
500         case NC_REQUEST_STOP_IDLE:
501             cyg_semaphore_wait(&idle_thread_sem);
502             idle_thread_stop_time = cyg_current_time();
503             test_printf("Stop IDLE thread\n");
504             reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
505             reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
506             reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
507             break;
508 #endif
509         default:
510             test_printf("Unrecognized request: %d\n", ntohl(req.type));
511             reply.response = htonl(NC_REPLY_NAK);
512             reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
513             break;
514         }
515         if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
516             pexit("sendto");
517         }
518         if (reply.response == ntohl(NC_REPLY_NAK)) {
519             continue;
520         }
521         switch (ntohl(req.type)) {
522         case NC_REQUEST_UDP_SEND:
523         case NC_REQUEST_UDP_RECV:
524         case NC_REQUEST_UDP_ECHO:
525             do_udp_test(s, &req, &master);
526             break;
527         case NC_REQUEST_TCP_SEND:
528         case NC_REQUEST_TCP_RECV:
529         case NC_REQUEST_TCP_ECHO:
530             do_tcp_test(s, &req, &master);
531             break;
532         case NC_REQUEST_START_IDLE:
533         case NC_REQUEST_STOP_IDLE:
534         case NC_REQUEST_SET_LOAD:
535         default:
536             break;
537         }
538     }
539     close(s);
540 }
541
542 void
543 net_test(test_param_t param)
544 {
545 //    int i;
546     if (param == 0) {
547         test_printf("Start Network Characterization - SLAVE\n");
548 #ifdef __ECOS
549         init_all_network_interfaces();
550         calibrate_load(DESIRED_BACKGROUND_LOAD);
551 #if 0
552 // I can see what this is trying to do, but I get "bind: Address already in
553 // use" errors from the 2nd interface - and the parameter is not used
554 // anyway, so one thread does quite well enough (but only tests one i/f at
555 // once).
556
557 // Comment in the 'int i' above too.
558         for (i = 1;  i < CYGHWR_NET_DRIVERS;  i++) {
559             cyg_thread_resume(main_thread_handle[i]);   // Start other threads
560         }
561 #endif
562 #endif
563     }
564     nc_slave(param);
565 #ifdef CYGDBG_NET_TIMING_STATS
566     show_net_times();
567 #endif
568     cyg_test_exit();
569 }
570
571 #ifdef __ECOS
572
573 //
574 // This function is called to calibrate the "background load" which can be
575 // applied during testing.  It will be called before any commands from the
576 // host are managed.
577 //
578 static void
579 calibrate_load(int desired_load)
580 {
581     long long no_load_idle, load_idle;
582     int percent_load;
583     int high, low;
584
585     // Set limits
586     high = MAX_LOAD_THREAD_LEVEL;
587     low = MIN_LOAD_THREAD_LEVEL;
588
589     // Compute the "no load" idle value
590     idle_thread_count = 0;
591     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
592     cyg_thread_delay(1*100);               // Pause for one second
593     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
594     no_load_idle = idle_thread_count;
595     diag_printf("No load = %d\n", (int)idle_thread_count);
596
597     // First ensure that the HIGH level is indeed higher
598     while (true) {
599         load_thread_level = high;
600         start_load(desired_load);              // Start up a given load
601         idle_thread_count = 0;
602         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
603         cyg_thread_delay(1*100);               // Pause for one second
604         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
605         load_idle = idle_thread_count;
606         start_load(0);                         // Shut down background load
607         percent_load = 100 - ((load_idle * 100) / no_load_idle);
608         diag_printf("High Load[%ld] = %d => %d%%\n", load_thread_level, 
609                     (int)idle_thread_count, percent_load);
610         if ( percent_load > desired_load )
611             break; // HIGH level is indeed higher
612         low = load_thread_level; // known to be lower
613         high *= 2; // else double it and try again
614     }
615
616     // Now chop down to the level required
617     while (true) {
618         load_thread_level = (high + low) / 2;
619         start_load(desired_load);              // Start up a given load
620         idle_thread_count = 0;
621         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
622         cyg_thread_delay(1*100);               // Pause for one second
623         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
624         load_idle = idle_thread_count;
625         start_load(0);                         // Shut down background load
626         percent_load = 100 - ((load_idle * 100) / no_load_idle);
627         diag_printf("Load[%ld] = %d => %d%%\n", load_thread_level, 
628                     (int)idle_thread_count, percent_load);
629         if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
630         if (percent_load < desired_load) {
631             low = load_thread_level;
632         } else {            
633             high = load_thread_level;
634         }
635     }
636
637     // Now we are within a few percent of the target; scale the load
638     // factor to get a better fit, and test it, print the answer.
639     load_thread_level *= desired_load;
640     load_thread_level /= percent_load;
641     start_load(desired_load);              // Start up a given load
642     idle_thread_count = 0;
643     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
644     cyg_thread_delay(1*100);               // Pause for one second
645     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
646     load_idle = idle_thread_count;
647     start_load(0);                         // Shut down background load
648     percent_load = 100 - ((load_idle * 100) / no_load_idle);
649     diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level, 
650                 (int)idle_thread_count, percent_load);
651 //    no_load_idle_count_1_second = no_load_idle;
652 }
653
654 //
655 // This function is called to set up a load level of 'load' percent (given
656 // as a whole number, e.g. start_load(20) would mean initiate a background
657 // load of 20%, leaving the cpu 80% idle).
658 //
659 static void
660 start_load(int load)
661 {
662     static int prev_load = 0;
663     int i;
664     test_printf("Set background load = %d%%\n", load);
665     if (load == 0) {
666         if (prev_load == 0) return;  // Nothing out there to stop
667         for (i = 0;  i < prev_load/10;  i++) {
668             cyg_semaphore_wait(&load_thread_sem[i]);
669         }
670         prev_load = 0;
671     } else {
672         for (i = 0;  i < load/10;  i++) {
673             cyg_semaphore_post(&load_thread_sem[i]);
674         }
675         prev_load = load;
676     }
677 }
678
679 //
680 // These thread(s) do some amount of "background" computing.  This is used
681 // to simulate a given load level.  They need to be run at a higher priority 
682 // than the network code itself.
683 //
684 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
685 // is enabled.
686 //
687 void
688 net_load(cyg_addrword_t who)
689 {
690     int i;
691     while (true) {
692         cyg_semaphore_wait(&load_thread_sem[who]);
693         for (i = 0;  i < load_thread_level;  i++) {
694             do_some_random_computation(i);
695         }
696         cyg_thread_delay(1);  // Wait until the next 'tick'
697         cyg_semaphore_post(&load_thread_sem[who]);
698     }
699 }
700
701 //
702 // Some arbitrary computation, designed to use up the CPU and cause associated
703 // cache "thrash" behaviour - part of background load modelling.
704 //
705 static void
706 do_some_random_computation(int p)
707 {
708     // Just something that might be "hard"
709     volatile double x;
710     x = ((p * 10) * 3.14159) / 180.0;  // radians
711 }
712
713 //
714 // This thread does nothing but count.  It will be allowed to count
715 // as long as the semaphore is "free".  
716 //
717 void
718 net_idle(cyg_addrword_t param)
719 {
720     while (true) {
721         cyg_semaphore_wait(&idle_thread_sem);
722         idle_thread_count++;
723         cyg_semaphore_post(&idle_thread_sem);
724     }
725 }
726
727 void
728 cyg_start(void)
729 {
730     int i;
731     // Create processing threads
732     for (i = 0;  i < CYGHWR_NET_DRIVERS;  i++) {
733         cyg_thread_create(MAIN_THREAD_PRIORITY,     // Priority
734                           net_test,                 // entry
735                           i,                        // entry parameter
736                           "Network test",           // Name
737                           &main_thread_stack[i][0], // Stack
738                           STACK_SIZE,               // Size
739                           &main_thread_handle[i],   // Handle
740                           &main_thread_data[i]      // Thread data structure
741             );
742     }
743     cyg_thread_resume(main_thread_handle[0]);   // Start first one
744     // Create the idle thread environment
745     cyg_semaphore_init(&idle_thread_sem, 0);
746     cyg_thread_create(IDLE_THREAD_PRIORITY,     // Priority
747                       net_idle,                 // entry
748                       0,                        // entry parameter
749                       "Network idle",           // Name
750                       &idle_thread_stack[0],    // Stack
751                       STACK_SIZE,               // Size
752                       &idle_thread_handle,      // Handle
753                       &idle_thread_data         // Thread data structure
754             );
755     cyg_thread_resume(idle_thread_handle);      // Start it
756     // Create the load threads and their environment(s)
757     for (i = 0;  i < NUM_LOAD_THREADS;  i++) {
758         cyg_semaphore_init(&load_thread_sem[i], 0);
759         cyg_thread_create(LOAD_THREAD_PRIORITY,     // Priority
760                           net_load,                 // entry
761                           i,                        // entry parameter
762                           "Background load",        // Name
763                           &load_thread_stack[i][0], // Stack
764                           STACK_SIZE,               // Size
765                           &load_thread_handle[i],   // Handle
766                           &load_thread_data[i]      // Thread data structure
767             );
768         cyg_thread_resume(load_thread_handle[i]);   // Start it
769     }
770     cyg_scheduler_start();
771 }
772
773 #else
774
775 int 
776 main(int argc, char *argv[])
777 {
778     net_test(0);
779 }
780 #endif