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, %lld", 418 procflow->pf_name, avd_get_int(procflow->pf_instances)); 419 420 #ifdef USE_PROCESS_MODEL 421 if ((ret = pthread_cond_init(&procflow_procs_created, NULL)) != 0) 422 return (ret); 423 424 if ((pthread_create(&tid, NULL, procflow_createnwait, NULL)) != 0) 425 return (ret); 426 427 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 428 429 if ((ret = pthread_cond_wait(&procflow_procs_created, 430 &filebench_shm->procflow_lock)) != 0) 431 return (ret); 432 433 if (cnw_wait == CNW_ERROR) 434 ret = -1; 435 436 #else /* USE_PROCESS_MODEL */ 437 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 438 439 ret = procflow_create_all_procs(); 440 #endif /* USE_PROCESS_MODEL */ 441 442 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 443 444 return (ret); 445 } 446 447 #ifdef USE_PROCESS_MODEL 448 /* 449 * Waits for child processes to finish and returns their exit 450 * status. Used by procflow_delete() when the process model is 451 * enabled to wait for a deleted process to exit. 452 */ 453 static void 454 procflow_wait(pid_t pid) 455 { 456 pid_t wpid; 457 int stat; 458 459 (void) waitpid(pid, &stat, 0); 460 while ((wpid = waitpid(getpid() * -1, &stat, WNOHANG)) > 0) 461 filebench_log(LOG_DEBUG_IMPL, "Waited for pid %lld", wpid); 462 } 463 #endif 464 465 /* 466 * Deletes the designated procflow and all its threadflows except 467 * for FLOW_MASTER ones. Waits 10 seconds if the procflow is still 468 * running, then kills the associated process. Finally it frees the 469 * procflow entity. filebench_shm->procflow_lock must be held on entry. 470 * 471 * If the designated procflow is not found on the list it returns -1 and 472 * the procflow is not deleted. Otherwise it returns 0. 473 */ 474 static int 475 procflow_delete(procflow_t *procflow, int wait_cnt) 476 { 477 procflow_t *entry; 478 479 threadflow_delete_all(&procflow->pf_threads, wait_cnt); 480 481 filebench_log(LOG_DEBUG_SCRIPT, 482 "Deleted proc: (%s-%d) pid %d", 483 procflow->pf_name, 484 procflow->pf_instance, 485 procflow->pf_pid); 486 487 while (procflow->pf_running == 1) { 488 filebench_log(LOG_DEBUG_SCRIPT, 489 "Waiting for process %s-%d %d", 490 procflow->pf_name, 491 procflow->pf_instance, 492 procflow->pf_pid); 493 494 if (wait_cnt) { 495 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 496 (void) sleep(1); 497 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 498 wait_cnt--; 499 continue; 500 } 501 #ifdef USE_PROCESS_MODEL 502 (void) kill(procflow->pf_pid, SIGKILL); 503 filebench_log(LOG_DEBUG_SCRIPT, 504 "Had to kill process %s-%d %d!", 505 procflow->pf_name, 506 procflow->pf_instance, 507 procflow->pf_pid); 508 procflow->pf_running = 0; 509 #endif 510 } 511 512 #ifdef USE_PROCESS_MODEL 513 procflow_wait(procflow->pf_pid); 514 #endif 515 /* remove entry from proclist */ 516 entry = filebench_shm->proclist; 517 518 /* unlink procflow entity from proclist */ 519 if (entry == procflow) { 520 /* at head of list */ 521 filebench_shm->proclist = procflow->pf_next; 522 } else { 523 /* search list for procflow */ 524 while (entry && entry->pf_next != procflow) 525 entry = entry->pf_next; 526 527 /* if entity found, unlink it */ 528 if (entry == NULL) 529 return (-1); 530 else 531 entry->pf_next = procflow->pf_next; 532 } 533 534 /* free up the procflow entity */ 535 ipc_free(FILEBENCH_PROCFLOW, (char *)procflow); 536 return (0); 537 } 538 539 540 /* 541 * Waits till all threadflows are started, or a timeout occurs. 542 * Checks through the list of procflows, waiting up to 30 543 * seconds for each one to set its pf_running flag to 1. If not 544 * set after 30 seconds, continues on to the next procflow 545 * anyway after logging the fact. Once pf_running is set 546 * to 1 for a given procflow or the timeout is reached, 547 * threadflow_allstarted() is called to start the threads. 548 * Returns 0 (OK), unless filebench_shm->f_abort is signaled, 549 * in which case it returns -1. 550 */ 551 int 552 procflow_allstarted() 553 { 554 procflow_t *procflow = filebench_shm->proclist; 555 int running_procs = 0; 556 int ret = 0; 557 558 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 559 560 (void) sleep(1); 561 562 while (procflow) { 563 int waits; 564 565 if (procflow->pf_instance && 566 (procflow->pf_instance == FLOW_MASTER)) { 567 procflow = procflow->pf_next; 568 continue; 569 } 570 571 waits = 10; 572 while (waits && procflow->pf_running == 0) { 573 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 574 if (filebench_shm->f_abort == 1) 575 return (-1); 576 577 if (waits < 3) 578 filebench_log(LOG_INFO, 579 "Waiting for process %s-%d %d", 580 procflow->pf_name, 581 procflow->pf_instance, 582 procflow->pf_pid); 583 584 (void) sleep(3); 585 waits--; 586 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 587 } 588 589 if (waits == 0) 590 filebench_log(LOG_INFO, 591 "Failed to start process %s-%d", 592 procflow->pf_name, 593 procflow->pf_instance); 594 595 running_procs++; 596 threadflow_allstarted(procflow->pf_pid, procflow->pf_threads); 597 598 procflow = procflow->pf_next; 599 } 600 filebench_shm->shm_running = running_procs; 601 602 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 603 604 605 return (ret); 606 } 607 608 609 /* 610 * Sets the f_abort flag and clears the running count to stop 611 * all the flowop execution threads from running. Iterates 612 * through the procflow list and deletes all procflows except 613 * for the FLOW_MASTER procflow. Resets the f_abort flag when 614 * finished. 615 */ 616 void 617 procflow_shutdown(void) 618 { 619 procflow_t *procflow = filebench_shm->proclist; 620 int wait_cnt; 621 622 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 623 filebench_shm->shm_running = 0; 624 filebench_shm->f_abort = 1; 625 wait_cnt = SHUTDOWN_WAIT_SECONDS; 626 627 while (procflow) { 628 if (procflow->pf_instance && 629 (procflow->pf_instance == FLOW_MASTER)) { 630 procflow = procflow->pf_next; 631 continue; 632 } 633 filebench_log(LOG_DEBUG_IMPL, "Deleting process %s-%d %d", 634 procflow->pf_name, 635 procflow->pf_instance, 636 procflow->pf_pid); 637 (void) procflow_delete(procflow, wait_cnt); 638 procflow = procflow->pf_next; 639 /* grow more impatient */ 640 if (wait_cnt) 641 wait_cnt--; 642 } 643 644 filebench_shm->f_abort = 0; 645 646 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 647 } 648 649 650 /* 651 * Create an in-memory process object. Allocates a procflow 652 * entity, initialized from the "inherit" procflow if supplied. 653 * The name and instance number are set from the supplied name 654 * and instance number and the procflow is added to the head of 655 * the master procflow list. Returns pointer to the allocated 656 * procflow, or NULL if a name isn't supplied or the procflow 657 * entity cannot be allocated. 658 * 659 * The calling routine must hold the filebench_shm->procflow_lock. 660 */ 661 static procflow_t * 662 procflow_define_common(procflow_t **list, char *name, 663 procflow_t *inherit, int instance) 664 { 665 procflow_t *procflow; 666 667 if (name == NULL) 668 return (NULL); 669 670 procflow = (procflow_t *)ipc_malloc(FILEBENCH_PROCFLOW); 671 672 if (procflow == NULL) 673 return (NULL); 674 675 if (inherit) 676 (void) memcpy(procflow, inherit, sizeof (procflow_t)); 677 else 678 (void) memset(procflow, 0, sizeof (procflow_t)); 679 680 procflow->pf_instance = instance; 681 (void) strcpy(procflow->pf_name, name); 682 683 filebench_log(LOG_DEBUG_IMPL, "defining process %s-%d", name, instance); 684 685 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 686 name, instance, filebench_shm->proclist); 687 /* Add procflow to list, lock is being held already */ 688 if (*list == NULL) { 689 *list = procflow; 690 procflow->pf_next = NULL; 691 } else { 692 procflow->pf_next = *list; 693 *list = procflow; 694 } 695 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 696 name, instance, filebench_shm->proclist); 697 698 return (procflow); 699 } 700 701 /* 702 * Create an in-memory process object as described by the syntax. 703 * Acquires the filebench_shm->procflow_lock and calls 704 * procflow_define_common() to create and initialize a 705 * FLOW_MASTER procflow entity from the optional "inherit" 706 * procflow with the given name and configured for "instances" 707 * number of worker procflows. Currently only called from 708 * parser_proc_define(). 709 */ 710 procflow_t * 711 procflow_define(char *name, procflow_t *inherit, avd_t instances) 712 { 713 procflow_t *procflow; 714 715 (void) ipc_mutex_lock(&filebench_shm->procflow_lock); 716 717 procflow = procflow_define_common(&filebench_shm->proclist, 718 name, inherit, FLOW_MASTER); 719 procflow->pf_instances = instances; 720 721 (void) ipc_mutex_unlock(&filebench_shm->procflow_lock); 722 723 return (procflow); 724 } 725