1*5184Sek110237 /*
2*5184Sek110237  * CDDL HEADER START
3*5184Sek110237  *
4*5184Sek110237  * The contents of this file are subject to the terms of the
5*5184Sek110237  * Common Development and Distribution License (the "License").
6*5184Sek110237  * You may not use this file except in compliance with the License.
7*5184Sek110237  *
8*5184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5184Sek110237  * or http://www.opensolaris.org/os/licensing.
10*5184Sek110237  * See the License for the specific language governing permissions
11*5184Sek110237  * and limitations under the License.
12*5184Sek110237  *
13*5184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
14*5184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
16*5184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
17*5184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5184Sek110237  *
19*5184Sek110237  * CDDL HEADER END
20*5184Sek110237  */
21*5184Sek110237 /*
22*5184Sek110237  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5184Sek110237  * Use is subject to license terms.
24*5184Sek110237  */
25*5184Sek110237 
26*5184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5184Sek110237 
28*5184Sek110237 #include "config.h"
29*5184Sek110237 #include <pthread.h>
30*5184Sek110237 #ifdef HAVE_LWPS
31*5184Sek110237 #include <sys/lwp.h>
32*5184Sek110237 #endif
33*5184Sek110237 #include <signal.h>
34*5184Sek110237 #include "threadflow.h"
35*5184Sek110237 #include "filebench.h"
36*5184Sek110237 #include "flowop.h"
37*5184Sek110237 #include "ipc.h"
38*5184Sek110237 
39*5184Sek110237 static threadflow_t *threadflow_define_common(procflow_t *procflow,
40*5184Sek110237     char *name, threadflow_t *inherit, int instance);
41*5184Sek110237 
42*5184Sek110237 /*
43*5184Sek110237  * Threadflows are filebench entities which manage operating system
44*5184Sek110237  * threads. Each worker threadflow spawns a separate filebench thread,
45*5184Sek110237  * with attributes inherited from a FLOW_MASTER threadflow created during
46*5184Sek110237  * f model language parsing. This section contains routines to define,
47*5184Sek110237  * create, control, and delete threadflows.
48*5184Sek110237  *
49*5184Sek110237  * Each thread defined in the f model creates a FLOW_MASTER
50*5184Sek110237  * threadflow which encapsulates the defined attributes and flowops of
51*5184Sek110237  * the f language thread, including the number of instances to create.
52*5184Sek110237  * At runtime, a worker threadflow instance with an associated filebench
53*5184Sek110237  * thread is created, which runs until told to quit or is specifically
54*5184Sek110237  * deleted.
55*5184Sek110237  */
56*5184Sek110237 
57*5184Sek110237 
58*5184Sek110237 /*
59*5184Sek110237  * Prints information about threadflow syntax.
60*5184Sek110237  */
61*5184Sek110237 void
62*5184Sek110237 threadflow_usage(void)
63*5184Sek110237 {
64*5184Sek110237 	(void) fprintf(stderr, "  thread  name=<name>[,instances=<count>]\n");
65*5184Sek110237 	(void) fprintf(stderr, "\n");
66*5184Sek110237 	(void) fprintf(stderr, "  {\n");
67*5184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
68*5184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
69*5184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
70*5184Sek110237 	(void) fprintf(stderr, "  }\n");
71*5184Sek110237 	(void) fprintf(stderr, "\n");
72*5184Sek110237 }
73*5184Sek110237 
74*5184Sek110237 /*
75*5184Sek110237  * Creates a thread for the supplied threadflow. If interprocess
76*5184Sek110237  * shared memory is desired, then increments the amount of shared
77*5184Sek110237  * memory needed by the amount specified in the threadflow's
78*5184Sek110237  * tf_memsize parameter. The thread starts in routine
79*5184Sek110237  * flowop_start() with a poineter to the threadflow supplied
80*5184Sek110237  * as the argument.
81*5184Sek110237  */
82*5184Sek110237 static int
83*5184Sek110237 threadflow_createthread(threadflow_t *threadflow)
84*5184Sek110237 {
85*5184Sek110237 	int fp = 0;
86*5184Sek110237 
87*5184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Creating thread %s, memory = %ld",
88*5184Sek110237 	    threadflow->tf_name,
89*5184Sek110237 	    *threadflow->tf_memsize);
90*5184Sek110237 
91*5184Sek110237 	if (threadflow->tf_attrs & THREADFLOW_USEISM)
92*5184Sek110237 		filebench_shm->shm_required += (*threadflow->tf_memsize);
93*5184Sek110237 
94*5184Sek110237 	if (pthread_create(&threadflow->tf_tid, NULL,
95*5184Sek110237 	    (void *(*)(void*))flowop_start, threadflow) != 0) {
96*5184Sek110237 		filebench_log(LOG_ERROR, "thread create failed");
97*5184Sek110237 		filebench_shutdown(1);
98*5184Sek110237 	}
99*5184Sek110237 
100*5184Sek110237 	/* XXX */
101*5184Sek110237 	return (fp < 0);
102*5184Sek110237 }
103*5184Sek110237 
104*5184Sek110237 #ifndef USE_PROCESS_MODEL
105*5184Sek110237 static procflow_t *my_procflow;
106*5184Sek110237 
107*5184Sek110237 /*
108*5184Sek110237  * Terminates (exits) all the threads of the procflow (process).
109*5184Sek110237  * The procflow is determined from a process private pointer
110*5184Sek110237  * initialized by threadflow_init().
111*5184Sek110237  */
112*5184Sek110237 /* ARGSUSED */
113*5184Sek110237 static void
114*5184Sek110237 threadflow_cancel(int arg1)
115*5184Sek110237 {
116*5184Sek110237 	threadflow_t *threadflow = my_procflow->pf_threads;
117*5184Sek110237 
118*5184Sek110237 #ifdef HAVE_LWPS
119*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Thread signal handler on tid %d",
120*5184Sek110237 	    _lwp_self());
121*5184Sek110237 #endif
122*5184Sek110237 
123*5184Sek110237 	my_procflow->pf_running = 0;
124*5184Sek110237 	exit(0);
125*5184Sek110237 
126*5184Sek110237 	while (threadflow) {
127*5184Sek110237 		if (threadflow->tf_tid) {
128*5184Sek110237 			(void) pthread_cancel(threadflow->tf_tid);
129*5184Sek110237 			filebench_log(LOG_DEBUG_IMPL, "Thread %d cancelled...",
130*5184Sek110237 			    threadflow->tf_tid);
131*5184Sek110237 		}
132*5184Sek110237 		threadflow = threadflow->tf_next;
133*5184Sek110237 	}
134*5184Sek110237 }
135*5184Sek110237 #endif /* USE_PROCESS_MODEL */
136*5184Sek110237 
137*5184Sek110237 /*
138*5184Sek110237  * Creates threads for the threadflows associated with a procflow.
139*5184Sek110237  * The routine iterates through the list of threadflows in the
140*5184Sek110237  * supplied procflow's pf_threads list. For each threadflow on
141*5184Sek110237  * the list, it defines tf_instances number of cloned
142*5184Sek110237  * threadflows, and then calls threadflow_createthread() for
143*5184Sek110237  * each to create and start the actual operating system thread.
144*5184Sek110237  * Note that each of the newly defined threadflows will be linked
145*5184Sek110237  * into the procflows threadflow list, but at the head of the
146*5184Sek110237  * list, so they will not become part of the supplied set. After
147*5184Sek110237  * all the threads have been created, threadflow_init enters
148*5184Sek110237  * a join loop for all the threads in the newly defined
149*5184Sek110237  * threadflows. Once all the created threads have exited,
150*5184Sek110237  * threadflow_init will return 0. If errors are encountered, it
151*5184Sek110237  * will return a non zero value.
152*5184Sek110237  */
153*5184Sek110237 int
154*5184Sek110237 threadflow_init(procflow_t *procflow)
155*5184Sek110237 {
156*5184Sek110237 	threadflow_t *threadflow = procflow->pf_threads;
157*5184Sek110237 	int ret = 0;
158*5184Sek110237 
159*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
160*5184Sek110237 #ifndef USE_PROCESS_MODEL
161*5184Sek110237 	my_procflow = procflow;
162*5184Sek110237 
163*5184Sek110237 	(void) signal(SIGUSR1, threadflow_cancel);
164*5184Sek110237 #endif
165*5184Sek110237 	while (threadflow) {
166*5184Sek110237 		threadflow_t *newthread;
167*5184Sek110237 		int i;
168*5184Sek110237 
169*5184Sek110237 		filebench_log(LOG_VERBOSE,
170*5184Sek110237 		    "Starting %lld %s threads",
171*5184Sek110237 		    *(threadflow->tf_instances),
172*5184Sek110237 		    threadflow->tf_name);
173*5184Sek110237 
174*5184Sek110237 		for (i = 1; i < *threadflow->tf_instances; i++) {
175*5184Sek110237 			/* Create threads */
176*5184Sek110237 			newthread =
177*5184Sek110237 			    threadflow_define_common(procflow,
178*5184Sek110237 			    threadflow->tf_name, threadflow, i + 1);
179*5184Sek110237 			if (newthread == NULL)
180*5184Sek110237 				return (-1);
181*5184Sek110237 			ret += threadflow_createthread(newthread);
182*5184Sek110237 		}
183*5184Sek110237 
184*5184Sek110237 		newthread = threadflow_define_common(procflow,
185*5184Sek110237 		    threadflow->tf_name,
186*5184Sek110237 		    threadflow, 1);
187*5184Sek110237 
188*5184Sek110237 		if (newthread == NULL)
189*5184Sek110237 			return (-1);
190*5184Sek110237 
191*5184Sek110237 		/* Create threads */
192*5184Sek110237 		ret += threadflow_createthread(newthread);
193*5184Sek110237 
194*5184Sek110237 		threadflow = threadflow->tf_next;
195*5184Sek110237 	}
196*5184Sek110237 
197*5184Sek110237 	threadflow = procflow->pf_threads;
198*5184Sek110237 
199*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->threadflow_lock);
200*5184Sek110237 
201*5184Sek110237 	while (threadflow) {
202*5184Sek110237 		void *status;
203*5184Sek110237 
204*5184Sek110237 		if (threadflow->tf_tid)
205*5184Sek110237 			(void) pthread_join(threadflow->tf_tid, &status);
206*5184Sek110237 
207*5184Sek110237 		ret += *(int *)status;
208*5184Sek110237 		threadflow = threadflow->tf_next;
209*5184Sek110237 	}
210*5184Sek110237 
211*5184Sek110237 	procflow->pf_running = 0;
212*5184Sek110237 
213*5184Sek110237 	return (ret);
214*5184Sek110237 }
215*5184Sek110237 
216*5184Sek110237 /*
217*5184Sek110237  * Tells the threadflow's thread to stop and optionally signals
218*5184Sek110237  * its associated process to end the thread.
219*5184Sek110237  */
220*5184Sek110237 static void
221*5184Sek110237 threadflow_kill(threadflow_t *threadflow)
222*5184Sek110237 {
223*5184Sek110237 	/* Tell thread to finish */
224*5184Sek110237 	threadflow->tf_abort = 1;
225*5184Sek110237 
226*5184Sek110237 #ifdef USE_PROCESS_MODEL
227*5184Sek110237 #ifdef HAVE_SIGSEND
228*5184Sek110237 	(void) sigsend(P_PID, threadflow->tf_process->pf_pid, SIGUSR1);
229*5184Sek110237 #else
230*5184Sek110237 	(void) kill(threadflow->tf_process->pf_pid, SIGUSR1);
231*5184Sek110237 #endif
232*5184Sek110237 #else /* USE_PROCESS_MODEL */
233*5184Sek110237 	threadflow->tf_process->pf_running = 0;
234*5184Sek110237 #endif /* USE_PROCESS_MODEL */
235*5184Sek110237 }
236*5184Sek110237 
237*5184Sek110237 /*
238*5184Sek110237  * Deletes the specified threadflow from the specified threadflow
239*5184Sek110237  * list after first terminating the threadflow's thread, deleting
240*5184Sek110237  * the threadflow's flowops, and finally freeing the threadflow
241*5184Sek110237  * entity. It also subtracts the threadflow's shared memory
242*5184Sek110237  * requirements from the total amount required, shm_required. If
243*5184Sek110237  * the specified threadflow is found, returns 0, otherwise
244*5184Sek110237  * returns -1.
245*5184Sek110237  */
246*5184Sek110237 static int
247*5184Sek110237 threadflow_delete(threadflow_t **threadlist, threadflow_t *threadflow)
248*5184Sek110237 {
249*5184Sek110237 	threadflow_t *entry = *threadlist;
250*5184Sek110237 
251*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Deleting thread: (%s-%d)",
252*5184Sek110237 	    threadflow->tf_name,
253*5184Sek110237 	    threadflow->tf_instance);
254*5184Sek110237 
255*5184Sek110237 	if (threadflow->tf_attrs & THREADFLOW_USEISM) {
256*5184Sek110237 		filebench_shm->shm_required -= (*threadflow->tf_memsize);
257*5184Sek110237 	}
258*5184Sek110237 
259*5184Sek110237 	if (threadflow == *threadlist) {
260*5184Sek110237 		/* First on list */
261*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Deleted thread: (%s-%d)",
262*5184Sek110237 		    threadflow->tf_name,
263*5184Sek110237 		    threadflow->tf_instance);
264*5184Sek110237 
265*5184Sek110237 		threadflow_kill(threadflow);
266*5184Sek110237 		flowop_delete_all(&threadflow->tf_ops);
267*5184Sek110237 		*threadlist = threadflow->tf_next;
268*5184Sek110237 		ipc_free(FILEBENCH_THREADFLOW, (char *)threadflow);
269*5184Sek110237 		return (0);
270*5184Sek110237 	}
271*5184Sek110237 
272*5184Sek110237 	while (entry->tf_next) {
273*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
274*5184Sek110237 		    "Delete thread: (%s-%d) == (%s-%d)",
275*5184Sek110237 		    entry->tf_next->tf_name,
276*5184Sek110237 		    entry->tf_next->tf_instance,
277*5184Sek110237 		    threadflow->tf_name,
278*5184Sek110237 		    threadflow->tf_instance);
279*5184Sek110237 
280*5184Sek110237 		if (threadflow == entry->tf_next) {
281*5184Sek110237 			/* Delete */
282*5184Sek110237 			filebench_log(LOG_DEBUG_IMPL,
283*5184Sek110237 			    "Deleted thread: (%s-%d)",
284*5184Sek110237 			    entry->tf_next->tf_name,
285*5184Sek110237 			    entry->tf_next->tf_instance);
286*5184Sek110237 			threadflow_kill(entry->tf_next);
287*5184Sek110237 			flowop_delete_all(&entry->tf_next->tf_ops);
288*5184Sek110237 			ipc_free(FILEBENCH_THREADFLOW, (char *)threadflow);
289*5184Sek110237 			entry->tf_next = entry->tf_next->tf_next;
290*5184Sek110237 			return (0);
291*5184Sek110237 		}
292*5184Sek110237 		entry = entry->tf_next;
293*5184Sek110237 	}
294*5184Sek110237 
295*5184Sek110237 	return (-1);
296*5184Sek110237 }
297*5184Sek110237 
298*5184Sek110237 /*
299*5184Sek110237  * Given a pointer to the thread list of a procflow, cycles
300*5184Sek110237  * through all the threadflows on the list, deleting each one
301*5184Sek110237  * except the FLOW_MASTER.
302*5184Sek110237  */
303*5184Sek110237 void
304*5184Sek110237 threadflow_delete_all(threadflow_t **threadlist)
305*5184Sek110237 {
306*5184Sek110237 	threadflow_t *threadflow = *threadlist;
307*5184Sek110237 
308*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
309*5184Sek110237 
310*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Deleting all threads");
311*5184Sek110237 
312*5184Sek110237 	while (threadflow) {
313*5184Sek110237 		if (threadflow->tf_instance &&
314*5184Sek110237 		    (threadflow->tf_instance == FLOW_MASTER)) {
315*5184Sek110237 			threadflow = threadflow->tf_next;
316*5184Sek110237 			continue;
317*5184Sek110237 		}
318*5184Sek110237 		(void) threadflow_delete(threadlist, threadflow);
319*5184Sek110237 		threadflow = threadflow->tf_next;
320*5184Sek110237 	}
321*5184Sek110237 
322*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->threadflow_lock);
323*5184Sek110237 }
324*5184Sek110237 
325*5184Sek110237 /*
326*5184Sek110237  * Waits till all threadflows are started, or a timeout occurs.
327*5184Sek110237  * Checks through the list of threadflows, waiting up to 10
328*5184Sek110237  * seconds for each one to set its tf_running flag to 1. If not
329*5184Sek110237  * set after 10 seconds, continues on to the next threadflow
330*5184Sek110237  * anyway.
331*5184Sek110237  */
332*5184Sek110237 void
333*5184Sek110237 threadflow_allstarted(pid_t pid, threadflow_t *threadflow)
334*5184Sek110237 {
335*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
336*5184Sek110237 
337*5184Sek110237 	while (threadflow) {
338*5184Sek110237 		int waits;
339*5184Sek110237 
340*5184Sek110237 		if ((threadflow->tf_instance == 0) ||
341*5184Sek110237 		    (threadflow->tf_instance == FLOW_MASTER)) {
342*5184Sek110237 			threadflow = threadflow->tf_next;
343*5184Sek110237 			continue;
344*5184Sek110237 		}
345*5184Sek110237 
346*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Checking pid %d thread %s-%d",
347*5184Sek110237 		    pid,
348*5184Sek110237 		    threadflow->tf_name,
349*5184Sek110237 		    threadflow->tf_instance);
350*5184Sek110237 
351*5184Sek110237 		waits = 10;
352*5184Sek110237 		while (waits && threadflow->tf_running == 0) {
353*5184Sek110237 			(void) ipc_mutex_unlock(
354*5184Sek110237 			    &filebench_shm->threadflow_lock);
355*5184Sek110237 			if (waits < 3)
356*5184Sek110237 				filebench_log(LOG_INFO,
357*5184Sek110237 				    "Waiting for pid %d thread %s-%d",
358*5184Sek110237 				    pid,
359*5184Sek110237 				    threadflow->tf_name,
360*5184Sek110237 				    threadflow->tf_instance);
361*5184Sek110237 
362*5184Sek110237 			(void) sleep(1);
363*5184Sek110237 			(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
364*5184Sek110237 			waits--;
365*5184Sek110237 		}
366*5184Sek110237 
367*5184Sek110237 		threadflow = threadflow->tf_next;
368*5184Sek110237 	}
369*5184Sek110237 
370*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->threadflow_lock);
371*5184Sek110237 }
372*5184Sek110237 
373*5184Sek110237 /*
374*5184Sek110237  * Create an in-memory thread object linked to a parent procflow.
375*5184Sek110237  * A threadflow entity is allocated from shared memory and
376*5184Sek110237  * initialized from the "inherit" threadflow if supplied,
377*5184Sek110237  * otherwise to zeros. The threadflow is assigned a unique
378*5184Sek110237  * thread id, the supplied instance number, the supplied name
379*5184Sek110237  * and added to the procflow's pf_thread list. If no name is
380*5184Sek110237  * supplied or the threadflow can't be allocated, NULL is
381*5184Sek110237  * returned Otherwise a pointer to the newly allocated threadflow
382*5184Sek110237  * is returned.
383*5184Sek110237  *
384*5184Sek110237  * The filebench_shm->threadflow_lock must be held by the caller.
385*5184Sek110237  */
386*5184Sek110237 static threadflow_t *
387*5184Sek110237 threadflow_define_common(procflow_t *procflow, char *name,
388*5184Sek110237     threadflow_t *inherit, int instance)
389*5184Sek110237 {
390*5184Sek110237 	threadflow_t *threadflow;
391*5184Sek110237 	threadflow_t **threadlistp = &procflow->pf_threads;
392*5184Sek110237 
393*5184Sek110237 	if (name == NULL)
394*5184Sek110237 		return (NULL);
395*5184Sek110237 
396*5184Sek110237 	threadflow = (threadflow_t *)ipc_malloc(FILEBENCH_THREADFLOW);
397*5184Sek110237 
398*5184Sek110237 	if (threadflow == NULL)
399*5184Sek110237 		return (NULL);
400*5184Sek110237 
401*5184Sek110237 	if (inherit)
402*5184Sek110237 		(void) memcpy(threadflow, inherit, sizeof (threadflow_t));
403*5184Sek110237 	else
404*5184Sek110237 		(void) memset(threadflow, 0, sizeof (threadflow_t));
405*5184Sek110237 
406*5184Sek110237 	threadflow->tf_utid = ++filebench_shm->utid;
407*5184Sek110237 
408*5184Sek110237 	threadflow->tf_instance = instance;
409*5184Sek110237 	(void) strcpy(threadflow->tf_name, name);
410*5184Sek110237 	threadflow->tf_process = procflow;
411*5184Sek110237 
412*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Defining thread %s-%d",
413*5184Sek110237 	    name, instance);
414*5184Sek110237 
415*5184Sek110237 	/* Add threadflow to list */
416*5184Sek110237 	if (*threadlistp == NULL) {
417*5184Sek110237 		*threadlistp = threadflow;
418*5184Sek110237 		threadflow->tf_next = NULL;
419*5184Sek110237 	} else {
420*5184Sek110237 		threadflow->tf_next = *threadlistp;
421*5184Sek110237 		*threadlistp = threadflow;
422*5184Sek110237 	}
423*5184Sek110237 
424*5184Sek110237 	return (threadflow);
425*5184Sek110237 }
426*5184Sek110237 
427*5184Sek110237 /*
428*5184Sek110237  * Create an in memory FLOW_MASTER thread object as described
429*5184Sek110237  * by the syntax. Acquire the  filebench_shm->threadflow_lock and
430*5184Sek110237  * call threadflow_define_common() to create a threadflow entity.
431*5184Sek110237  * Set the number of instances to create at runtime,
432*5184Sek110237  * tf_instances, to "instances". Return the threadflow pointer
433*5184Sek110237  * returned by the threadflow_define_common call.
434*5184Sek110237  */
435*5184Sek110237 threadflow_t *
436*5184Sek110237 threadflow_define(procflow_t *procflow, char *name,
437*5184Sek110237     threadflow_t *inherit, var_integer_t instances)
438*5184Sek110237 {
439*5184Sek110237 	threadflow_t *threadflow;
440*5184Sek110237 
441*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
442*5184Sek110237 
443*5184Sek110237 	if ((threadflow = threadflow_define_common(procflow, name,
444*5184Sek110237 	    inherit, FLOW_MASTER)) == NULL)
445*5184Sek110237 		return (NULL);
446*5184Sek110237 
447*5184Sek110237 	threadflow->tf_instances = instances;
448*5184Sek110237 
449*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->threadflow_lock);
450*5184Sek110237 
451*5184Sek110237 	return (threadflow);
452*5184Sek110237 }
453*5184Sek110237 
454*5184Sek110237 
455*5184Sek110237 /*
456*5184Sek110237  * Searches the provided threadflow list for the named threadflow.
457*5184Sek110237  * A pointer to the threadflow is returned, or NULL if threadflow
458*5184Sek110237  * is not found.
459*5184Sek110237  */
460*5184Sek110237 threadflow_t *
461*5184Sek110237 threadflow_find(threadflow_t *threadlist, char *name)
462*5184Sek110237 {
463*5184Sek110237 	threadflow_t *threadflow = threadlist;
464*5184Sek110237 
465*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->threadflow_lock);
466*5184Sek110237 
467*5184Sek110237 	while (threadflow) {
468*5184Sek110237 		if (strcmp(name, threadflow->tf_name) == 0) {
469*5184Sek110237 
470*5184Sek110237 			(void) ipc_mutex_unlock(
471*5184Sek110237 			    &filebench_shm->threadflow_lock);
472*5184Sek110237 
473*5184Sek110237 			return (threadflow);
474*5184Sek110237 		}
475*5184Sek110237 		threadflow = threadflow->tf_next;
476*5184Sek110237 	}
477*5184Sek110237 
478*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->threadflow_lock);
479*5184Sek110237 
480*5184Sek110237 
481*5184Sek110237 	return (NULL);
482*5184Sek110237 }
483