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 */
39pid_t my_pid;
40procflow_t *my_procflow = NULL;
41
42static procflow_t *procflow_define_common(procflow_t **list, char *name,
43    procflow_t *inherit, int instance);
44static void procflow_sleep(procflow_t *procflow, int wait_cnt);
45
46#ifdef USE_PROCESS_MODEL
47
48static 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 */
75void
76procflow_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 */
99static int
100procflow_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 */
198static procflow_t *
199procflow_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
229static int
230procflow_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 */
276int
277procflow_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 */
348static void *
349procflow_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
379		/* if nothing running, exit */
380		if (filebench_shm->shm_procs_running == 0) {
381			filebench_shm->shm_f_abort = FILEBENCH_ABORT_RSRC;
382			(void) ipc_mutex_unlock(
383			    &filebench_shm->shm_procflow_lock);
384			pthread_exit(0);
385		}
386		(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
387
388		if (status.si_code == CLD_EXITED) {
389			/* A process called exit(); check returned status */
390			if (status.si_status != 0) {
391				filebench_log(LOG_ERROR,
392				    "Unexpected Process termination; exiting",
393				    status.si_status);
394				filebench_shutdown(1);
395			}
396		} else {
397			/* A process quit because of some fatal error */
398			filebench_log(LOG_ERROR,
399			    "Unexpected Process termination Code %d, Errno %d",
400			    status.si_code, status.si_errno);
401			filebench_shutdown(1);
402		}
403
404	}
405	/* NOTREACHED */
406	return (NULL);
407}
408
409/*
410 * Cancel all threads within a processes, as well as the process itself.
411 * Called by ^c or by sig_kill
412 */
413/* ARGSUSED */
414static void
415procflow_cancel(int arg1)
416{
417	filebench_log(LOG_DEBUG_IMPL, "Process signal handler on pid %",
418	    my_procflow->pf_pid);
419
420	procflow_sleep(my_procflow, SHUTDOWN_WAIT_SECONDS);
421
422	threadflow_delete_all(&my_procflow->pf_threads);
423
424	/* quit the main procflow thread and hence the process */
425	exit(0);
426}
427
428#endif	/* USE_PROCESS_MODEL */
429
430/*
431 * Iterates through proclist, the master list of procflows,
432 * creating the number of instances of each procflow specified
433 * by its pf_instance attribute. Returns 0 on success, or -1
434 * times the number of procflow instances that were not
435 * successfully created.
436 */
437int
438procflow_init(void)
439{
440	procflow_t *procflow = filebench_shm->shm_proclist;
441	pthread_t tid;
442	int ret = 0;
443
444	if (procflow == NULL) {
445		filebench_log(LOG_ERROR, "Workload has no processes");
446		return (FILEBENCH_ERROR);
447	}
448
449	filebench_log(LOG_DEBUG_IMPL,
450	    "procflow_init %s, %llu",
451	    procflow->pf_name,
452	    (u_longlong_t)avd_get_int(procflow->pf_instances));
453
454#ifdef USE_PROCESS_MODEL
455	if ((pthread_create(&tid, NULL, procflow_createnwait, NULL)) != 0)
456		return (ret);
457
458	(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
459
460	(void) signal(SIGUSR1, procflow_cancel);
461
462	if ((ret = pthread_cond_wait(&filebench_shm->shm_procflow_procs_cv,
463	    &filebench_shm->shm_procflow_lock)) != 0)
464		return (ret);
465
466	if (cnw_wait == CNW_ERROR)
467		ret = -1;
468
469#else /* USE_PROCESS_MODEL */
470	(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
471
472	ret = procflow_create_all_procs();
473#endif /* USE_PROCESS_MODEL */
474
475	(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
476
477	return (ret);
478}
479
480#ifdef USE_PROCESS_MODEL
481/*
482 * Waits for child processes to finish and returns their exit
483 * status. Used by procflow_delete() when the process model is
484 * enabled to wait for a deleted process to exit.
485 */
486static void
487procflow_wait(pid_t pid)
488{
489	pid_t wpid;
490	int stat;
491
492	(void) waitpid(pid, &stat, 0);
493	while ((wpid = waitpid(getpid() * -1, &stat, WNOHANG)) > 0)
494		filebench_log(LOG_DEBUG_IMPL, "Waited for pid %d", (int)wpid);
495}
496#endif
497
498/*
499 * Common routine to sleep for wait_cnt seconds or for pf_running to
500 * go false. Checks once a second to see if pf_running has gone false.
501 */
502static void
503procflow_sleep(procflow_t *procflow, int wait_cnt)
504{
505	while (procflow->pf_running & wait_cnt) {
506		(void) sleep(1);
507		wait_cnt--;
508	}
509}
510
511/*
512 * Deletes the designated procflow. Finally it frees the
513 * procflow entity. filebench_shm->shm_procflow_lock must be held on entry.
514 *
515 * If the designated procflow is not found on the list it returns -1 and
516 * the procflow is not deleted. Otherwise it returns 0.
517 */
518static int
519procflow_cleanup(procflow_t *procflow)
520{
521	procflow_t *entry;
522
523	filebench_log(LOG_DEBUG_SCRIPT,
524	    "Deleted proc: (%s-%d) pid %d",
525	    procflow->pf_name,
526	    procflow->pf_instance,
527	    procflow->pf_pid);
528
529	procflow->pf_running = 0;
530
531	/* remove entry from proclist */
532	entry = filebench_shm->shm_proclist;
533
534	/* unlink procflow entity from proclist */
535	if (entry == procflow) {
536		/* at head of list */
537		filebench_shm->shm_proclist = procflow->pf_next;
538	} else {
539		/* search list for procflow */
540		while (entry && entry->pf_next != procflow)
541			entry = entry->pf_next;
542
543		/* if entity found, unlink it */
544		if (entry == NULL)
545			return (-1);
546		else
547			entry->pf_next = procflow->pf_next;
548	}
549
550	/* free up the procflow entity */
551	ipc_free(FILEBENCH_PROCFLOW, (char *)procflow);
552	return (0);
553}
554
555
556/*
557 * Waits till all threadflows are started, or a timeout occurs.
558 * Checks through the list of procflows, waiting up to 30
559 * seconds for each one to set its pf_running flag to 1. If not
560 * set after 30 seconds, continues on to the next procflow
561 * anyway after logging the fact. Once pf_running is set
562 * to 1 for a given procflow or the timeout is reached,
563 * threadflow_allstarted() is called to start the threads.
564 * Returns 0 (OK), unless filebench_shm->shm_f_abort is signaled,
565 * in which case it returns -1.
566 */
567int
568procflow_allstarted()
569{
570	procflow_t *procflow = filebench_shm->shm_proclist;
571	int running_procs = 0;
572	int ret = 0;
573
574	(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
575
576	(void) sleep(1);
577
578	while (procflow) {
579		int waits;
580
581		if (procflow->pf_instance &&
582		    (procflow->pf_instance == FLOW_MASTER)) {
583			procflow = procflow->pf_next;
584			continue;
585		}
586
587		waits = 10;
588		while (waits && procflow->pf_running == 0) {
589			(void) ipc_mutex_unlock(
590			    &filebench_shm->shm_procflow_lock);
591			if (filebench_shm->shm_f_abort == 1)
592				return (-1);
593
594			if (waits < 3)
595				filebench_log(LOG_INFO,
596				    "Waiting for process %s-%d %d",
597				    procflow->pf_name,
598				    procflow->pf_instance,
599				    procflow->pf_pid);
600
601			(void) sleep(3);
602			waits--;
603			(void) ipc_mutex_lock(
604			    &filebench_shm->shm_procflow_lock);
605		}
606
607		if (waits == 0)
608			filebench_log(LOG_INFO,
609			    "Failed to start process %s-%d",
610			    procflow->pf_name,
611			    procflow->pf_instance);
612
613		running_procs++;
614		threadflow_allstarted(procflow->pf_pid, procflow->pf_threads);
615
616		procflow = procflow->pf_next;
617	}
618	(void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock);
619	filebench_shm->shm_procs_running = running_procs;
620	(void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock);
621
622	(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
623
624
625	return (ret);
626}
627
628
629/*
630 * Sets the f_abort flag and clears the running count to stop
631 * all the flowop execution threads from running. Iterates
632 * through the procflow list and deletes all procflows except
633 * for the FLOW_MASTER procflow. Resets the f_abort flag when
634 * finished.
635 *
636 */
637void
638procflow_shutdown(void)
639{
640	procflow_t *procflow, *next_procflow;
641	int wait_cnt = SHUTDOWN_WAIT_SECONDS;
642
643	(void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock);
644	if (filebench_shm->shm_procs_running <= 0) {
645		/* No processes running, so no need to do anything */
646		(void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock);
647		return;
648	}
649	(void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock);
650
651	(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
652	if (filebench_shm->shm_f_abort == FILEBENCH_ABORT_FINI) {
653		(void) ipc_mutex_unlock(
654		    &filebench_shm->shm_procflow_lock);
655		return;
656	}
657
658	procflow = filebench_shm->shm_proclist;
659	if (filebench_shm->shm_f_abort == FILEBENCH_OK)
660		filebench_shm->shm_f_abort = FILEBENCH_ABORT_DONE;
661
662	while (procflow) {
663		if (procflow->pf_instance &&
664		    (procflow->pf_instance == FLOW_MASTER)) {
665			procflow = procflow->pf_next;
666			continue;
667		}
668		filebench_log(LOG_DEBUG_IMPL, "Deleting process %s-%d %d",
669		    procflow->pf_name,
670		    procflow->pf_instance,
671		    procflow->pf_pid);
672
673		next_procflow = procflow->pf_next;
674
675		/*
676		 * Signalling the process with SIGUSR1 will result in it
677		 * gracefully shutting down and exiting
678		 */
679		procflow_sleep(procflow, wait_cnt);
680		if (procflow->pf_running) {
681#ifdef USE_PROCESS_MODEL
682			pid_t pid;
683
684			pid = procflow->pf_pid;
685#ifdef HAVE_SIGSEND
686			(void) sigsend(P_PID, pid, SIGUSR1);
687#else
688			(void) kill(pid, SIGUSR1);
689#endif
690			procflow_wait(pid);
691
692#else /* USE_PROCESS_MODEL */
693			threadflow_delete_all(&procflow->pf_threads);
694#endif /* USE_PROCESS_MODEL */
695		}
696		(void) procflow_cleanup(procflow);
697		procflow = next_procflow;
698		if (wait_cnt > 0)
699			wait_cnt--;
700	}
701
702	filebench_shm->shm_f_abort = FILEBENCH_ABORT_FINI;
703	(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
704
705	/* indicate all processes are stopped, even if some are "stuck" */
706	(void) ipc_mutex_lock(&filebench_shm->shm_procs_running_lock);
707	filebench_shm->shm_procs_running = 0;
708	(void) ipc_mutex_unlock(&filebench_shm->shm_procs_running_lock);
709}
710
711
712/*
713 * Create an in-memory process object. Allocates a procflow
714 * entity, initialized from the "inherit" procflow if supplied.
715 * The name and instance number are set from the supplied name
716 * and instance number and the procflow is added to the head of
717 * the master procflow list. Returns pointer to the allocated
718 * procflow, or NULL if a name isn't supplied or the procflow
719 * entity cannot be allocated.
720 *
721 * The calling routine must hold the filebench_shm->shm_procflow_lock.
722 */
723static procflow_t *
724procflow_define_common(procflow_t **list, char *name,
725    procflow_t *inherit, int instance)
726{
727	procflow_t *procflow;
728
729	if (name == NULL)
730		return (NULL);
731
732	procflow = (procflow_t *)ipc_malloc(FILEBENCH_PROCFLOW);
733
734	if (procflow == NULL)
735		return (NULL);
736
737	if (inherit)
738		(void) memcpy(procflow, inherit, sizeof (procflow_t));
739	else
740		(void) memset(procflow, 0, sizeof (procflow_t));
741
742	procflow->pf_instance = instance;
743	(void) strcpy(procflow->pf_name, name);
744
745	filebench_log(LOG_DEBUG_IMPL, "defining process %s-%d", name, instance);
746
747	/* Add procflow to list, lock is being held already */
748	if (*list == NULL) {
749		*list = procflow;
750		procflow->pf_next = NULL;
751	} else {
752		procflow->pf_next = *list;
753		*list = procflow;
754	}
755	filebench_log(LOG_DEBUG_IMPL, "process %s-%d proclist %zx",
756	    name, instance, filebench_shm->shm_proclist);
757
758	return (procflow);
759}
760
761/*
762 * Create an in-memory process object as described by the syntax.
763 * Acquires the filebench_shm->shm_procflow_lock and calls
764 * procflow_define_common() to create and initialize a
765 * FLOW_MASTER procflow entity from the optional "inherit"
766 * procflow with the given name and configured for "instances"
767 * number of worker procflows. Currently only called from
768 * parser_proc_define().
769 */
770procflow_t *
771procflow_define(char *name, procflow_t *inherit, avd_t instances)
772{
773	procflow_t *procflow;
774
775	(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
776
777	procflow = procflow_define_common(&filebench_shm->shm_proclist,
778	    name, inherit, FLOW_MASTER);
779	procflow->pf_instances = instances;
780
781	(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
782
783	return (procflow);
784}
785