Mercurial > ecos
comparison packages/net/common/current/tests/nc_test_slave.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 | 75bb10dfa0df |
comparison
equal
deleted
inserted
replaced
| 207:74c807ddde34 | 208:e0c0827131d1 |
|---|---|
| 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 tdp = (struct nc_test_data *)out_buf; | |
| 214 tdp->key1 = htonl(NC_TEST_DATA_KEY1); | |
| 215 tdp->key2 = htonl(NC_TEST_DATA_KEY2); | |
| 216 tdp->seq = htonl(seq); | |
| 217 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); | |
| 218 tdp->len = htonl(td_len); | |
| 219 if (sendto(s, tdp, td_len, 0, | |
| 220 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) { | |
| 221 perror("sendto"); | |
| 222 if (errno == ENOBUFS) { | |
| 223 // Saturated the system | |
| 224 test_delay(10); // Time for 200 500 byte 10-baseT packets | |
| 225 } else { | |
| 226 // What else to do? | |
| 227 close(s); | |
| 228 return; | |
| 229 } | |
| 230 } else { | |
| 231 nsent++; | |
| 232 } | |
| 233 } | |
| 234 seq++; | |
| 235 } | |
| 236 results.key1 = htonl(NC_TEST_RESULT_KEY1); | |
| 237 results.key2 = htonl(NC_TEST_RESULT_KEY2); | |
| 238 results.seq = req->seq; | |
| 239 results.nsent = htonl(nsent); | |
| 240 results.nrecvd = htonl(nrecvd); | |
| 241 if (sendto(s, &results, sizeof(results), 0, | |
| 242 (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) { | |
| 243 perror("sendto"); | |
| 244 } | |
| 245 close(s); | |
| 246 } | |
| 247 | |
| 248 // | |
| 249 // Read data from a stream, accounting for the fact that packet 'boundaries' | |
| 250 // are not preserved. This can also timeout (which would probably wreck the | |
| 251 // data boundaries). | |
| 252 // | |
| 253 | |
| 254 int | |
| 255 do_read(int fd, void *buf, int buflen) | |
| 256 { | |
| 257 char *p = (char *)buf; | |
| 258 int len = buflen; | |
| 259 int res; | |
| 260 while (len) { | |
| 261 res = read(fd, p, len); | |
| 262 if (res < 0) { | |
| 263 perror("read"); | |
| 264 } else { | |
| 265 len -= res; | |
| 266 p += res; | |
| 267 if (res == 0) { | |
| 268 break; | |
| 269 } | |
| 270 } | |
| 271 } | |
| 272 return (buflen - len); | |
| 273 } | |
| 274 | |
| 275 // | |
| 276 // Generic TCP test | |
| 277 // | |
| 278 | |
| 279 static void | |
| 280 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master) | |
| 281 { | |
| 282 int i, s, len, td_len, seq, seq_errors, lost, test_chan, res; | |
| 283 struct sockaddr_in test_chan_slave, test_chan_master; | |
| 284 struct nc_test_results results; | |
| 285 struct nc_test_data *tdp; | |
| 286 int nsent, nrecvd; | |
| 287 int need_recv, need_send; | |
| 288 int one = 1; | |
| 289 static int slave_tcp_port = -1; | |
| 290 | |
| 291 need_recv = true; need_send = true; | |
| 292 switch (ntohl(req->type)) { | |
| 293 case NC_REQUEST_TCP_SEND: | |
| 294 need_recv = false; | |
| 295 need_send = true; | |
| 296 break; | |
| 297 case NC_REQUEST_TCP_RECV: | |
| 298 need_recv = true; | |
| 299 need_send = false; | |
| 300 break; | |
| 301 case NC_REQUEST_TCP_ECHO: | |
| 302 break; | |
| 303 } | |
| 304 | |
| 305 if (slave_tcp_port < 0) { | |
| 306 s = socket(AF_INET, SOCK_STREAM, 0); | |
| 307 if (s < 0) { | |
| 308 pexit("datagram socket"); | |
| 309 } | |
| 310 | |
| 311 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { | |
| 312 perror("setsockopt SO_REUSEADDR"); | |
| 313 return; | |
| 314 } | |
| 315 #ifdef SO_REUSEPORT | |
| 316 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { | |
| 317 perror("setsockopt SO_REUSEPORT"); | |
| 318 return; | |
| 319 } | |
| 320 #endif | |
| 321 memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave)); | |
| 322 test_chan_slave.sin_family = AF_INET; | |
| 323 #ifdef __ECOS | |
| 324 test_chan_slave.sin_len = sizeof(test_chan_slave); | |
| 325 #endif | |
| 326 test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY); | |
| 327 test_chan_slave.sin_port = htons(ntohl(req->slave_port)); | |
| 328 | |
| 329 if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) { | |
| 330 perror("bind"); | |
| 331 close(s); | |
| 332 } | |
| 333 listen(s, SOMAXCONN); | |
| 334 slave_tcp_port = s; | |
| 335 } | |
| 336 | |
| 337 s = slave_tcp_port; | |
| 338 len = sizeof(test_chan_master); | |
| 339 if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) { | |
| 340 pexit("accept"); | |
| 341 } | |
| 342 len = sizeof(test_chan_master); | |
| 343 getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len); | |
| 344 test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), | |
| 345 ntohs(test_chan_master.sin_port)); | |
| 346 | |
| 347 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0; | |
| 348 for (i = 0; i < ntohl(req->nbufs); i++) { | |
| 349 if (need_recv) { | |
| 350 tdp = (struct nc_test_data *)in_buf; | |
| 351 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); | |
| 352 res = do_read(test_chan, tdp, td_len); | |
| 353 if (res != td_len) { | |
| 354 test_printf("recvfrom timeout, expecting seq #%d\n", seq); | |
| 355 if (++lost > MAX_ERRORS) { | |
| 356 test_printf("... giving up\n"); | |
| 357 break; | |
| 358 } | |
| 359 } else { | |
| 360 nrecvd++; | |
| 361 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) && | |
| 362 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) { | |
| 363 if (ntohl(tdp->seq) != seq) { | |
| 364 test_printf("Packets out of sequence - recvd: %d, expected: %d\n", | |
| 365 ntohl(tdp->seq), seq); | |
| 366 seq = ntohl(tdp->seq); | |
| 367 seq_errors++; | |
| 368 } | |
| 369 } else { | |
| 370 test_printf("Bad data packet - key: %x/%x, seq: %d\n", | |
| 371 ntohl(tdp->key1), ntohl(tdp->key2), | |
| 372 ntohl(tdp->seq)); | |
| 373 } | |
| 374 } | |
| 375 } | |
| 376 if (need_send) { | |
| 377 tdp = (struct nc_test_data *)out_buf; | |
| 378 tdp->key1 = htonl(NC_TEST_DATA_KEY1); | |
| 379 tdp->key2 = htonl(NC_TEST_DATA_KEY2); | |
| 380 tdp->seq = htonl(seq); | |
| 381 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); | |
| 382 tdp->len = htonl(td_len); | |
| 383 if (write(test_chan, tdp, td_len) != td_len) { | |
| 384 perror("write"); | |
| 385 if (errno == ENOBUFS) { | |
| 386 // Saturated the system | |
| 387 test_delay(25); | |
| 388 } else { | |
| 389 // What else to do? | |
| 390 close(test_chan); | |
| 391 return; | |
| 392 } | |
| 393 } else { | |
| 394 nsent++; | |
| 395 } | |
| 396 } | |
| 397 seq++; | |
| 398 } | |
| 399 results.key1 = htonl(NC_TEST_RESULT_KEY1); | |
| 400 results.key2 = htonl(NC_TEST_RESULT_KEY2); | |
| 401 results.seq = req->seq; | |
| 402 results.nsent = htonl(nsent); | |
| 403 results.nrecvd = htonl(nrecvd); | |
| 404 if (write(test_chan, &results, sizeof(results)) != sizeof(results)) { | |
| 405 perror("write"); | |
| 406 } | |
| 407 close(test_chan); | |
| 408 } | |
| 409 | |
| 410 // | |
| 411 // Protocol driver for testing slave. | |
| 412 // | |
| 413 // This function is the main routine running here, handling requests sent from | |
| 414 // the master and providing various responses. | |
| 415 // | |
| 416 static void | |
| 417 nc_slave(test_param_t param) | |
| 418 { | |
| 419 int s, masterlen; | |
| 420 struct sockaddr_in my_addr, master; | |
| 421 struct nc_request req; | |
| 422 struct nc_reply reply; | |
| 423 int done = false; | |
| 424 | |
| 425 test_printf("Start test for eth%d\n", param); | |
| 426 | |
| 427 s = socket(AF_INET, SOCK_DGRAM, 0); | |
| 428 if (s < 0) { | |
| 429 pexit("datagram socket"); | |
| 430 } | |
| 431 | |
| 432 memset((char *) &my_addr, 0, sizeof(my_addr)); | |
| 433 my_addr.sin_family = AF_INET; | |
| 434 #ifdef __ECOS | |
| 435 my_addr.sin_len = sizeof(my_addr); | |
| 436 #endif | |
| 437 my_addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
| 438 my_addr.sin_port = htons(NC_SLAVE_PORT); | |
| 439 | |
| 440 if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) { | |
| 441 pexit("bind"); | |
| 442 } | |
| 443 | |
| 444 while (!done) { | |
| 445 masterlen = sizeof(master); | |
| 446 if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) { | |
| 447 pexit("recvfrom"); | |
| 448 } | |
| 449 #if 0 | |
| 450 test_printf("Request %d from %s:%d\n", ntohl(req.type), | |
| 451 inet_ntoa(master.sin_addr), ntohs(master.sin_port)); | |
| 452 #endif | |
| 453 reply.response = htonl(NC_REPLY_ACK); | |
| 454 reply.seq = req.seq; | |
| 455 switch (ntohl(req.type)) { | |
| 456 case NC_REQUEST_DISCONNECT: | |
| 457 done = true; | |
| 458 break; | |
| 459 case NC_REQUEST_UDP_SEND: | |
| 460 test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 461 break; | |
| 462 case NC_REQUEST_UDP_RECV: | |
| 463 test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 464 break; | |
| 465 case NC_REQUEST_UDP_ECHO: | |
| 466 test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 467 break; | |
| 468 case NC_REQUEST_TCP_SEND: | |
| 469 test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 470 break; | |
| 471 case NC_REQUEST_TCP_RECV: | |
| 472 test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 473 break; | |
| 474 case NC_REQUEST_TCP_ECHO: | |
| 475 test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); | |
| 476 break; | |
| 477 #ifdef __ECOS | |
| 478 case NC_REQUEST_SET_LOAD: | |
| 479 start_load(ntohl(req.nbufs)); | |
| 480 break; | |
| 481 case NC_REQUEST_START_IDLE: | |
| 482 test_printf("Start IDLE thread\n"); | |
| 483 idle_thread_count = 0; | |
| 484 idle_thread_start_time = cyg_current_time(); | |
| 485 cyg_semaphore_post(&idle_thread_sem); | |
| 486 break; | |
| 487 case NC_REQUEST_STOP_IDLE: | |
| 488 cyg_semaphore_wait(&idle_thread_sem); | |
| 489 idle_thread_stop_time = cyg_current_time(); | |
| 490 test_printf("Stop IDLE thread\n"); | |
| 491 reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time); | |
| 492 reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32); | |
| 493 reply.misc.idle_results.count[1] = htonl((long)idle_thread_count); | |
| 494 break; | |
| 495 #endif | |
| 496 default: | |
| 497 test_printf("Unrecognized request: %d\n", ntohl(req.type)); | |
| 498 reply.response = htonl(NC_REPLY_NAK); | |
| 499 reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST); | |
| 500 break; | |
| 501 } | |
| 502 if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) { | |
| 503 pexit("sendto"); | |
| 504 } | |
| 505 if (reply.response == ntohl(NC_REPLY_NAK)) { | |
| 506 continue; | |
| 507 } | |
| 508 switch (ntohl(req.type)) { | |
| 509 case NC_REQUEST_UDP_SEND: | |
| 510 case NC_REQUEST_UDP_RECV: | |
| 511 case NC_REQUEST_UDP_ECHO: | |
| 512 do_udp_test(s, &req, &master); | |
| 513 break; | |
| 514 case NC_REQUEST_TCP_SEND: | |
| 515 case NC_REQUEST_TCP_RECV: | |
| 516 case NC_REQUEST_TCP_ECHO: | |
| 517 do_tcp_test(s, &req, &master); | |
| 518 break; | |
| 519 case NC_REQUEST_START_IDLE: | |
| 520 case NC_REQUEST_STOP_IDLE: | |
| 521 case NC_REQUEST_SET_LOAD: | |
| 522 default: | |
| 523 break; | |
| 524 } | |
| 525 } | |
| 526 close(s); | |
| 527 } | |
| 528 | |
| 529 void | |
| 530 net_test(test_param_t param) | |
| 531 { | |
| 532 // int i; | |
| 533 if (param == 0) { | |
| 534 test_printf("Start Network Characterization - SLAVE\n"); | |
| 535 #ifdef __ECOS | |
| 536 init_all_network_interfaces(); | |
| 537 calibrate_load(DESIRED_BACKGROUND_LOAD); | |
| 538 #if 0 | |
| 539 // I can see what this is trying to do, but I get "bind: Address already in | |
| 540 // use" errors from the 2nd interface - and the parameter is not used | |
| 541 // anyway, so one thread does quite well enough (but only tests one i/f at | |
| 542 // once). | |
| 543 | |
| 544 // Comment in the 'int i' above too. | |
| 545 for (i = 1; i < CYGHWR_NET_DRIVERS; i++) { | |
| 546 cyg_thread_resume(main_thread_handle[i]); // Start other threads | |
| 547 } | |
| 548 #endif | |
| 549 #endif | |
| 550 } | |
| 551 nc_slave(param); | |
| 552 #ifdef CYGDBG_NET_TIMING_STATS | |
| 553 show_net_times(); | |
| 554 #endif | |
| 555 cyg_test_exit(); | |
| 556 } | |
| 557 | |
| 558 #ifdef __ECOS | |
| 559 | |
| 560 // | |
| 561 // This function is called to calibrate the "background load" which can be | |
| 562 // applied during testing. It will be called before any commands from the | |
| 563 // host are managed. | |
| 564 // | |
| 565 static void | |
| 566 calibrate_load(int desired_load) | |
| 567 { | |
| 568 long long no_load_idle, load_idle; | |
| 569 int percent_load; | |
| 570 int high, low; | |
| 571 | |
| 572 // Set limits | |
| 573 high = MAX_LOAD_THREAD_LEVEL; | |
| 574 low = MIN_LOAD_THREAD_LEVEL; | |
| 575 | |
| 576 // Compute the "no load" idle value | |
| 577 idle_thread_count = 0; | |
| 578 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 579 cyg_thread_delay(1*100); // Pause for one second | |
| 580 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 581 no_load_idle = idle_thread_count; | |
| 582 diag_printf("No load = %d\n", (int)idle_thread_count); | |
| 583 | |
| 584 // First ensure that the HIGH level is indeed higher | |
| 585 while (true) { | |
| 586 load_thread_level = high; | |
| 587 start_load(desired_load); // Start up a given load | |
| 588 idle_thread_count = 0; | |
| 589 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 590 cyg_thread_delay(1*100); // Pause for one second | |
| 591 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 592 load_idle = idle_thread_count; | |
| 593 start_load(0); // Shut down background load | |
| 594 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 595 diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level, | |
| 596 (int)idle_thread_count, percent_load); | |
| 597 if ( percent_load > desired_load ) | |
| 598 break; // HIGH level is indeed higher | |
| 599 low = load_thread_level; // known to be lower | |
| 600 high *= 2; // else double it and try again | |
| 601 } | |
| 602 | |
| 603 // Now chop down to the level required | |
| 604 while (true) { | |
| 605 load_thread_level = (high + low) / 2; | |
| 606 start_load(desired_load); // Start up a given load | |
| 607 idle_thread_count = 0; | |
| 608 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 609 cyg_thread_delay(1*100); // Pause for one second | |
| 610 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 611 load_idle = idle_thread_count; | |
| 612 start_load(0); // Shut down background load | |
| 613 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 614 diag_printf("Load[%d] = %d => %d%%\n", load_thread_level, | |
| 615 (int)idle_thread_count, percent_load); | |
| 616 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break; | |
| 617 if (percent_load < desired_load) { | |
| 618 low = load_thread_level; | |
| 619 } else { | |
| 620 high = load_thread_level; | |
| 621 } | |
| 622 } | |
| 623 | |
| 624 // Now we are within a few percent of the target; scale the load | |
| 625 // factor to get a better fit, and test it, print the answer. | |
| 626 load_thread_level *= desired_load; | |
| 627 load_thread_level /= percent_load; | |
| 628 start_load(desired_load); // Start up a given load | |
| 629 idle_thread_count = 0; | |
| 630 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 631 cyg_thread_delay(1*100); // Pause for one second | |
| 632 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 633 load_idle = idle_thread_count; | |
| 634 start_load(0); // Shut down background load | |
| 635 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 636 diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, | |
| 637 (int)idle_thread_count, percent_load); | |
| 638 // no_load_idle_count_1_second = no_load_idle; | |
| 639 } | |
| 640 | |
| 641 // | |
| 642 // This function is called to set up a load level of 'load' percent (given | |
| 643 // as a whole number, e.g. start_load(20) would mean initiate a background | |
| 644 // load of 20%, leaving the cpu 80% idle). | |
| 645 // | |
| 646 static void | |
| 647 start_load(int load) | |
| 648 { | |
| 649 static int prev_load = 0; | |
| 650 int i; | |
| 651 test_printf("Set background load = %d%%\n", load); | |
| 652 if (load == 0) { | |
| 653 if (prev_load == 0) return; // Nothing out there to stop | |
| 654 for (i = 0; i < prev_load/10; i++) { | |
| 655 cyg_semaphore_wait(&load_thread_sem[i]); | |
| 656 } | |
| 657 prev_load = 0; | |
| 658 } else { | |
| 659 for (i = 0; i < load/10; i++) { | |
| 660 cyg_semaphore_post(&load_thread_sem[i]); | |
| 661 } | |
| 662 prev_load = load; | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 // | |
| 667 // These thread(s) do some amount of "background" computing. This is used | |
| 668 // to simulate a given load level. They need to be run at a higher priority | |
| 669 // than the network code itself. | |
| 670 // | |
| 671 // Like the "idle" thread, they run as long as their "switch" (aka semaphore) | |
| 672 // is enabled. | |
| 673 // | |
| 674 void | |
| 675 net_load(cyg_addrword_t who) | |
| 676 { | |
| 677 int i; | |
| 678 while (true) { | |
| 679 cyg_semaphore_wait(&load_thread_sem[who]); | |
| 680 for (i = 0; i < load_thread_level; i++) { | |
| 681 do_some_random_computation(i); | |
| 682 } | |
| 683 cyg_thread_delay(1); // Wait until the next 'tick' | |
| 684 cyg_semaphore_post(&load_thread_sem[who]); | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 // | |
| 689 // Some arbitrary computation, designed to use up the CPU and cause associated | |
| 690 // cache "thrash" behaviour - part of background load modelling. | |
| 691 // | |
| 692 static void | |
| 693 do_some_random_computation(int p) | |
| 694 { | |
| 695 // Just something that might be "hard" | |
| 696 volatile double x; | |
| 697 x = ((p * 10) * 3.14159) / 180.0; // radians | |
| 698 } | |
| 699 | |
| 700 // | |
| 701 // This thread does nothing but count. It will be allowed to count | |
| 702 // as long as the semaphore is "free". | |
| 703 // | |
| 704 void | |
| 705 net_idle(cyg_addrword_t param) | |
| 706 { | |
| 707 while (true) { | |
| 708 cyg_semaphore_wait(&idle_thread_sem); | |
| 709 idle_thread_count++; | |
| 710 cyg_semaphore_post(&idle_thread_sem); | |
| 711 } | |
| 712 } | |
| 713 | |
| 714 void | |
| 715 cyg_start(void) | |
| 716 { | |
| 717 int i; | |
| 718 // Create processing threads | |
| 719 for (i = 0; i < CYGHWR_NET_DRIVERS; i++) { | |
| 720 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority | |
| 721 net_test, // entry | |
| 722 i, // entry parameter | |
| 723 "Network test", // Name | |
| 724 &main_thread_stack[i][0], // Stack | |
| 725 STACK_SIZE, // Size | |
| 726 &main_thread_handle[i], // Handle | |
| 727 &main_thread_data[i] // Thread data structure | |
| 728 ); | |
| 729 } | |
| 730 cyg_thread_resume(main_thread_handle[0]); // Start first one | |
| 731 // Create the idle thread environment | |
| 732 cyg_semaphore_init(&idle_thread_sem, 0); | |
| 733 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority | |
| 734 net_idle, // entry | |
| 735 0, // entry parameter | |
| 736 "Network idle", // Name | |
| 737 &idle_thread_stack[0], // Stack | |
| 738 STACK_SIZE, // Size | |
| 739 &idle_thread_handle, // Handle | |
| 740 &idle_thread_data // Thread data structure | |
| 741 ); | |
| 742 cyg_thread_resume(idle_thread_handle); // Start it | |
| 743 // Create the load threads and their environment(s) | |
| 744 for (i = 0; i < NUM_LOAD_THREADS; i++) { | |
| 745 cyg_semaphore_init(&load_thread_sem[i], 0); | |
| 746 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority | |
| 747 net_load, // entry | |
| 748 i, // entry parameter | |
| 749 "Background load", // Name | |
| 750 &load_thread_stack[i][0], // Stack | |
| 751 STACK_SIZE, // Size | |
| 752 &load_thread_handle[i], // Handle | |
| 753 &load_thread_data[i] // Thread data structure | |
| 754 ); | |
| 755 cyg_thread_resume(load_thread_handle[i]); // Start it | |
| 756 } | |
| 757 cyg_scheduler_start(); | |
| 758 } | |
| 759 | |
| 760 #else | |
| 761 | |
| 762 int | |
| 763 main(int argc, char *argv[]) | |
| 764 { | |
| 765 net_test(0); | |
| 766 } | |
| 767 #endif |
