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