/* * The final initialization and main execution loop for the * worker threads. Sets threadflow and flowop start times, * waits for all process to start, then creates the runtime * flowops from those defined by the F language workload * script. It does some more initialization, then enters a * loop to repeatedly execute the flowops on the flowop list * until an abort condition is detected, at which time it exits. * This is the starting routine for the new worker thread * created by threadflow_createthread(), and is not currently * called from anywhere else. */ void flowop_start(threadflow_t *threadflow) { flowop_t *flowop; size_t memsize; int ret = FILEBENCH_OK; set_thread_ioprio(threadflow); #ifdef HAVE_PROC_PID_LWP char procname[128]; long ctl[2] = {PCSET, PR_MSACCT}; int pfd; (void) snprintf(procname, sizeof (procname), "/proc/%d/lwp/%d/lwpctl", (int)my_pid, _lwp_self()); pfd = open(procname, O_WRONLY); (void) pwrite(pfd, &ctl, sizeof (ctl), 0); (void) close(pfd); #endif (void) ipc_mutex_lock(&controlstats_lock); if (!controlstats_zeroed) { (void) memset(&controlstats, 0, sizeof (controlstats)); controlstats_zeroed = 1; } (void) ipc_mutex_unlock(&controlstats_lock); flowop = threadflow->tf_thrd_fops; /* Hold the flowop find lock as reader to prevent lookups */ (void) pthread_rwlock_rdlock(&filebench_shm->shm_flowop_find_lock); /* Create the runtime flowops from those defined by the script */ (void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock); if (flowop_create_runtime_flowops(threadflow, &threadflow->tf_thrd_fops) != FILEBENCH_OK) { (void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock); filebench_shutdown(1); return; } (void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock); /* Release the find lock as reader to allow lookups */ (void) pthread_rwlock_unlock(&filebench_shm->shm_flowop_find_lock); /* Set to the start of the new flowop list */ flowop = threadflow->tf_thrd_fops; memsize = (size_t)threadflow->tf_constmemsize; /* If we are going to use ISM, allocate later */ if (threadflow->tf_attrs & THREADFLOW_USEISM) { threadflow->tf_mem = ipc_ismmalloc(memsize); } else { threadflow->tf_mem = malloc(memsize); } (void) memset(threadflow->tf_mem, 0, memsize); filebench_log(LOG_DEBUG_SCRIPT, "Thread allocated %d bytes", memsize); #ifdef HAVE_LWPS filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx (%d) started", threadflow, _lwp_self()); #endif /* * Now we set tf_running flag to indicate to the main process * that the worker thread is running. However, the thread is * still not executing the workload, as it is blocked by the * shm_run_lock. Main thread will release this lock when all * threads set their tf_running flag to 1. */ threadflow->tf_abort = 0; threadflow->tf_running = 1; /* * Block until all processes have started, acting like * a barrier. The original filebench process initially * holds the run_lock as a reader, preventing any of the * threads from obtaining the writer lock, and hence * passing this point. Once all processes and threads * have been created, the original process unlocks * run_lock, allowing each waiting thread to lock * and then immediately unlock it, then begin running. */ (void) pthread_rwlock_wrlock(&filebench_shm->shm_run_lock); (void) pthread_rwlock_unlock(&filebench_shm->shm_run_lock); /* Main filebench worker loop */ while (ret == FILEBENCH_OK) { int i, count; /* Abort if asked */ if (threadflow->tf_abort || filebench_shm->shm_f_abort) break; /* Be quiet while stats are gathered */ if (filebench_shm->shm_bequiet) { (void) sleep(1); continue; } /* Take it easy until everyone is ready to go */ if (!filebench_shm->shm_procs_running) { (void) sleep(1); continue; } if (flowop == NULL) { filebench_log(LOG_ERROR, "flowop_read null flowop"); return; } /* Execute the flowop for fo_iters times */ count = (int)avd_get_int(flowop->fo_iters); for (i = 0; i < count; i++) { filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop " "%s-%d", threadflow->tf_name, flowop->fo_name, flowop->fo_instance); ret = (*flowop->fo_func)(threadflow, flowop); /* * Return value FILEBENCH_ERROR means "flowop * failed, stop the filebench run" */ if (ret == FILEBENCH_ERROR) { filebench_log(LOG_ERROR, "%s-%d: flowop %s-%d failed", threadflow->tf_name, threadflow->tf_instance, flowop->fo_name, flowop->fo_instance); (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_abort = 1; filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR; (void) ipc_mutex_unlock(&threadflow->tf_lock); break; } /* * Return value of FILEBENCH_NORSC means "stop * the filebench run" if in "end on no work mode", * otherwise it indicates an error */ if (ret == FILEBENCH_NORSC) { (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_abort = FILEBENCH_DONE; if (filebench_shm->shm_rmode == FILEBENCH_MODE_Q1STDONE) { filebench_shm->shm_f_abort = FILEBENCH_ABORT_RSRC; } else if (filebench_shm->shm_rmode != FILEBENCH_MODE_QALLDONE) { filebench_log(LOG_ERROR1, "WARNING! Run stopped early:\n " " flowop %s-%d could " "not obtain a file. Please\n " " reduce runtime, " "increase fileset entries " "($nfiles), or switch modes.", flowop->fo_name, flowop->fo_instance); filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR; } (void) ipc_mutex_unlock(&threadflow->tf_lock); break; } /* * Return value of FILEBENCH_DONE means "stop * the filebench run without error" */ if (ret == FILEBENCH_DONE) { (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_abort = FILEBENCH_DONE; filebench_shm->shm_f_abort = FILEBENCH_ABORT_DONE; (void) ipc_mutex_unlock(&threadflow->tf_lock); break; } /* * If we get here and the return is something other * than FILEBENCH_OK, it means a spurious code * was returned, so treat as major error. This * probably indicates a bug in the flowop. */ if (ret != FILEBENCH_OK) { filebench_log(LOG_ERROR, "Flowop %s unexpected return value = %d\n", flowop->fo_name, ret); filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR; break; } } /* advance to next flowop */ flowop = flowop->fo_exec_next; /* but if at end of list, start over from the beginning */ if (flowop == NULL) { flowop = threadflow->tf_thrd_fops; threadflow->tf_stats.fs_count++; } } #ifdef HAVE_LWPS filebench_log(LOG_DEBUG_SCRIPT, "Thread %d exiting", _lwp_self()); #endif /* Tell flowops to destroy locally acquired state */ flowop_destruct_all_flows(threadflow); pthread_exit(&threadflow->tf_abort); }
/* * The final initialization and main execution loop for the * worker threads. Sets threadflow and flowop start times, * waits for all process to start, then creates the runtime * flowops from those defined by the F language workload * script. It does some more initialization, then enters a * loop to repeatedly execute the flowops on the flowop list * until an abort condition is detected, at which time it exits. * This is the starting routine for the new worker thread * created by threadflow_createthread(), and is not currently * called from anywhere else. */ void flowop_start(threadflow_t *threadflow) { flowop_t *flowop; size_t memsize; int ret = 0; pid = getpid(); #ifdef HAVE_PROCFS if (noproc == 0) { char procname[128]; long ctl[2] = {PCSET, PR_MSACCT}; int pfd; (void) snprintf(procname, sizeof (procname), "/proc/%d/lwp/%d/lwpctl", pid, _lwp_self()); pfd = open(procname, O_WRONLY); (void) pwrite(pfd, &ctl, sizeof (ctl), 0); (void) close(pfd); } #endif if (!controlstats_zeroed) { (void) memset(&controlstats, 0, sizeof (controlstats)); controlstats_zeroed = 1; } flowop = threadflow->tf_ops; threadflow->tf_stats.fs_stime = gethrtime(); flowop->fo_stats.fs_stime = gethrtime(); /* Hold the flowop find lock as reader to prevent lookups */ (void) pthread_rwlock_rdlock(&filebench_shm->flowop_find_lock); /* * Block until all processes have started, acting like * a barrier. The original filebench process initially * holds the run_lock as a reader, preventing any of the * threads from obtaining the writer lock, and hence * passing this point. Once all processes and threads * have been created, the original process unlocks * run_lock, allowing each waiting thread to lock * and then immediately unlock it, then begin running. */ (void) pthread_rwlock_wrlock(&filebench_shm->run_lock); (void) pthread_rwlock_unlock(&filebench_shm->run_lock); /* Create the runtime flowops from those defined by the script */ (void) ipc_mutex_lock(&filebench_shm->flowop_lock); while (flowop) { flowop_t *newflowop; if (flowop == threadflow->tf_ops) threadflow->tf_ops = NULL; newflowop = flowop_define_common(threadflow, flowop->fo_name, flowop, 1, 0); if (newflowop == NULL) return; if (flowop_initflow(newflowop) < 0) { filebench_log(LOG_ERROR, "Flowop init of %s failed", newflowop->fo_name); } flowop = flowop->fo_threadnext; } (void) ipc_mutex_unlock(&filebench_shm->flowop_lock); filebench_log(LOG_DEBUG_SCRIPT, "Thread started"); /* Release the find lock as reader to allow lookups */ (void) pthread_rwlock_unlock(&filebench_shm->flowop_find_lock); /* Set to the start of the new flowop list */ flowop = threadflow->tf_ops; threadflow->tf_abort = 0; threadflow->tf_running = 1; /* If we are going to use ISM, allocate later */ if (threadflow->tf_attrs & THREADFLOW_USEISM) { threadflow->tf_mem = ipc_ismmalloc((size_t)*threadflow->tf_memsize); } else { threadflow->tf_mem = malloc((size_t)*threadflow->tf_memsize); } if (threadflow->tf_mem == NULL) { filebench_log(LOG_ERROR, "Thread failed to allocate %zd bytes", threadflow->tf_memsize); return; } memsize = *threadflow->tf_memsize; filebench_log(LOG_DEBUG_SCRIPT, "Thread allocated %lld bytes at %llx", memsize, threadflow->tf_mem); (void) memset(threadflow->tf_mem, 0, memsize); filebench_log(LOG_DEBUG_SCRIPT, "Thread zeroed %lld bytes", memsize); #ifdef HAVE_LWPS filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx (%d) started", threadflow, _lwp_self()); #else filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx started", threadflow); #endif /* Main filebench worker loop */ /* CONSTCOND */ while (1) { int i; /* Abort if asked */ if (threadflow->tf_abort || filebench_shm->f_abort) { (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_running = 0; (void) ipc_mutex_unlock(&threadflow->tf_lock); filebench_log(LOG_DEBUG_SCRIPT, "%s: aborting thread %s on request of a flowop", threadflow->tf_name); break; } /* Be quiet while stats are gathered */ if (filebench_shm->bequiet) { (void) sleep(1); continue; } /* Take it easy until everyone is ready to go */ if (!filebench_shm->allrunning) (void) sleep(1); if (flowop->fo_stats.fs_stime == 0) flowop->fo_stats.fs_stime = gethrtime(); if (flowop == NULL) { filebench_log(LOG_ERROR, "flowop_read null flowop"); return; } if (threadflow->tf_memsize == 0) { filebench_log(LOG_ERROR, "Zero memory size for thread %s", threadflow->tf_name); return; } /* Execute the flowop for fo_iters times */ for (i = 0; i < *flowop->fo_iters; i++) { filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop " "%s-%d", threadflow->tf_name, flowop->fo_name, flowop->fo_instance); ret = (*flowop->fo_func)(threadflow, flowop); filebench_log(LOG_DEBUG_SCRIPT, "%s: finished flowop " "%s-%d", threadflow->tf_name, flowop->fo_name, flowop->fo_instance); /* Return value > 0 means "stop the filebench run" */ if (ret > 0) { filebench_log(LOG_VERBOSE, "%s: exiting flowop %s-%d", threadflow->tf_name, flowop->fo_name, flowop->fo_instance); (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_abort = 1; filebench_shm->f_abort = 1; threadflow->tf_running = 0; (void) ipc_mutex_unlock(&threadflow->tf_lock); break; } /* * Return value < 0 means "flowop failed, stop the * filebench run" */ if (ret < 0) { filebench_log(LOG_ERROR, "flowop %s failed", flowop->fo_name); (void) ipc_mutex_lock(&threadflow->tf_lock); threadflow->tf_abort = 1; filebench_shm->f_abort = 1; threadflow->tf_running = 0; (void) ipc_mutex_unlock(&threadflow->tf_lock); break; } } /* advance to next flowop */ flowop = flowop->fo_threadnext; /* but if at end of list, start over from the beginning */ if (flowop == NULL) { flowop = threadflow->tf_ops; threadflow->tf_stats.fs_count++; } } #ifdef HAVE_LWPS filebench_log(LOG_DEBUG_SCRIPT, "Thread %d exiting", _lwp_self()); #else filebench_log(LOG_DEBUG_SCRIPT, "Thread exiting"); #endif pthread_exit(&ret); }