15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
226084Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
246613Sek110237  *
256613Sek110237  * Portions Copyright 2008 Denis Cheng
265184Sek110237  */
275184Sek110237 
285184Sek110237 #include "config.h"
295184Sek110237 #include <pthread.h>
305184Sek110237 #ifdef HAVE_LWPS
315184Sek110237 #include <sys/lwp.h>
325184Sek110237 #endif
335184Sek110237 #include <signal.h>
346613Sek110237 
356613Sek110237 #include "filebench.h"
365184Sek110237 #include "threadflow.h"
375184Sek110237 #include "flowop.h"
385184Sek110237 #include "ipc.h"
395184Sek110237 
405184Sek110237 static threadflow_t *threadflow_define_common(procflow_t *procflow,
415184Sek110237     char *name, threadflow_t *inherit, int instance);
425184Sek110237 
435184Sek110237 /*
445184Sek110237  * Threadflows are filebench entities which manage operating system
455184Sek110237  * threads. Each worker threadflow spawns a separate filebench thread,
465184Sek110237  * with attributes inherited from a FLOW_MASTER threadflow created during
475184Sek110237  * f model language parsing. This section contains routines to define,
485184Sek110237  * create, control, and delete threadflows.
495184Sek110237  *
505184Sek110237  * Each thread defined in the f model creates a FLOW_MASTER
515184Sek110237  * threadflow which encapsulates the defined attributes and flowops of
525184Sek110237  * the f language thread, including the number of instances to create.
535184Sek110237  * At runtime, a worker threadflow instance with an associated filebench
545184Sek110237  * thread is created, which runs until told to quit or is specifically
555184Sek110237  * deleted.
565184Sek110237  */
575184Sek110237 
585184Sek110237 
595184Sek110237 /*
605184Sek110237  * Prints information about threadflow syntax.
615184Sek110237  */
625184Sek110237 void
635184Sek110237 threadflow_usage(void)
645184Sek110237 {
655184Sek110237 	(void) fprintf(stderr, "  thread  name=<name>[,instances=<count>]\n");
665184Sek110237 	(void) fprintf(stderr, "\n");
675184Sek110237 	(void) fprintf(stderr, "  {\n");
685184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
695184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
705184Sek110237 	(void) fprintf(stderr, "    flowop ...\n");
715184Sek110237 	(void) fprintf(stderr, "  }\n");
725184Sek110237 	(void) fprintf(stderr, "\n");
735184Sek110237 }
745184Sek110237 
755184Sek110237 /*
765184Sek110237  * Creates a thread for the supplied threadflow. If interprocess
775184Sek110237  * shared memory is desired, then increments the amount of shared
785184Sek110237  * memory needed by the amount specified in the threadflow's
795184Sek110237  * tf_memsize parameter. The thread starts in routine
805184Sek110237  * flowop_start() with a poineter to the threadflow supplied
815184Sek110237  * as the argument.
825184Sek110237  */
835184Sek110237 static int
845184Sek110237 threadflow_createthread(threadflow_t *threadflow)
855184Sek110237 {
866212Saw148015 	fbint_t memsize;
876212Saw148015 	memsize = avd_get_int(threadflow->tf_memsize);
886212Saw148015 	threadflow->tf_constmemsize = memsize;
896212Saw148015 
905184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Creating thread %s, memory = %ld",
916212Saw148015 	    threadflow->tf_name, memsize);
925184Sek110237 
935184Sek110237 	if (threadflow->tf_attrs & THREADFLOW_USEISM)
946212Saw148015 		filebench_shm->shm_required += memsize;
955184Sek110237 
965184Sek110237 	if (pthread_create(&threadflow->tf_tid, NULL,
975184Sek110237 	    (void *(*)(void*))flowop_start, threadflow) != 0) {
985184Sek110237 		filebench_log(LOG_ERROR, "thread create failed");
995184Sek110237 		filebench_shutdown(1);
1006084Saw148015 		return (FILEBENCH_ERROR);
1015184Sek110237 	}
1025184Sek110237 
1036084Saw148015 	return (FILEBENCH_OK);
1045184Sek110237 }
1055184Sek110237 
1065184Sek110237 /*
1075184Sek110237  * Terminates (exits) all the threads of the procflow (process).
1085184Sek110237  * The procflow is determined from a process private pointer
1095184Sek110237  * initialized by threadflow_init().
1105184Sek110237  */
1115184Sek110237 /* ARGSUSED */
1125184Sek110237 static void
1135184Sek110237 threadflow_cancel(int arg1)
1145184Sek110237 {
1156084Saw148015 	threadflow_t *threadflow;
1165184Sek110237 
1175184Sek110237 #ifdef HAVE_LWPS
1185184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Thread signal handler on tid %d",
1195184Sek110237 	    _lwp_self());
1205184Sek110237 #endif
1215184Sek110237 
1226084Saw148015 	threadflow = my_procflow->pf_threads;
1235184Sek110237 	my_procflow->pf_running = 0;
1245184Sek110237 
1255184Sek110237 	while (threadflow) {
1265184Sek110237 		if (threadflow->tf_tid) {
1276084Saw148015 			/* make sure thread has been cleaned up */
1286084Saw148015 			flowop_destruct_all_flows(threadflow);
1296084Saw148015 
1305184Sek110237 			(void) pthread_cancel(threadflow->tf_tid);
1315184Sek110237 			filebench_log(LOG_DEBUG_IMPL, "Thread %d cancelled...",
1325184Sek110237 			    threadflow->tf_tid);
1335184Sek110237 		}
1345184Sek110237 		threadflow = threadflow->tf_next;
1355184Sek110237 	}
1366084Saw148015 
1376084Saw148015 	exit(0);
1385184Sek110237 }
1395184Sek110237 
1405184Sek110237 /*
1415184Sek110237  * Creates threads for the threadflows associated with a procflow.
1425184Sek110237  * The routine iterates through the list of threadflows in the
1435184Sek110237  * supplied procflow's pf_threads list. For each threadflow on
1445184Sek110237  * the list, it defines tf_instances number of cloned
1455184Sek110237  * threadflows, and then calls threadflow_createthread() for
1465184Sek110237  * each to create and start the actual operating system thread.
1475184Sek110237  * Note that each of the newly defined threadflows will be linked
1485184Sek110237  * into the procflows threadflow list, but at the head of the
1495184Sek110237  * list, so they will not become part of the supplied set. After
1505184Sek110237  * all the threads have been created, threadflow_init enters
1515184Sek110237  * a join loop for all the threads in the newly defined
1525184Sek110237  * threadflows. Once all the created threads have exited,
1535184Sek110237  * threadflow_init will return 0. If errors are encountered, it
1545184Sek110237  * will return a non zero value.
1555184Sek110237  */
1565184Sek110237 int
1575184Sek110237 threadflow_init(procflow_t *procflow)
1585184Sek110237 {
1595184Sek110237 	threadflow_t *threadflow = procflow->pf_threads;
1605184Sek110237 	int ret = 0;
1615184Sek110237 
1626391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_threadflow_lock);
1635184Sek110237 
1645184Sek110237 	(void) signal(SIGUSR1, threadflow_cancel);
1656084Saw148015 
1665184Sek110237 	while (threadflow) {
1675184Sek110237 		threadflow_t *newthread;
1686212Saw148015 		int instances;
1695184Sek110237 		int i;
1705184Sek110237 
1716212Saw148015 		instances = avd_get_int(threadflow->tf_instances);
1725184Sek110237 		filebench_log(LOG_VERBOSE,
1736286Saw148015 		    "Starting %d %s threads",
1746212Saw148015 		    instances, threadflow->tf_name);
1755184Sek110237 
1766212Saw148015 		for (i = 1; i < instances; i++) {
1775184Sek110237 			/* Create threads */
1785184Sek110237 			newthread =
1795184Sek110237 			    threadflow_define_common(procflow,
1805184Sek110237 			    threadflow->tf_name, threadflow, i + 1);
1815184Sek110237 			if (newthread == NULL)
1825184Sek110237 				return (-1);
1836084Saw148015 			ret |= threadflow_createthread(newthread);
1845184Sek110237 		}
1855184Sek110237 
1865184Sek110237 		newthread = threadflow_define_common(procflow,
1875184Sek110237 		    threadflow->tf_name,
1885184Sek110237 		    threadflow, 1);
1895184Sek110237 
1905184Sek110237 		if (newthread == NULL)
1915184Sek110237 			return (-1);
1925184Sek110237 
1936084Saw148015 		/* Create each thread */
1946084Saw148015 		ret |= threadflow_createthread(newthread);
1955184Sek110237 
1965184Sek110237 		threadflow = threadflow->tf_next;
1975184Sek110237 	}
1985184Sek110237 
1995184Sek110237 	threadflow = procflow->pf_threads;
2005184Sek110237 
2016391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_threadflow_lock);
2025184Sek110237 
2035184Sek110237 	while (threadflow) {
2046333Sek110237 		/* wait for all threads to finish */
2056333Sek110237 		if (threadflow->tf_tid) {
2066333Sek110237 			void *status;
2075184Sek110237 
2086333Sek110237 			if (pthread_join(threadflow->tf_tid, &status) == 0)
2096333Sek110237 				ret += *(int *)status;
2106333Sek110237 		}
2115184Sek110237 		threadflow = threadflow->tf_next;
2125184Sek110237 	}
2135184Sek110237 
2145184Sek110237 	procflow->pf_running = 0;
2155184Sek110237 
2165184Sek110237 	return (ret);
2175184Sek110237 }
2185184Sek110237 
2195184Sek110237 /*
2205184Sek110237  * Tells the threadflow's thread to stop and optionally signals
2215184Sek110237  * its associated process to end the thread.
2225184Sek110237  */
2235184Sek110237 static void
2246084Saw148015 threadflow_kill(threadflow_t *threadflow, int wait_cnt)
2255184Sek110237 {
2265184Sek110237 	/* Tell thread to finish */
2275184Sek110237 	threadflow->tf_abort = 1;
2285184Sek110237 
2296084Saw148015 	/* wait a bit for threadflow to stop */
2306084Saw148015 	while (wait_cnt && threadflow->tf_running) {
2316084Saw148015 		(void) sleep(1);
2326084Saw148015 		wait_cnt--;
2336084Saw148015 	}
2346084Saw148015 
2355184Sek110237 #ifdef USE_PROCESS_MODEL
2365184Sek110237 #ifdef HAVE_SIGSEND
2375184Sek110237 	(void) sigsend(P_PID, threadflow->tf_process->pf_pid, SIGUSR1);
2385184Sek110237 #else
2395184Sek110237 	(void) kill(threadflow->tf_process->pf_pid, SIGUSR1);
2405184Sek110237 #endif
2415184Sek110237 #else /* USE_PROCESS_MODEL */
2425184Sek110237 	threadflow->tf_process->pf_running = 0;
2435184Sek110237 #endif /* USE_PROCESS_MODEL */
2445184Sek110237 }
2455184Sek110237 
2465184Sek110237 /*
2475184Sek110237  * Deletes the specified threadflow from the specified threadflow
2485184Sek110237  * list after first terminating the threadflow's thread, deleting
2495184Sek110237  * the threadflow's flowops, and finally freeing the threadflow
2505184Sek110237  * entity. It also subtracts the threadflow's shared memory
2515184Sek110237  * requirements from the total amount required, shm_required. If
2525184Sek110237  * the specified threadflow is found, returns 0, otherwise
2535184Sek110237  * returns -1.
2545184Sek110237  */
2555184Sek110237 static int
2566084Saw148015 threadflow_delete(threadflow_t **threadlist, threadflow_t *threadflow,
2576084Saw148015     int wait_cnt)
2585184Sek110237 {
2595184Sek110237 	threadflow_t *entry = *threadlist;
2605184Sek110237 
2615184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Deleting thread: (%s-%d)",
2625184Sek110237 	    threadflow->tf_name,
2635184Sek110237 	    threadflow->tf_instance);
2645184Sek110237 
2656212Saw148015 	if (threadflow->tf_attrs & THREADFLOW_USEISM)
2666212Saw148015 		filebench_shm->shm_required -= threadflow->tf_constmemsize;
2675184Sek110237 
2685184Sek110237 	if (threadflow == *threadlist) {
2695184Sek110237 		/* First on list */
2705184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Deleted thread: (%s-%d)",
2715184Sek110237 		    threadflow->tf_name,
2725184Sek110237 		    threadflow->tf_instance);
2735184Sek110237 
2746084Saw148015 		threadflow_kill(threadflow, wait_cnt);
2756550Saw148015 		flowop_delete_all(&threadflow->tf_thrd_fops);
2765184Sek110237 		*threadlist = threadflow->tf_next;
2776613Sek110237 		(void) pthread_mutex_destroy(&threadflow->tf_lock);
2785184Sek110237 		ipc_free(FILEBENCH_THREADFLOW, (char *)threadflow);
2795184Sek110237 		return (0);
2805184Sek110237 	}
2815184Sek110237 
2825184Sek110237 	while (entry->tf_next) {
2835184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
2845184Sek110237 		    "Delete thread: (%s-%d) == (%s-%d)",
2855184Sek110237 		    entry->tf_next->tf_name,
2865184Sek110237 		    entry->tf_next->tf_instance,
2875184Sek110237 		    threadflow->tf_name,
2885184Sek110237 		    threadflow->tf_instance);
2895184Sek110237 
2905184Sek110237 		if (threadflow == entry->tf_next) {
2915184Sek110237 			/* Delete */
2925184Sek110237 			filebench_log(LOG_DEBUG_IMPL,
2935184Sek110237 			    "Deleted thread: (%s-%d)",
2945184Sek110237 			    entry->tf_next->tf_name,
2955184Sek110237 			    entry->tf_next->tf_instance);
2966084Saw148015 			threadflow_kill(entry->tf_next, wait_cnt);
2976550Saw148015 			flowop_delete_all(&entry->tf_next->tf_thrd_fops);
2986613Sek110237 			(void) pthread_mutex_destroy(&threadflow->tf_lock);
2995184Sek110237 			ipc_free(FILEBENCH_THREADFLOW, (char *)threadflow);
3005184Sek110237 			entry->tf_next = entry->tf_next->tf_next;
3015184Sek110237 			return (0);
3025184Sek110237 		}
3035184Sek110237 		entry = entry->tf_next;
3045184Sek110237 	}
3055184Sek110237 
3065184Sek110237 	return (-1);
3075184Sek110237 }
3085184Sek110237 
3095184Sek110237 /*
3105184Sek110237  * Given a pointer to the thread list of a procflow, cycles
3115184Sek110237  * through all the threadflows on the list, deleting each one
3125184Sek110237  * except the FLOW_MASTER.
3135184Sek110237  */
3145184Sek110237 void
3156084Saw148015 threadflow_delete_all(threadflow_t **threadlist, int wait_cnt)
3165184Sek110237 {
3175184Sek110237 	threadflow_t *threadflow = *threadlist;
3185184Sek110237 
3196391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_threadflow_lock);
3205184Sek110237 
3215184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Deleting all threads");
3225184Sek110237 
3235184Sek110237 	while (threadflow) {
3245184Sek110237 		if (threadflow->tf_instance &&
3255184Sek110237 		    (threadflow->tf_instance == FLOW_MASTER)) {
3265184Sek110237 			threadflow = threadflow->tf_next;
3275184Sek110237 			continue;
3285184Sek110237 		}
3296084Saw148015 		(void) threadflow_delete(threadlist, threadflow, wait_cnt);
3305184Sek110237 		threadflow = threadflow->tf_next;
3316084Saw148015 		/* grow more impatient */
3326084Saw148015 		if (wait_cnt > 0)
3336084Saw148015 			wait_cnt--;
3345184Sek110237 	}
3355184Sek110237 
3366391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_threadflow_lock);
3375184Sek110237 }
3385184Sek110237 
3395184Sek110237 /*
3405184Sek110237  * Waits till all threadflows are started, or a timeout occurs.
3415184Sek110237  * Checks through the list of threadflows, waiting up to 10
3425184Sek110237  * seconds for each one to set its tf_running flag to 1. If not
3435184Sek110237  * set after 10 seconds, continues on to the next threadflow
3445184Sek110237  * anyway.
3455184Sek110237  */
3465184Sek110237 void
3475184Sek110237 threadflow_allstarted(pid_t pid, threadflow_t *threadflow)
3485184Sek110237 {
3496391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_threadflow_lock);
3505184Sek110237 
3515184Sek110237 	while (threadflow) {
3525184Sek110237 		int waits;
3535184Sek110237 
3545184Sek110237 		if ((threadflow->tf_instance == 0) ||
3555184Sek110237 		    (threadflow->tf_instance == FLOW_MASTER)) {
3565184Sek110237 			threadflow = threadflow->tf_next;
3575184Sek110237 			continue;
3585184Sek110237 		}
3595184Sek110237 
3605184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Checking pid %d thread %s-%d",
3615184Sek110237 		    pid,
3625184Sek110237 		    threadflow->tf_name,
3635184Sek110237 		    threadflow->tf_instance);
3645184Sek110237 
3655184Sek110237 		waits = 10;
3666391Saw148015 		while (waits && (threadflow->tf_running == 0) &&
3676391Saw148015 		    (filebench_shm->shm_f_abort == 0)) {
3685184Sek110237 			(void) ipc_mutex_unlock(
3696391Saw148015 			    &filebench_shm->shm_threadflow_lock);
3705184Sek110237 			if (waits < 3)
3715184Sek110237 				filebench_log(LOG_INFO,
3725184Sek110237 				    "Waiting for pid %d thread %s-%d",
3735184Sek110237 				    pid,
3745184Sek110237 				    threadflow->tf_name,
3755184Sek110237 				    threadflow->tf_instance);
3765184Sek110237 
3775184Sek110237 			(void) sleep(1);
3786391Saw148015 			(void) ipc_mutex_lock(
3796391Saw148015 			    &filebench_shm->shm_threadflow_lock);
3805184Sek110237 			waits--;
3815184Sek110237 		}
3825184Sek110237 
3835184Sek110237 		threadflow = threadflow->tf_next;
3845184Sek110237 	}
3855184Sek110237 
3866391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_threadflow_lock);
3875184Sek110237 }
3885184Sek110237 
3895184Sek110237 /*
3905184Sek110237  * Create an in-memory thread object linked to a parent procflow.
3915184Sek110237  * A threadflow entity is allocated from shared memory and
3925184Sek110237  * initialized from the "inherit" threadflow if supplied,
3935184Sek110237  * otherwise to zeros. The threadflow is assigned a unique
3945184Sek110237  * thread id, the supplied instance number, the supplied name
3955184Sek110237  * and added to the procflow's pf_thread list. If no name is
3965184Sek110237  * supplied or the threadflow can't be allocated, NULL is
3975184Sek110237  * returned Otherwise a pointer to the newly allocated threadflow
3985184Sek110237  * is returned.
3995184Sek110237  *
4006391Saw148015  * The filebench_shm->shm_threadflow_lock must be held by the caller.
4015184Sek110237  */
4025184Sek110237 static threadflow_t *
4035184Sek110237 threadflow_define_common(procflow_t *procflow, char *name,
4045184Sek110237     threadflow_t *inherit, int instance)
4055184Sek110237 {
4065184Sek110237 	threadflow_t *threadflow;
4075184Sek110237 	threadflow_t **threadlistp = &procflow->pf_threads;
4085184Sek110237 
4095184Sek110237 	if (name == NULL)
4105184Sek110237 		return (NULL);
4115184Sek110237 
4125184Sek110237 	threadflow = (threadflow_t *)ipc_malloc(FILEBENCH_THREADFLOW);
4135184Sek110237 
4145184Sek110237 	if (threadflow == NULL)
4155184Sek110237 		return (NULL);
4165184Sek110237 
4175184Sek110237 	if (inherit)
4185184Sek110237 		(void) memcpy(threadflow, inherit, sizeof (threadflow_t));
4195184Sek110237 	else
4205184Sek110237 		(void) memset(threadflow, 0, sizeof (threadflow_t));
4215184Sek110237 
4226391Saw148015 	threadflow->tf_utid = ++filebench_shm->shm_utid;
4235184Sek110237 
4245184Sek110237 	threadflow->tf_instance = instance;
4255184Sek110237 	(void) strcpy(threadflow->tf_name, name);
4265184Sek110237 	threadflow->tf_process = procflow;
427*7556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_init(&threadflow->tf_lock,
428*7556SAndrew.W.Wilson@sun.com 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
4295184Sek110237 
4305184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Defining thread %s-%d",
4315184Sek110237 	    name, instance);
4325184Sek110237 
4335184Sek110237 	/* Add threadflow to list */
4345184Sek110237 	if (*threadlistp == NULL) {
4355184Sek110237 		*threadlistp = threadflow;
4365184Sek110237 		threadflow->tf_next = NULL;
4375184Sek110237 	} else {
4385184Sek110237 		threadflow->tf_next = *threadlistp;
4395184Sek110237 		*threadlistp = threadflow;
4405184Sek110237 	}
4415184Sek110237 
4425184Sek110237 	return (threadflow);
4435184Sek110237 }
4445184Sek110237 
4455184Sek110237 /*
4465184Sek110237  * Create an in memory FLOW_MASTER thread object as described
4476391Saw148015  * by the syntax. Acquire the  filebench_shm->shm_threadflow_lock and
4485184Sek110237  * call threadflow_define_common() to create a threadflow entity.
4495184Sek110237  * Set the number of instances to create at runtime,
4505184Sek110237  * tf_instances, to "instances". Return the threadflow pointer
4515184Sek110237  * returned by the threadflow_define_common call.
4525184Sek110237  */
4535184Sek110237 threadflow_t *
4545184Sek110237 threadflow_define(procflow_t *procflow, char *name,
4556212Saw148015     threadflow_t *inherit, avd_t instances)
4565184Sek110237 {
4575184Sek110237 	threadflow_t *threadflow;
4585184Sek110237 
4596391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_threadflow_lock);
4605184Sek110237 
4615184Sek110237 	if ((threadflow = threadflow_define_common(procflow, name,
4625184Sek110237 	    inherit, FLOW_MASTER)) == NULL)
4635184Sek110237 		return (NULL);
4645184Sek110237 
4655184Sek110237 	threadflow->tf_instances = instances;
4665184Sek110237 
4676391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_threadflow_lock);
4685184Sek110237 
4695184Sek110237 	return (threadflow);
4705184Sek110237 }
4715184Sek110237 
4725184Sek110237 
4735184Sek110237 /*
4745184Sek110237  * Searches the provided threadflow list for the named threadflow.
4755184Sek110237  * A pointer to the threadflow is returned, or NULL if threadflow
4765184Sek110237  * is not found.
4775184Sek110237  */
4785184Sek110237 threadflow_t *
4795184Sek110237 threadflow_find(threadflow_t *threadlist, char *name)
4805184Sek110237 {
4815184Sek110237 	threadflow_t *threadflow = threadlist;
4825184Sek110237 
4836391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_threadflow_lock);
4845184Sek110237 
4855184Sek110237 	while (threadflow) {
4865184Sek110237 		if (strcmp(name, threadflow->tf_name) == 0) {
4875184Sek110237 
4885184Sek110237 			(void) ipc_mutex_unlock(
4896391Saw148015 			    &filebench_shm->shm_threadflow_lock);
4905184Sek110237 
4915184Sek110237 			return (threadflow);
4925184Sek110237 		}
4935184Sek110237 		threadflow = threadflow->tf_next;
4945184Sek110237 	}
4955184Sek110237 
4966391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_threadflow_lock);
4975184Sek110237 
4985184Sek110237 
4995184Sek110237 	return (NULL);
5005184Sek110237 }
501