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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Portions Copyright 2008 Denis Cheng 26 */ 27 28 #include <signal.h> 29 #include <fcntl.h> 30 #include <sys/stat.h> 31 #include <sys/wait.h> 32 33 #include "filebench.h" 34 #include "procflow.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 static void procflow_sleep(procflow_t *procflow, int wait_cnt); 45 46 #ifdef USE_PROCESS_MODEL 47 48 static enum create_n_wait { 49 CNW_DONE, 50 CNW_ERROR 51 } cnw_wait; 52 53 #endif /* USE_PROCESS_MODEL */ 54 55 56 /* 57 * Procflows are filebench entities which manage processes. Each 58 * worker procflow spawns a separate filebench process, with attributes 59 * inherited from a FLOW_MASTER procflow created during f model language 60 * parsing. This section contains routines to define, create, control, 61 * and delete procflows. 62 * 63 * Each process defined in the f model creates a FLOW_MASTER 64 * procflow which encapsulates the defined attributes, and threads of 65 * the f process, including the number of instances to create. At 66 * runtime, a worker procflow instance with an associated filebench 67 * process is created, which runs until told to quite by the original 68 * filebench process or is specifically deleted. 69 */ 70 71 72 /* 73 * Prints a summary of the syntax for setting procflow parameters. 74 */ 75 void 76 procflow_usage(void) 77 { 78 (void) fprintf(stderr, 79 "define process name=<name>[,instances=<count>]\n"); 80 (void) fprintf(stderr, "{\n"); 81 (void) fprintf(stderr, " thread ...\n"); 82 (void) fprintf(stderr, " thread ...\n"); 83 (void) fprintf(stderr, " thread ...\n"); 84 (void) fprintf(stderr, "}\n"); 85 (void) fprintf(stderr, "\n"); 86 (void) fprintf(stderr, "\n"); 87 } 88 89 /* 90 * If filebench has been compiled to support multiple processes 91 * (USE_PROCESS_MODEL defined), this routine forks a child 92 * process and uses either system() or exec() to start up a new 93 * instance of filebench, passing it the procflow name, instance 94 * number and shared memory region address. 95 * If USE_PROCESS_MODEL is NOT defined, then the routine 96 * just creates a child thread which begins executing 97 * threadflow_init() for the specified procflow. 98 */ 99 static int 100 procflow_createproc(procflow_t *procflow) 101 { 102 char instance[128]; 103 char shmaddr[128]; 104 char procname[128]; 105 pid_t pid; 106 107 #ifdef USE_PROCESS_MODEL 108 109 (void) snprintf(instance, sizeof (instance), "%d", 110 procflow->pf_instance); 111 (void) snprintf(procname, sizeof (procname), "%s", procflow->pf_name); 112 #if defined(_LP64) || (__WORDSIZE == 64) 113 (void) snprintf(shmaddr, sizeof (shmaddr), "%llx", filebench_shm); 114 #else 115 (void) snprintf(shmaddr, sizeof (shmaddr), "%x", filebench_shm); 116 #endif 117 filebench_log(LOG_DEBUG_IMPL, "creating process %s", 118 procflow->pf_name); 119 120 procflow->pf_running = 0; 121 122 #ifdef HAVE_FORK1 123 if ((pid = fork1()) < 0) { 124 filebench_log(LOG_ERROR, 125 "procflow_createproc fork failed: %s", 126 strerror(errno)); 127 return (-1); 128 } 129 #else 130 if ((pid = fork()) < 0) { 131 filebench_log(LOG_ERROR, 132 "procflow_createproc fork failed: %s", 133 strerror(errno)); 134 return (-1); 135 } 136 #endif /* HAVE_FORK1 */ 137 138 /* if child, start up new copy of filebench */ 139 if (pid == 0) { 140 #ifdef USE_SYSTEM 141 char syscmd[1024]; 142 #endif 143 144 (void) sigignore(SIGINT); 145 filebench_log(LOG_DEBUG_SCRIPT, 146 "Starting %s-%d", procflow->pf_name, 147 procflow->pf_instance); 148 /* Child */ 149 150 #ifdef USE_SYSTEM 151 (void) snprintf(syscmd, sizeof (syscmd), "%s -a %s -i %s -s %s", 152 execname, 153 procname, 154 instance, 155 shmaddr); 156 if (system(syscmd) < 0) { 157 filebench_log(LOG_ERROR, 158 "procflow exec proc failed: %s", 159 strerror(errno)); 160 filebench_shutdown(1); 161 } 162 163 #else 164 if (execlp(execname, procname, "-a", procname, "-i", 165 instance, "-s", shmaddr, "-m", shmpath, NULL) < 0) { 166 filebench_log(LOG_ERROR, 167 "procflow exec proc failed: %s", 168 strerror(errno)); 169 filebench_shutdown(1); 170 } 171 #endif 172 exit(1); 173 } else { 174 /* if parent, save pid and return */ 175 procflow->pf_pid = pid; 176 } 177 #else 178 procflow->pf_running = 1; 179 if (pthread_create(&procflow->pf_tid, NULL, 180 (void *(*)(void*))threadflow_init, procflow) != 0) { 181 filebench_log(LOG_ERROR, "proc-thread create failed"); 182 procflow->pf_running = 0; 183 } 184 #endif 185 filebench_log(LOG_DEBUG_IMPL, "procflow_createproc created pid %d", 186 pid); 187 188 return (0); 189 } 190 191 /* 192 * Find a procflow of name "name" and instance "instance" on the 193 * master procflow list, filebench_shm->shm_proclist. Locks the list 194 * and scans through it searching for a procflow with matching 195 * name and instance number. If found returns a pointer to the 196 * procflow, otherwise returns NULL. 197 */ 198 static procflow_t * 199 procflow_find(char *name, int instance) 200 { 201 procflow_t *procflow = filebench_shm->shm_proclist; 202 203 filebench_log(LOG_DEBUG_IMPL, "Find: (%s-%d) proclist = %zx", 204 name, instance, procflow); 205 206 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 207 208 while (procflow) { 209 filebench_log(LOG_DEBUG_IMPL, "Find: (%s-%d) == (%s-%d)", 210 name, instance, 211 procflow->pf_name, 212 procflow->pf_instance); 213 if ((strcmp(name, procflow->pf_name) == 0) && 214 (instance == procflow->pf_instance)) { 215 216 (void) ipc_mutex_unlock( 217 &filebench_shm->shm_procflow_lock); 218 219 return (procflow); 220 } 221 procflow = procflow->pf_next; 222 } 223 224 (void) ipc_mutex_unlock(&filebench_shm->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->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->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 (void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock); 333 filebench_shm->shm_procs_running --; 334 (void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock); 335 procflow->pf_running = 0; 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->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(&filebench_shm->shm_procflow_procs_cv) != 0) 359 exit(1); 360 361 (void) ipc_mutex_unlock(&filebench_shm->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 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 372 /* if normal shutdown in progress, just quit */ 373 if (filebench_shm->shm_f_abort) 374 (void) ipc_mutex_unlock( 375 &filebench_shm->shm_procflow_lock); 376 pthread_exit(0); 377 378 /* if nothing running, exit */ 379 if (filebench_shm->shm_procs_running == 0) { 380 filebench_shm->shm_f_abort = FILEBENCH_ABORT_RSRC; 381 (void) ipc_mutex_unlock( 382 &filebench_shm->shm_procflow_lock); 383 pthread_exit(0); 384 } 385 (void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock); 386 387 if (status.si_code == CLD_EXITED) { 388 /* A process called exit(); check returned status */ 389 if (status.si_status != 0) { 390 filebench_log(LOG_ERROR, 391 "Unexpected Process termination; exiting", 392 status.si_status); 393 filebench_shutdown(1); 394 } 395 } else { 396 /* A process quit because of some fatal error */ 397 filebench_log(LOG_ERROR, 398 "Unexpected Process termination Code %d, Errno %d", 399 status.si_code, status.si_errno); 400 filebench_shutdown(1); 401 } 402 403 } 404 /* NOTREACHED */ 405 return (NULL); 406 } 407 408 /* 409 * Cancel all threads within a processes, as well as the process itself. 410 * Called by ^c or by sig_kill 411 */ 412 /* ARGSUSED */ 413 static void 414 procflow_cancel(int arg1) 415 { 416 filebench_log(LOG_DEBUG_IMPL, "Process signal handler on pid %", 417 my_procflow->pf_pid); 418 419 procflow_sleep(my_procflow, SHUTDOWN_WAIT_SECONDS); 420 421 threadflow_delete_all(&my_procflow->pf_threads); 422 423 /* quit the main procflow thread and hence the process */ 424 exit(0); 425 } 426 427 #endif /* USE_PROCESS_MODEL */ 428 429 /* 430 * Iterates through proclist, the master list of procflows, 431 * creating the number of instances of each procflow specified 432 * by its pf_instance attribute. Returns 0 on success, or -1 433 * times the number of procflow instances that were not 434 * successfully created. 435 */ 436 int 437 procflow_init(void) 438 { 439 procflow_t *procflow = filebench_shm->shm_proclist; 440 pthread_t tid; 441 int ret = 0; 442 443 filebench_log(LOG_DEBUG_IMPL, 444 "procflow_init %s, %llu", 445 procflow->pf_name, 446 (u_longlong_t)avd_get_int(procflow->pf_instances)); 447 448 #ifdef USE_PROCESS_MODEL 449 450 if ((pthread_create(&tid, NULL, procflow_createnwait, NULL)) != 0) 451 return (ret); 452 453 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 454 455 (void) signal(SIGUSR1, procflow_cancel); 456 457 if ((ret = pthread_cond_wait(&filebench_shm->shm_procflow_procs_cv, 458 &filebench_shm->shm_procflow_lock)) != 0) 459 return (ret); 460 461 if (cnw_wait == CNW_ERROR) 462 ret = -1; 463 464 #else /* USE_PROCESS_MODEL */ 465 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 466 467 ret = procflow_create_all_procs(); 468 #endif /* USE_PROCESS_MODEL */ 469 470 (void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock); 471 472 return (ret); 473 } 474 475 #ifdef USE_PROCESS_MODEL 476 /* 477 * Waits for child processes to finish and returns their exit 478 * status. Used by procflow_delete() when the process model is 479 * enabled to wait for a deleted process to exit. 480 */ 481 static void 482 procflow_wait(pid_t pid) 483 { 484 pid_t wpid; 485 int stat; 486 487 (void) waitpid(pid, &stat, 0); 488 while ((wpid = waitpid(getpid() * -1, &stat, WNOHANG)) > 0) 489 filebench_log(LOG_DEBUG_IMPL, "Waited for pid %d", (int)wpid); 490 } 491 #endif 492 493 /* 494 * Common routine to sleep for wait_cnt seconds or for pf_running to 495 * go false. Checks once a second to see if pf_running has gone false. 496 */ 497 static void 498 procflow_sleep(procflow_t *procflow, int wait_cnt) 499 { 500 while (procflow->pf_running & wait_cnt) { 501 sleep(1); 502 wait_cnt--; 503 } 504 } 505 506 /* 507 * Deletes the designated procflow. Finally it frees the 508 * procflow entity. filebench_shm->shm_procflow_lock must be held on entry. 509 * 510 * If the designated procflow is not found on the list it returns -1 and 511 * the procflow is not deleted. Otherwise it returns 0. 512 */ 513 static int 514 procflow_cleanup(procflow_t *procflow) 515 { 516 procflow_t *entry; 517 518 filebench_log(LOG_DEBUG_SCRIPT, 519 "Deleted proc: (%s-%d) pid %d", 520 procflow->pf_name, 521 procflow->pf_instance, 522 procflow->pf_pid); 523 524 procflow->pf_running = 0; 525 526 /* remove entry from proclist */ 527 entry = filebench_shm->shm_proclist; 528 529 /* unlink procflow entity from proclist */ 530 if (entry == procflow) { 531 /* at head of list */ 532 filebench_shm->shm_proclist = procflow->pf_next; 533 } else { 534 /* search list for procflow */ 535 while (entry && entry->pf_next != procflow) 536 entry = entry->pf_next; 537 538 /* if entity found, unlink it */ 539 if (entry == NULL) 540 return (-1); 541 else 542 entry->pf_next = procflow->pf_next; 543 } 544 545 /* free up the procflow entity */ 546 ipc_free(FILEBENCH_PROCFLOW, (char *)procflow); 547 return (0); 548 } 549 550 551 /* 552 * Waits till all threadflows are started, or a timeout occurs. 553 * Checks through the list of procflows, waiting up to 30 554 * seconds for each one to set its pf_running flag to 1. If not 555 * set after 30 seconds, continues on to the next procflow 556 * anyway after logging the fact. Once pf_running is set 557 * to 1 for a given procflow or the timeout is reached, 558 * threadflow_allstarted() is called to start the threads. 559 * Returns 0 (OK), unless filebench_shm->shm_f_abort is signaled, 560 * in which case it returns -1. 561 */ 562 int 563 procflow_allstarted() 564 { 565 procflow_t *procflow = filebench_shm->shm_proclist; 566 int running_procs = 0; 567 int ret = 0; 568 569 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 570 571 (void) sleep(1); 572 573 while (procflow) { 574 int waits; 575 576 if (procflow->pf_instance && 577 (procflow->pf_instance == FLOW_MASTER)) { 578 procflow = procflow->pf_next; 579 continue; 580 } 581 582 waits = 10; 583 while (waits && procflow->pf_running == 0) { 584 (void) ipc_mutex_unlock( 585 &filebench_shm->shm_procflow_lock); 586 if (filebench_shm->shm_f_abort == 1) 587 return (-1); 588 589 if (waits < 3) 590 filebench_log(LOG_INFO, 591 "Waiting for process %s-%d %d", 592 procflow->pf_name, 593 procflow->pf_instance, 594 procflow->pf_pid); 595 596 (void) sleep(3); 597 waits--; 598 (void) ipc_mutex_lock( 599 &filebench_shm->shm_procflow_lock); 600 } 601 602 if (waits == 0) 603 filebench_log(LOG_INFO, 604 "Failed to start process %s-%d", 605 procflow->pf_name, 606 procflow->pf_instance); 607 608 running_procs++; 609 threadflow_allstarted(procflow->pf_pid, procflow->pf_threads); 610 611 procflow = procflow->pf_next; 612 } 613 (void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock); 614 filebench_shm->shm_procs_running = running_procs; 615 (void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock); 616 617 (void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock); 618 619 620 return (ret); 621 } 622 623 624 /* 625 * Sets the f_abort flag and clears the running count to stop 626 * all the flowop execution threads from running. Iterates 627 * through the procflow list and deletes all procflows except 628 * for the FLOW_MASTER procflow. Resets the f_abort flag when 629 * finished. 630 * 631 */ 632 void 633 procflow_shutdown(void) 634 { 635 procflow_t *procflow, *next_procflow; 636 int wait_cnt = SHUTDOWN_WAIT_SECONDS; 637 638 (void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock); 639 if (filebench_shm->shm_procs_running <= 0) { 640 /* No processes running, so no need to do anything */ 641 (void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock); 642 return; 643 } 644 (void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock); 645 646 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 647 procflow = filebench_shm->shm_proclist; 648 filebench_shm->shm_f_abort = 1; 649 650 while (procflow) { 651 if (procflow->pf_instance && 652 (procflow->pf_instance == FLOW_MASTER)) { 653 procflow = procflow->pf_next; 654 continue; 655 } 656 filebench_log(LOG_DEBUG_IMPL, "Deleting process %s-%d %d", 657 procflow->pf_name, 658 procflow->pf_instance, 659 procflow->pf_pid); 660 661 next_procflow = procflow->pf_next; 662 663 /* 664 * Signalling the process with SIGUSR1 will result in it 665 * gracefully shutting down and exiting 666 */ 667 procflow_sleep(procflow, wait_cnt); 668 if (procflow->pf_running) { 669 #ifdef USE_PROCESS_MODEL 670 pid_t pid; 671 672 pid = procflow->pf_pid; 673 #ifdef HAVE_SIGSEND 674 (void) sigsend(P_PID, pid, SIGUSR1); 675 #else 676 (void) kill(pid, SIGUSR1); 677 #endif 678 procflow_wait(pid); 679 680 #else /* USE_PROCESS_MODEL */ 681 threadflow_delete_all(&procflow->pf_threads); 682 #endif /* USE_PROCESS_MODEL */ 683 } 684 (void) procflow_cleanup(procflow); 685 procflow = next_procflow; 686 if (wait_cnt > 0) 687 wait_cnt--; 688 } 689 690 filebench_shm->shm_f_abort = 0; 691 692 (void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock); 693 694 /* indicate all processes are stopped, even if some are "stuck" */ 695 (void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock); 696 filebench_shm->shm_procs_running = 0; 697 (void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock); 698 } 699 700 701 /* 702 * Create an in-memory process object. Allocates a procflow 703 * entity, initialized from the "inherit" procflow if supplied. 704 * The name and instance number are set from the supplied name 705 * and instance number and the procflow is added to the head of 706 * the master procflow list. Returns pointer to the allocated 707 * procflow, or NULL if a name isn't supplied or the procflow 708 * entity cannot be allocated. 709 * 710 * The calling routine must hold the filebench_shm->shm_procflow_lock. 711 */ 712 static procflow_t * 713 procflow_define_common(procflow_t **list, char *name, 714 procflow_t *inherit, int instance) 715 { 716 procflow_t *procflow; 717 718 if (name == NULL) 719 return (NULL); 720 721 procflow = (procflow_t *)ipc_malloc(FILEBENCH_PROCFLOW); 722 723 if (procflow == NULL) 724 return (NULL); 725 726 if (inherit) 727 (void) memcpy(procflow, inherit, sizeof (procflow_t)); 728 else 729 (void) memset(procflow, 0, sizeof (procflow_t)); 730 731 procflow->pf_instance = instance; 732 (void) strcpy(procflow->pf_name, name); 733 734 filebench_log(LOG_DEBUG_IMPL, "defining process %s-%d", name, instance); 735 736 /* Add procflow to list, lock is being held already */ 737 if (*list == NULL) { 738 *list = procflow; 739 procflow->pf_next = NULL; 740 } else { 741 procflow->pf_next = *list; 742 *list = procflow; 743 } 744 filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx", 745 name, instance, filebench_shm->shm_proclist); 746 747 return (procflow); 748 } 749 750 /* 751 * Create an in-memory process object as described by the syntax. 752 * Acquires the filebench_shm->shm_procflow_lock and calls 753 * procflow_define_common() to create and initialize a 754 * FLOW_MASTER procflow entity from the optional "inherit" 755 * procflow with the given name and configured for "instances" 756 * number of worker procflows. Currently only called from 757 * parser_proc_define(). 758 */ 759 procflow_t * 760 procflow_define(char *name, procflow_t *inherit, avd_t instances) 761 { 762 procflow_t *procflow; 763 764 (void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock); 765 766 procflow = procflow_define_common(&filebench_shm->shm_proclist, 767 name, inherit, FLOW_MASTER); 768 procflow->pf_instances = instances; 769 770 (void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock); 771 772 return (procflow); 773 } 774