1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <signal.h> 29 #include <fcntl.h> 30 #include <sys/stat.h> 31 #include <sys/wait.h> 32 33 #include "procflow.h" 34 #include "filebench.h" 35 #include "flowop.h" 36 #include "ipc.h" 37 38 /* pid and procflow pointer for this process */ 39 pid_t my_pid; 40 procflow_t *my_procflow = NULL; 41 42 static procflow_t *procflow_define_common(procflow_t **list, char *name, 43 procflow_t *inherit, int instance); 44 45 #ifdef USE_PROCESS_MODEL 46 47 static enum create_n_wait { 48 CNW_DONE, 49 CNW_ERROR 50 } cnw_wait; 51 52 static pthread_cond_t procflow_procs_created; 53 54 #endif /* USE_PROCESS_MODEL */ 55 56 57 /* 58 * Procflows are filebench entities which manage processes. Each 59 * worker procflow spawns a separate filebench process, with attributes 60 * inherited from a FLOW_MASTER procflow created during f model language 61 * parsing. This section contains routines to define, create, control, 62 * and delete procflows. 63 * 64 * Each process defined in the f model creates a FLOW_MASTER 65 * procflow which encapsulates the defined attributes, and threads of 66 * the f process, including the number of instances to create. At 67 * runtime, a worker procflow instance with an associated filebench 68 * process is created, which runs until told to quite by the original 69 * filebench process or is specifically deleted. 70 */ 71 72 73 /* 74 * Prints a summary of the syntax for setting procflow parameters. 75 */ 76 void 77 procflow_usage(void) 78 { 79 (void) fprintf(stderr, 80 "define process name=<name>[,instances=<count>]\n"); 81 (void) fprintf(stderr, "{\n"); 82 (void) fprintf(stderr, " thread ...\n"); 83 (void) fprintf(stderr, " thread ...\n"); 84 (void) fprintf(stderr, " thread ...\n"); 85 (void) fprintf(stderr, "}\n"); 86 (void) fprintf(stderr, "\n"); 87 (void) fprintf(stderr, "\n"); 88 } 89 90 /* 91 * If filebench has been compiled to support multiple processes 92 * (USE_PROCESS_MODEL defined), this routine forks a child 93 * process and uses either system() or exec() to start up a new 94 * instance of filebench, passing it the procflow name, instance 95 * number and shared memory region address. 96 * If USE_PROCESS_MODEL is NOT defined, then the routine 97 * just creates a child thread which begins executing 98 * threadflow_init() for the specified procflow. 99 */ 100 static int 101 procflow_createproc(procflow_t *procflow) 102 { 103 char instance[128]; 104 char shmaddr[128]; 105 char procname[128]; 106 pid_t pid; 107 108 #ifdef USE_PROCESS_MODEL 109 110 (void) snprintf(instance, sizeof (instance), "%d", 111 procflow->pf_instance); 112 (void) snprintf(procname, sizeof (procname), "%s", procflow->pf_name); 113 #if defined(_LP64) || (__WORDSIZE == 64) 114 (void) snprintf(shmaddr, sizeof (shmaddr), "%llx", filebench_shm); 115 #else 116 (void) snprintf(shmaddr, sizeof (shmaddr), "%x", filebench_shm); 117 #endif 118 filebench_log(LOG_DEBUG_IMPL, "creating process %s", 119 procflow->pf_name); 120 121 procflow->pf_running = 0; 122 123 #ifdef HAVE_FORK1 124 if ((pid = fork1()) < 0) { 125 filebench_log(LOG_ERROR, 126 "procflow_createproc fork failed: %s", 127 strerror(errno)); 128 return (-1); 129 } 130 #else 131 if ((pid = fork()) < 0) { 132 filebench_log(LOG_ERROR, 133 "procflow_createproc fork failed: %s", 134 strerror(errno)); 135 return (-1); 136 } 137 #endif /* HAVE_FORK1 */ 138 139 /* if child, start up new copy of filebench */ 140 if (pid == 0) { 141 #ifdef USE_SYSTEM 142 char syscmd[1024]; 143 #endif 144 145 (void) sigignore(SIGINT); 146 filebench_log(LOG_DEBUG_SCRIPT, 147 "Starting %s-%d", procflow->pf_name, 148 procflow->pf_instance); 149 /* Child */ 150 151 #ifdef USE_SYSTEM 152 (void) snprintf(syscmd, sizeof (syscmd), "%s -a %s -i %s -s %s", 153 execname, 154 procname, 155 instance, 156 shmaddr); 157 if (system(syscmd) < 0) { 158 filebench_log(LOG_ERROR, 159 "procflow exec proc failed: %s", 160 strerror(errno)); 161 filebench_shutdown(1); 162 } 163 164 #else 165 if (execl(execname, procname, "-a", procname, "-i", 166 instance, "-s", shmaddr, "-m", shmpath, NULL) < 0) { 167 filebench_log(LOG_ERROR, 168 "procflow exec proc failed: %s", 169 strerror(errno)); 170 filebench_shutdown(1); 171 } 172 #endif 173 exit(1); 174 } else { 175 /* if parent, save pid and return */ 176 procflow->pf_pid = pid; 177 } 178 #else 179 procflow->pf_running = 1; 180 if (pthread_create(&procflow->pf_tid, NULL, 181 (void *(*)(void*))threadflow_init, procflow) != 0) { 182 filebench_log(LOG_ERROR, "proc-thread create failed"); 183 procflow->pf_running = 0; 184 } 185 #endif 186 filebench_log(LOG_DEBUG_IMPL, "procflow_createproc created pid %d", 187 pid); 188 189 return (0); 190 } 191 192 /* 193 * Find a procflow of name "name" and instance "instance" on the 194 * master procflow list, filebench_shm->proclist. Locks the list 195 * and scans through it searching for a procflow with matching 196 * name and instance number. If found returns a pointer to the 197 * procflow, otherwise returns NULL. 198 */ 199 static procflow_t * 200 procflow_find(char *name, int instance) 201 { 202 procflow_t *procflow = filebench_shm->proclist; 203 204 filebench_log(LOG_DEBUG_IMPL, "Find: (%s-%d) proclist = %zx", 205 name, instance, procflow); 206 207 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 208 209 while (procflow) { 210 filebench_log(LOG_DEBUG_IMPL, "Find: (%s-%d) == (%s-%d)", 211 name, instance, 212 procflow->pf_name, 213 procflow->pf_instance); 214 if ((strcmp(name, procflow->pf_name) == 0) && 215 (instance == procflow->pf_instance)) { 216 217 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 218 219 return (procflow); 220 } 221 procflow = procflow->pf_next; 222 } 223 224 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 225 226 return (NULL); 227 } 228 229 static int 230 procflow_create_all_procs(void) 231 { 232 procflow_t *procflow = filebench_shm->proclist; 233 int ret = 0; 234 235 while (procflow) { 236 int i; 237 238 filebench_log(LOG_INFO, "Starting %lld %s instances", 239 *(procflow->pf_instances), procflow->pf_name); 240 241 /* Create instances of procflow */ 242 for (i = 0; (i < *procflow->pf_instances) && (ret == 0); i++) { 243 procflow_t *newproc; 244 245 /* Create processes */ 246 newproc = 247 procflow_define_common(&filebench_shm->proclist, 248 procflow->pf_name, procflow, i + 1); 249 if (newproc == NULL) 250 ret = -1; 251 else 252 ret = procflow_createproc(newproc); 253 } 254 255 if (ret != 0) 256 break; 257 258 procflow = procflow->pf_next; 259 } 260 261 return (ret); 262 } 263 264 #ifdef USE_PROCESS_MODEL 265 /* 266 * Used to start up threads on a child process, when filebench is 267 * compiled to support multiple processes. Uses the name string 268 * and instance number passed to the child to find the previously 269 * created procflow entity. Then uses nice() to reduce the 270 * process' priority by at least 10. A call is then made to 271 * threadflow_init() which creates and runs the process' threads 272 * and flowops to completion. When threadflow_init() returns, 273 * a call to exit() terminates the child process. 274 */ 275 int 276 procflow_exec(char *name, int instance) 277 { 278 procflow_t *procflow; 279 int proc_nice; 280 #ifdef HAVE_SETRLIMIT 281 struct rlimit rlp; 282 #endif 283 int ret; 284 285 filebench_log(LOG_DEBUG_IMPL, 286 "procflow_execproc %s-%d", 287 name, instance); 288 289 if ((procflow = procflow_find(name, instance)) == NULL) { 290 filebench_log(LOG_ERROR, 291 "procflow_exec could not find %s-%d", 292 name, instance); 293 return (-1); 294 } 295 296 /* set the slave process' procflow pointer */ 297 my_procflow = procflow; 298 299 /* set its pid from value stored by main() */ 300 procflow->pf_pid = my_pid; 301 302 filebench_log(LOG_DEBUG_IMPL, 303 "Started up %s pid %d", procflow->pf_name, my_pid); 304 305 filebench_log(LOG_DEBUG_IMPL, 306 "nice = %llx", procflow->pf_nice); 307 308 proc_nice = *procflow->pf_nice; 309 filebench_log(LOG_DEBUG_IMPL, "Setting pri of %s-%d to %d", 310 name, instance, nice(proc_nice + 10)); 311 312 procflow->pf_running = 1; 313 314 #ifdef HAVE_SETRLIMIT 315 /* Get resource limits */ 316 (void) getrlimit(RLIMIT_NOFILE, &rlp); 317 filebench_log(LOG_DEBUG_SCRIPT, "%d file descriptors", rlp.rlim_cur); 318 #endif 319 320 if ((ret = threadflow_init(procflow)) != FILEBENCH_OK) { 321 if (ret < 0) { 322 filebench_log(LOG_ERROR, 323 "Failed to start threads for %s pid %d", 324 procflow->pf_name, my_pid); 325 } 326 } else { 327 filebench_log(LOG_DEBUG_IMPL, 328 "procflow_createproc exiting..."); 329 } 330 331 procflow->pf_running = 0; 332 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 333 filebench_shm->shm_running --; 334 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 335 336 return (ret); 337 } 338 339 340 /* 341 * A special thread from which worker (child) processes are created, and 342 * which then waits for worker processes to die. If they die unexpectedly, 343 * that is not a simple exit(0), then report an error and terminate the 344 * run. 345 */ 346 /* ARGSUSED */ 347 static void * 348 procflow_createnwait(void *nothing) 349 { 350 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 351 352 if (procflow_create_all_procs() == 0) 353 cnw_wait = CNW_DONE; 354 else 355 cnw_wait = CNW_ERROR; 356 357 if (pthread_cond_signal(&procflow_procs_created) != 0) 358 exit(1); 359 360 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 361 362 /* CONSTCOND */ 363 while (1) { 364 siginfo_t status; 365 366 /* wait for any child process to exit */ 367 if (waitid(P_ALL, 0, &status, WEXITED) != 0) 368 pthread_exit(0); 369 370 /* if normal shutdown in progress, just quit */ 371 if (filebench_shm->f_abort) 372 pthread_exit(0); 373 374 if (status.si_code == CLD_EXITED) { 375 /* A process called exit(); check returned status */ 376 if (status.si_status != 0) { 377 filebench_log(LOG_ERROR, 378 "Unexpected Process termination; exiting", 379 status.si_status); 380 filebench_shutdown(1); 381 } 382 } else { 383 /* A process quit because of some fatal error */ 384 filebench_log(LOG_ERROR, 385 "Unexpected Process termination Code %d, Errno %d", 386 status.si_code, status.si_errno); 387 filebench_shutdown(1); 388 } 389 390 /* nothing running, exit */ 391 if (filebench_shm->shm_running == 0) { 392 filebench_shm->f_abort = FILEBENCH_ABORT_RSRC; 393 pthread_exit(0); 394 } 395 } 396 /* NOTREACHED */ 397 return (NULL); 398 } 399 #endif /* USE_PROCESS_MODEL */ 400 401 /* 402 * Iterates through proclist, the master list of procflows, 403 * creating the number of instances of each procflow specified 404 * by its pf_instance attribute. Returns 0 on success, or -1 405 * times the number of procflow instances that were not 406 * successfully created. 407 */ 408 int 409 procflow_init(void) 410 { 411 procflow_t *procflow = filebench_shm->proclist; 412 pthread_t tid; 413 int ret = 0; 414 415 filebench_log(LOG_DEBUG_IMPL, 416 "procflow_init %s, %lld", 417 procflow->pf_name, *(procflow->pf_instances)); 418 419 #ifdef USE_PROCESS_MODEL 420 if ((ret = pthread_cond_init(&procflow_procs_created, NULL)) != 0) 421 return (ret); 422 423 if ((pthread_create(&tid, NULL, procflow_createnwait, NULL)) != 0) 424 return (ret); 425 426 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 427 428 if ((ret = pthread_cond_wait(&procflow_procs_created, 429 &filebench_shm->procflow_lock)) != 0) 430 return (ret); 431 432 if (cnw_wait == CNW_ERROR) 433 ret = -1; 434 435 #else /* USE_PROCESS_MODEL */ 436 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 437 438 ret = procflow_create_all_procs(); 439 #endif /* USE_PROCESS_MODEL */ 440 441 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 442 443 return (ret); 444 } 445 446 #ifdef USE_PROCESS_MODEL 447 /* 448 * Waits for child processes to finish and returns their exit 449 * status. Used by procflow_delete() when the process model is 450 * enabled to wait for a deleted process to exit. 451 */ 452 static void 453 procflow_wait(pid_t pid) 454 { 455 pid_t wpid; 456 int stat; 457 458 (void) waitpid(pid, &stat, 0); 459 while ((wpid = waitpid(getpid() * -1, &stat, WNOHANG)) > 0) 460 filebench_log(LOG_DEBUG_IMPL, "Waited for pid %lld", wpid); 461 } 462 #endif 463 464 /* 465 * Deletes the designated procflow and all its threadflows except 466 * for FLOW_MASTER ones. Waits 10 seconds if the procflow is still 467 * running, then kills the associated process. Finally it frees the 468 * procflow entity. filebench_shm->procflow_lock must be held on entry. 469 * 470 * If the designated procflow is not found on the list it returns -1 and 471 * the procflow is not deleted. Otherwise it returns 0. 472 */ 473 static int 474 procflow_delete(procflow_t *procflow, int wait_cnt) 475 { 476 procflow_t *entry; 477 478 threadflow_delete_all(&procflow->pf_threads, wait_cnt); 479 480 filebench_log(LOG_DEBUG_SCRIPT, 481 "Deleted proc: (%s-%d) pid %d", 482 procflow->pf_name, 483 procflow->pf_instance, 484 procflow->pf_pid); 485 486 while (procflow->pf_running == 1) { 487 filebench_log(LOG_DEBUG_SCRIPT, 488 "Waiting for process %s-%d %d", 489 procflow->pf_name, 490 procflow->pf_instance, 491 procflow->pf_pid); 492 493 if (wait_cnt) { 494 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 495 (void) sleep(1); 496 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 497 wait_cnt--; 498 continue; 499 } 500 #ifdef USE_PROCESS_MODEL 501 (void) kill(procflow->pf_pid, SIGKILL); 502 filebench_log(LOG_DEBUG_SCRIPT, 503 "Had to kill process %s-%d %d!", 504 procflow->pf_name, 505 procflow->pf_instance, 506 procflow->pf_pid); 507 procflow->pf_running = 0; 508 #endif 509 } 510 511 #ifdef USE_PROCESS_MODEL 512 procflow_wait(procflow->pf_pid); 513 #endif 514 /* remove entry from proclist */ 515 entry = filebench_shm->proclist; 516 517 /* unlink procflow entity from proclist */ 518 if (entry == procflow) { 519 /* at head of list */ 520 filebench_shm->proclist = procflow->pf_next; 521 } else { 522 /* search list for procflow */ 523 while (entry && entry->pf_next != procflow) 524 entry = entry->pf_next; 525 526 /* if entity found, unlink it */ 527 if (entry == NULL) 528 return (-1); 529 else 530 entry->pf_next = procflow->pf_next; 531 } 532 533 /* free up the procflow entity */ 534 ipc_free(FILEBENCH_PROCFLOW, (char *)procflow); 535 return (0); 536 } 537 538 539 /* 540 * Waits till all threadflows are started, or a timeout occurs. 541 * Checks through the list of procflows, waiting up to 30 542 * seconds for each one to set its pf_running flag to 1. If not 543 * set after 30 seconds, continues on to the next procflow 544 * anyway after logging the fact. Once pf_running is set 545 * to 1 for a given procflow or the timeout is reached, 546 * threadflow_allstarted() is called to start the threads. 547 * Returns 0 (OK), unless filebench_shm->f_abort is signaled, 548 * in which case it returns -1. 549 */ 550 int 551 procflow_allstarted() 552 { 553 procflow_t *procflow = filebench_shm->proclist; 554 int running_procs = 0; 555 int ret = 0; 556 557 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 558 559 (void) sleep(1); 560 561 while (procflow) { 562 int waits; 563 564 if (procflow->pf_instance && 565 (procflow->pf_instance == FLOW_MASTER)) { 566 procflow = procflow->pf_next; 567 continue; 568 } 569 570 waits = 10; 571 while (waits && procflow->pf_running == 0) { 572 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 573 if (filebench_shm->f_abort == 1) 574 return (-1); 575 576 if (waits < 3) 577 filebench_log(LOG_INFO, 578 "Waiting for process %s-%d %d", 579 procflow->pf_name, 580 procflow->pf_instance, 581 procflow->pf_pid); 582 583 (void) sleep(3); 584 waits--; 585 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 586 } 587 588 if (waits == 0) 589 filebench_log(LOG_INFO, 590 "Failed to start process %s-%d", 591 procflow->pf_name, 592 procflow->pf_instance); 593 594 running_procs++; 595 threadflow_allstarted(procflow->pf_pid, procflow->pf_threads); 596 597 procflow = procflow->pf_next; 598 } 599 filebench_shm->shm_running = running_procs; 600 601 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 602 603 604 return (ret); 605 } 606 607 608 /* 609 * Sets the f_abort flag and clears the running count to stop 610 * all the flowop execution threads from running. Iterates 611 * through the procflow list and deletes all procflows except 612 * for the FLOW_MASTER procflow. Resets the f_abort flag when 613 * finished. 614 */ 615 void 616 procflow_shutdown(void) 617 { 618 procflow_t *procflow = filebench_shm->proclist; 619 int wait_cnt; 620 621 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 622 filebench_shm->shm_running = 0; 623 filebench_shm->f_abort = 1; 624 wait_cnt = SHUTDOWN_WAIT_SECONDS; 625 626 while (procflow) { 627 if (procflow->pf_instance && 628 (procflow->pf_instance == FLOW_MASTER)) { 629 procflow = procflow->pf_next; 630 continue; 631 } 632 filebench_log(LOG_DEBUG_IMPL, "Deleting process %s-%d %d", 633 procflow->pf_name, 634 procflow->pf_instance, 635 procflow->pf_pid); 636 (void) procflow_delete(procflow, wait_cnt); 637 procflow = procflow->pf_next; 638 /* grow more impatient */ 639 if (wait_cnt) 640 wait_cnt--; 641 } 642 643 filebench_shm->f_abort = 0; 644 645 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 646 } 647 648 649 /* 650 * Create an in-memory process object. Allocates a procflow 651 * entity, initialized from the "inherit" procflow if supplied. 652 * The name and instance number are set from the supplied name 653 * and instance number and the procflow is added to the head of 654 * the master procflow list. Returns pointer to the allocated 655 * procflow, or NULL if a name isn't supplied or the procflow 656 * entity cannot be allocated. 657 * 658 * The calling routine must hold the filebench_shm->procflow_lock. 659 */ 660 static procflow_t * 661 procflow_define_common(procflow_t **list, char *name, 662 procflow_t *inherit, int instance) 663 { 664 procflow_t *procflow; 665 666 if (name == NULL) 667 return (NULL); 668 669 procflow = (procflow_t *)ipc_malloc(FILEBENCH_PROCFLOW); 670 671 if (procflow == NULL) 672 return (NULL); 673 674 if (inherit) 675 (void) memcpy(procflow, inherit, sizeof (procflow_t)); 676 else 677 (void) memset(procflow, 0, sizeof (procflow_t)); 678 679 procflow->pf_instance = instance; 680 (void) strcpy(procflow->pf_name, name); 681 682 filebench_log(LOG_DEBUG_IMPL, "defining process %s-%d", name, instance); 683 684 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 685 name, instance, filebench_shm->proclist); 686 /* Add procflow to list, lock is being held already */ 687 if (*list == NULL) { 688 *list = procflow; 689 procflow->pf_next = NULL; 690 } else { 691 procflow->pf_next = *list; 692 *list = procflow; 693 } 694 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 695 name, instance, filebench_shm->proclist); 696 697 return (procflow); 698 } 699 700 /* 701 * Create an in-memory process object as described by the syntax. 702 * Acquires the filebench_shm->procflow_lock and calls 703 * procflow_define_common() to create and initialize a 704 * FLOW_MASTER procflow entity from the optional "inherit" 705 * procflow with the given name and configured for "instances" 706 * number of worker procflows. Currently only called from 707 * parser_proc_define(). 708 */ 709 procflow_t * 710 procflow_define(char *name, procflow_t *inherit, var_integer_t instances) 711 { 712 procflow_t *procflow; 713 714 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 715 716 procflow = procflow_define_common(&filebench_shm->proclist, 717 name, inherit, FLOW_MASTER); 718 procflow->pf_instances = instances; 719 720 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 721 722 return (procflow); 723 } 724