xref: /onnv-gate/usr/src/cmd/filebench/common/procflow.c (revision 6286:edb6e4556869)
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