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, instances; 237 238 instances = (int)avd_get_int(procflow->pf_instances); 239 filebench_log(LOG_INFO, "Starting %d %s instances", 240 instances, procflow->pf_name); 241 242 /* Create instances of procflow */ 243 for (i = 0; (i < instances) && (ret == 0); i++) { 244 procflow_t *newproc; 245 246 /* Create processes */ 247 newproc = 248 procflow_define_common(&filebench_shm->proclist, 249 procflow->pf_name, procflow, i + 1); 250 if (newproc == NULL) 251 ret = -1; 252 else 253 ret = procflow_createproc(newproc); 254 } 255 256 if (ret != 0) 257 break; 258 259 procflow = procflow->pf_next; 260 } 261 262 return (ret); 263 } 264 265 #ifdef USE_PROCESS_MODEL 266 /* 267 * Used to start up threads on a child process, when filebench is 268 * compiled to support multiple processes. Uses the name string 269 * and instance number passed to the child to find the previously 270 * created procflow entity. Then uses nice() to reduce the 271 * process' priority by at least 10. A call is then made to 272 * threadflow_init() which creates and runs the process' threads 273 * and flowops to completion. When threadflow_init() returns, 274 * a call to exit() terminates the child process. 275 */ 276 int 277 procflow_exec(char *name, int instance) 278 { 279 procflow_t *procflow; 280 int proc_nice; 281 #ifdef HAVE_SETRLIMIT 282 struct rlimit rlp; 283 #endif 284 int ret; 285 286 filebench_log(LOG_DEBUG_IMPL, 287 "procflow_execproc %s-%d", 288 name, instance); 289 290 if ((procflow = procflow_find(name, instance)) == NULL) { 291 filebench_log(LOG_ERROR, 292 "procflow_exec could not find %s-%d", 293 name, instance); 294 return (-1); 295 } 296 297 /* set the slave process' procflow pointer */ 298 my_procflow = procflow; 299 300 /* set its pid from value stored by main() */ 301 procflow->pf_pid = my_pid; 302 303 filebench_log(LOG_DEBUG_IMPL, 304 "Started up %s pid %d", procflow->pf_name, my_pid); 305 306 filebench_log(LOG_DEBUG_IMPL, 307 "nice = %llx", procflow->pf_nice); 308 309 proc_nice = avd_get_int(procflow->pf_nice); 310 filebench_log(LOG_DEBUG_IMPL, "Setting pri of %s-%d to %d", 311 name, instance, nice(proc_nice + 10)); 312 313 procflow->pf_running = 1; 314 315 #ifdef HAVE_SETRLIMIT 316 /* Get resource limits */ 317 (void) getrlimit(RLIMIT_NOFILE, &rlp); 318 filebench_log(LOG_DEBUG_SCRIPT, "%d file descriptors", rlp.rlim_cur); 319 #endif 320 321 if ((ret = threadflow_init(procflow)) != FILEBENCH_OK) { 322 if (ret < 0) { 323 filebench_log(LOG_ERROR, 324 "Failed to start threads for %s pid %d", 325 procflow->pf_name, my_pid); 326 } 327 } else { 328 filebench_log(LOG_DEBUG_IMPL, 329 "procflow_createproc exiting..."); 330 } 331 332 procflow->pf_running = 0; 333 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 334 filebench_shm->shm_running --; 335 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 336 337 return (ret); 338 } 339 340 341 /* 342 * A special thread from which worker (child) processes are created, and 343 * which then waits for worker processes to die. If they die unexpectedly, 344 * that is not a simple exit(0), then report an error and terminate the 345 * run. 346 */ 347 /* ARGSUSED */ 348 static void * 349 procflow_createnwait(void *nothing) 350 { 351 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 352 353 if (procflow_create_all_procs() == 0) 354 cnw_wait = CNW_DONE; 355 else 356 cnw_wait = CNW_ERROR; 357 358 if (pthread_cond_signal(&procflow_procs_created) != 0) 359 exit(1); 360 361 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 362 363 /* CONSTCOND */ 364 while (1) { 365 siginfo_t status; 366 367 /* wait for any child process to exit */ 368 if (waitid(P_ALL, 0, &status, WEXITED) != 0) 369 pthread_exit(0); 370 371 /* if normal shutdown in progress, just quit */ 372 if (filebench_shm->f_abort) 373 pthread_exit(0); 374 375 if (status.si_code == CLD_EXITED) { 376 /* A process called exit(); check returned status */ 377 if (status.si_status != 0) { 378 filebench_log(LOG_ERROR, 379 "Unexpected Process termination; exiting", 380 status.si_status); 381 filebench_shutdown(1); 382 } 383 } else { 384 /* A process quit because of some fatal error */ 385 filebench_log(LOG_ERROR, 386 "Unexpected Process termination Code %d, Errno %d", 387 status.si_code, status.si_errno); 388 filebench_shutdown(1); 389 } 390 391 /* nothing running, exit */ 392 if (filebench_shm->shm_running == 0) { 393 filebench_shm->f_abort = FILEBENCH_ABORT_RSRC; 394 pthread_exit(0); 395 } 396 } 397 /* NOTREACHED */ 398 return (NULL); 399 } 400 #endif /* USE_PROCESS_MODEL */ 401 402 /* 403 * Iterates through proclist, the master list of procflows, 404 * creating the number of instances of each procflow specified 405 * by its pf_instance attribute. Returns 0 on success, or -1 406 * times the number of procflow instances that were not 407 * successfully created. 408 */ 409 int 410 procflow_init(void) 411 { 412 procflow_t *procflow = filebench_shm->proclist; 413 pthread_t tid; 414 int ret = 0; 415 416 filebench_log(LOG_DEBUG_IMPL, 417 "procflow_init %s, %llu", 418 procflow->pf_name, 419 (u_longlong_t)avd_get_int(procflow->pf_instances)); 420 421 #ifdef USE_PROCESS_MODEL 422 if ((ret = pthread_cond_init(&procflow_procs_created, NULL)) != 0) 423 return (ret); 424 425 if ((pthread_create(&tid, NULL, procflow_createnwait, NULL)) != 0) 426 return (ret); 427 428 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 429 430 if ((ret = pthread_cond_wait(&procflow_procs_created, 431 &filebench_shm->procflow_lock)) != 0) 432 return (ret); 433 434 if (cnw_wait == CNW_ERROR) 435 ret = -1; 436 437 #else /* USE_PROCESS_MODEL */ 438 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 439 440 ret = procflow_create_all_procs(); 441 #endif /* USE_PROCESS_MODEL */ 442 443 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 444 445 return (ret); 446 } 447 448 #ifdef USE_PROCESS_MODEL 449 /* 450 * Waits for child processes to finish and returns their exit 451 * status. Used by procflow_delete() when the process model is 452 * enabled to wait for a deleted process to exit. 453 */ 454 static void 455 procflow_wait(pid_t pid) 456 { 457 pid_t wpid; 458 int stat; 459 460 (void) waitpid(pid, &stat, 0); 461 while ((wpid = waitpid(getpid() * -1, &stat, WNOHANG)) > 0) 462 filebench_log(LOG_DEBUG_IMPL, "Waited for pid %d", (int)wpid); 463 } 464 #endif 465 466 /* 467 * Deletes the designated procflow and all its threadflows except 468 * for FLOW_MASTER ones. Waits 10 seconds if the procflow is still 469 * running, then kills the associated process. Finally it frees the 470 * procflow entity. filebench_shm->procflow_lock must be held on entry. 471 * 472 * If the designated procflow is not found on the list it returns -1 and 473 * the procflow is not deleted. Otherwise it returns 0. 474 */ 475 static int 476 procflow_delete(procflow_t *procflow, int wait_cnt) 477 { 478 procflow_t *entry; 479 480 threadflow_delete_all(&procflow->pf_threads, wait_cnt); 481 482 filebench_log(LOG_DEBUG_SCRIPT, 483 "Deleted proc: (%s-%d) pid %d", 484 procflow->pf_name, 485 procflow->pf_instance, 486 procflow->pf_pid); 487 488 while (procflow->pf_running == 1) { 489 filebench_log(LOG_DEBUG_SCRIPT, 490 "Waiting for process %s-%d %d", 491 procflow->pf_name, 492 procflow->pf_instance, 493 procflow->pf_pid); 494 495 if (wait_cnt) { 496 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 497 (void) sleep(1); 498 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 499 wait_cnt--; 500 continue; 501 } 502 #ifdef USE_PROCESS_MODEL 503 (void) kill(procflow->pf_pid, SIGKILL); 504 filebench_log(LOG_DEBUG_SCRIPT, 505 "Had to kill process %s-%d %d!", 506 procflow->pf_name, 507 procflow->pf_instance, 508 procflow->pf_pid); 509 procflow->pf_running = 0; 510 #endif 511 } 512 513 #ifdef USE_PROCESS_MODEL 514 procflow_wait(procflow->pf_pid); 515 #endif 516 /* remove entry from proclist */ 517 entry = filebench_shm->proclist; 518 519 /* unlink procflow entity from proclist */ 520 if (entry == procflow) { 521 /* at head of list */ 522 filebench_shm->proclist = procflow->pf_next; 523 } else { 524 /* search list for procflow */ 525 while (entry && entry->pf_next != procflow) 526 entry = entry->pf_next; 527 528 /* if entity found, unlink it */ 529 if (entry == NULL) 530 return (-1); 531 else 532 entry->pf_next = procflow->pf_next; 533 } 534 535 /* free up the procflow entity */ 536 ipc_free(FILEBENCH_PROCFLOW, (char *)procflow); 537 return (0); 538 } 539 540 541 /* 542 * Waits till all threadflows are started, or a timeout occurs. 543 * Checks through the list of procflows, waiting up to 30 544 * seconds for each one to set its pf_running flag to 1. If not 545 * set after 30 seconds, continues on to the next procflow 546 * anyway after logging the fact. Once pf_running is set 547 * to 1 for a given procflow or the timeout is reached, 548 * threadflow_allstarted() is called to start the threads. 549 * Returns 0 (OK), unless filebench_shm->f_abort is signaled, 550 * in which case it returns -1. 551 */ 552 int 553 procflow_allstarted() 554 { 555 procflow_t *procflow = filebench_shm->proclist; 556 int running_procs = 0; 557 int ret = 0; 558 559 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 560 561 (void) sleep(1); 562 563 while (procflow) { 564 int waits; 565 566 if (procflow->pf_instance && 567 (procflow->pf_instance == FLOW_MASTER)) { 568 procflow = procflow->pf_next; 569 continue; 570 } 571 572 waits = 10; 573 while (waits && procflow->pf_running == 0) { 574 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 575 if (filebench_shm->f_abort == 1) 576 return (-1); 577 578 if (waits < 3) 579 filebench_log(LOG_INFO, 580 "Waiting for process %s-%d %d", 581 procflow->pf_name, 582 procflow->pf_instance, 583 procflow->pf_pid); 584 585 (void) sleep(3); 586 waits--; 587 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 588 } 589 590 if (waits == 0) 591 filebench_log(LOG_INFO, 592 "Failed to start process %s-%d", 593 procflow->pf_name, 594 procflow->pf_instance); 595 596 running_procs++; 597 threadflow_allstarted(procflow->pf_pid, procflow->pf_threads); 598 599 procflow = procflow->pf_next; 600 } 601 filebench_shm->shm_running = running_procs; 602 603 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 604 605 606 return (ret); 607 } 608 609 610 /* 611 * Sets the f_abort flag and clears the running count to stop 612 * all the flowop execution threads from running. Iterates 613 * through the procflow list and deletes all procflows except 614 * for the FLOW_MASTER procflow. Resets the f_abort flag when 615 * finished. 616 */ 617 void 618 procflow_shutdown(void) 619 { 620 procflow_t *procflow = filebench_shm->proclist; 621 int wait_cnt; 622 623 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 624 filebench_shm->shm_running = 0; 625 filebench_shm->f_abort = 1; 626 wait_cnt = SHUTDOWN_WAIT_SECONDS; 627 628 while (procflow) { 629 if (procflow->pf_instance && 630 (procflow->pf_instance == FLOW_MASTER)) { 631 procflow = procflow->pf_next; 632 continue; 633 } 634 filebench_log(LOG_DEBUG_IMPL, "Deleting process %s-%d %d", 635 procflow->pf_name, 636 procflow->pf_instance, 637 procflow->pf_pid); 638 (void) procflow_delete(procflow, wait_cnt); 639 procflow = procflow->pf_next; 640 /* grow more impatient */ 641 if (wait_cnt) 642 wait_cnt--; 643 } 644 645 filebench_shm->f_abort = 0; 646 647 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 648 } 649 650 651 /* 652 * Create an in-memory process object. Allocates a procflow 653 * entity, initialized from the "inherit" procflow if supplied. 654 * The name and instance number are set from the supplied name 655 * and instance number and the procflow is added to the head of 656 * the master procflow list. Returns pointer to the allocated 657 * procflow, or NULL if a name isn't supplied or the procflow 658 * entity cannot be allocated. 659 * 660 * The calling routine must hold the filebench_shm->procflow_lock. 661 */ 662 static procflow_t * 663 procflow_define_common(procflow_t **list, char *name, 664 procflow_t *inherit, int instance) 665 { 666 procflow_t *procflow; 667 668 if (name == NULL) 669 return (NULL); 670 671 procflow = (procflow_t *)ipc_malloc(FILEBENCH_PROCFLOW); 672 673 if (procflow == NULL) 674 return (NULL); 675 676 if (inherit) 677 (void) memcpy(procflow, inherit, sizeof (procflow_t)); 678 else 679 (void) memset(procflow, 0, sizeof (procflow_t)); 680 681 procflow->pf_instance = instance; 682 (void) strcpy(procflow->pf_name, name); 683 684 filebench_log(LOG_DEBUG_IMPL, "defining process %s-%d", name, instance); 685 686 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 687 name, instance, filebench_shm->proclist); 688 /* Add procflow to list, lock is being held already */ 689 if (*list == NULL) { 690 *list = procflow; 691 procflow->pf_next = NULL; 692 } else { 693 procflow->pf_next = *list; 694 *list = procflow; 695 } 696 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 697 name, instance, filebench_shm->proclist); 698 699 return (procflow); 700 } 701 702 /* 703 * Create an in-memory process object as described by the syntax. 704 * Acquires the filebench_shm->procflow_lock and calls 705 * procflow_define_common() to create and initialize a 706 * FLOW_MASTER procflow entity from the optional "inherit" 707 * procflow with the given name and configured for "instances" 708 * number of worker procflows. Currently only called from 709 * parser_proc_define(). 710 */ 711 procflow_t * 712 procflow_define(char *name, procflow_t *inherit, avd_t instances) 713 { 714 procflow_t *procflow; 715 716 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 717 718 procflow = procflow_define_common(&filebench_shm->proclist, 719 name, inherit, FLOW_MASTER); 720 procflow->pf_instances = instances; 721 722 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 723 724 return (procflow); 725 } 726