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